I haven’t documented the new sound API but in Codea 4 when you play a sound: sound.play(asset)
or sound.playBackground(asset)
you get a sound instance object. You can then get/set the .time
property to seek or check the current playing position of the sound. I used that to sync the gameplay instead of using time.delta
or time.elapsed
There’s no long a music function, you can have multiple music tracks…
First I load the music tracks using the new sound.read()
API, which works the same way for all assets
-- sound assets provided by a student
musicBase = sound.read(asset.documents.MusicForJohn_JP.JP_TestTrack_60BPM)
musicLayer1 = sound.read(asset.documents.MusicForJohn_JP.JP_TestTrack_Layer1)
musicLayer2 = sound.read(asset.documents.MusicForJohn_JP.JP_TestTrack_Layer2)
When the game begins I set up the tracks to play in a loop but make the extra layers mute (volume 0) so they can be turned on and off when the main music track loops
-- somewhere in game init() play multiple background tracks
self.music = sound.playBackground(musicBase)
self.music.loop = true
self.musicLayer1 = sound.playBackground(musicLayer1)
self.musicLayer1.loop = true
self.musicLayer1.volume = 0
self.musicLayer2 = sound.playBackground(musicLayer2)
self.musicLayer2.loop = true
self.musicLayer2.volume = 0
Each frame I check if the music has looped (comparing the previous music time with last frame) and turn some of the extra tracks on or off
-- somewhere in update(), check for loops in the music and randomly turn on/off extra music track layers
local t = self.baseTime + self.music.time
if self.prevTime > t then
self.baseTime = self.baseTime + musicBase.length
t = self.baseTime + self.music.time
self.loops = self.loops + 1
if self.loops > 1 then
self.musicLayer1.volume = math.random(0,1)
end
if self.loops > 2 then
self.musicLayer2.volume = math.random(0,1)
end
end
The gameplay relies on some variables I calculate to do the procedural animations. It’s all based on the BPM of the song, which is 60 or one beat per second roughly
-- calculate some useful variables for the bpm including some normalised repeating signals
local beatTimePrev = self.prevTime % self.beatRate
local beatTime = t % self.beatRate
self.beatTime = beatTime
self.beatTimeNorm = t / self.beatRate
self.beatPercent = beatTime / self.beatRate
self.prevTime = t
This also ties into the logic of the game itself, with a bit of an added leniency window (so you can press a button a bit after the beat happens)
-- update game logic to the beat of the music!
self.tickThisFrame = beatTimePrev < 0.1 and beatTime >= 0.1
if self.tickThisFrame then
self:tick()
end