Ran into another problem in my Codea learning adventure…
I am trying to spawn clouds at a certain frequency using a timer and delta time, however something strange is happening. The first 2 clouds spawn at the correct interval, but after that the clouds start spawning closer and closer together until they are spawning once every frame… not sure what I’m doing wrong. Any advice would be appreciated.
Here is what the code looks like:
-- Gravity test
supportedOrientations(LANDSCAPE_ANY)
displayMode(FULLSCREEN)
function setup()
clds={} -- clouds table
grnd={} -- ground table
obst={} -- obstacles table
rectMode(CENTER) -- rectangle coordinates from center of rectangle
heliX = WIDTH/6 -- helicopter x starting position
heliY = HEIGHT/2 -- helicopter y starting position
timer=0
cloudFrequency=3.5 -- cloud spawn frequency
heliGrav = 0 -- starting vertical velocity
heliAccel = -.15 -- New variable for acceleration speed
sky() -- spawn first cloud at startup
end
-- cloud spawning function
function sky()
table.insert(clds,vec2(WIDTH+WIDTH/2,700))
end
-- ground spawning function
function ground()
end
-- obstacle spawning function
function obstacles()
end
function draw()
-- draw background
sprite("SpaceCute:Background",WIDTH/2,HEIGHT/2,WIDTH,HEIGHT)
-- spawn clouds
for a,b in pairs(clds) do
sprite("Space Art:Cloudy Nebula",b.x,b.y,1024,128)
b.x=b.x-6
end
-- destroy clouds when off screen
for a,b in pairs(clds) do
if b.x<-500 then
table.remove(clds, a)
end
-- cloud spawn timer
timer=timer+DeltaTime
if timer>cloudFrequency then
sky()
timer=0
end
end
-- spawn spaceship
sprite("SpaceCute:Rocketship",heliX, heliY, 128, 64)
heliGrav = heliGrav + heliAccel -- Apply acceleration
heliY = heliY + heliGrav
end
function touched(t)
-- When you touch and when you let go, reverse the acceleration
if t.state == BEGAN or t.state == ENDED then
heliAccel = heliAccel * -1
end
-- So, when you touch it switches to going up, but when you let go it will start falling again
end
What’s happening is you Delta Time is getting larger as the clouds get added. This means there is more time per frame. As a consequence, there is less movement in a given amount of time since the cloud movement occurs once per frame and the fps is slowing down. However, the clouds are still spawning when the timer hits your cloud frequency. This causes the clouds to appear on time but they their movement over time is decreasing. This causes clouds to spawn on top of each other since the other clouds aren’t moving as far. I hope that makes sense. If not do a print statement on delta time and watch.
@Crumble - assuming the diagnosis above is correct, one thing you can do is to make the cloud movement dependent on time as well, so even if the clouds draw more slowly, they will still be the correct distance apart.
So instead of
b.x=b.x-6
use this (6 pixels every 1/60 of a second = 3600 pixels per second)
So the reason it is happening is because the frame rate is slowing down? I tried it without using delta time and using a regular timer instead and the same thing occurred, so I don’t think it is tied to frame rate slowdown.
The way I tried with a timer was:
cloudFrequency=180
Then
Timer=timer+1
Which should have spawned one every 3 seconds
That didn’t seem to work Ingatz, it spawned one cloud that moved very fast, then no other clouds.
I disagree, I think you need to remove timers and focus on counters so a changing delta time won’t hurt you. Using this method it works great! Note: wait a few seconds for clouds to start spawning.
Try this:
-- Gravity test
supportedOrientations(LANDSCAPE_ANY)
displayMode(FULLSCREEN)
function setup()
clds={}
grnd={}
obst={}
rectMode(CENTER)
heliX = WIDTH/6
heliY = HEIGHT/2
timer=0
--Now this is a counter variable instead of time
cloudFrequency=200
heliGrav = 0
heliAccel = -.15 -- New variable for acceleration speed
--New Counter
counter=0
end
function sky()
table.insert(clds,vec2(WIDTH+WIDTH/2,700))
end
function ground()
end
function obstacles()
end
function draw()
--Increase counter
counter = counter + 1
--rint(counter)
sprite("SpaceCute:Background",WIDTH/2,HEIGHT/2,WIDTH,HEIGHT)
for a,b in pairs(clds) do
sprite("Space Art:Cloudy Nebula",b.x,b.y,1024,128)
b.x=b.x-6
end
for a,b in pairs(clds) do
if b.x<-500 then
table.remove(clds, a)
end
end
--If your counter is a multiple of your cloud frequency then...
if counter%cloudFrequency==0 then
sky()
print("draw")
end
sprite("SpaceCute:Rocketship",heliX, heliY, 128, 64)
heliGrav = heliGrav + heliAccel -- Apply acceleration
heliY = heliY + heliGrav
end
function touched(t)
-- When you touch and when you let go, reverse the acceleration
if t.state == BEGAN or t.state == ENDED then
heliAccel = heliAccel * -1
end
-- So, when you touch it switches to going up, but when you let go it will start falling again
end
% is a modulus. This returns the remainder of the first number divided by the second number. A modulus returns zero if the first number divided by the second number is an integer.
Actually modulus is the remainder of one number divided by another number. A modulus returns 0 when one number is evenly divisible by another number, ie. 8%4, 21%7, 99%3 will all have a modulus of 0. Other examples of modulus are 23%7 will return 2, 12%9 will return 3.
So with a 200 cloudFrequency, and the count increasing by 1 every frame(60 fps), it will spawn a new cloud every 3.3333… seconds, or at 200 draws, 400, 600, etc. Cool trick.
When Codea is keeping track of draws, does it start to slow down if the game goes on for say 15 minutes and the draw count gets really high?
You guys are the best, I would be seriously lost without the help of you guys on the forums that are willing to take the time to help. I greatly appreciate it!
@Crumble There are different ways of doing a timer. You can use a counter that increments in the draw function, but the more objects you draw, the more draw slows down. You could use os.time to check the difference between a time start and the current time, that’s not dependent on draw. Or you could use Tweens. I have a tween example below.
displayMode(FULLSCREEN)
supportedOrientations(LANDSCAPE_ANY)
function setup()
clds={} -- table of clouds
clouds=cloud() -- instance of cloud
clouds:start(0) -- spawn a cloud at startup
end
function draw()
background(40, 40, 50)
-- draw cloud and move it
for a,b in pairs(clds) do
sprite("Space Art:Cloudy Nebula",b.x,b.y,1024,128)
b.x=b.x-6
end
-- destroy clouds when off screen
for a,b in pairs(clds) do
if b.x<-500 then
table.remove(clds, a)
end
end
end
cloud=class() -- cloud timer class
function cloud:start(dur) -- start a tween with length of dur
tween(dur,{0,0},{0,0},tween.loop.once,function() cloud:spawn() end)
end
function cloud:spawn()
table.insert(clds,vec2(WIDTH+WIDTH/2,700)) -- put cloud in table
clouds:start(4) -- spawn the next cloud in 4 seconds
end
@dave1707 No need for an empty tween, you can just use tween.delay (which is designed for exactly what you need)
displayMode(FULLSCREEN)
supportedOrientations(LANDSCAPE_ANY)
function setup()
clds={} -- table of clouds
clouds=cloud() -- instance of cloud
clouds:start(0) -- spawn a cloud at startup
end
function draw()
background(40, 40, 50)
-- draw cloud and move it
for a,b in pairs(clds) do
sprite("Space Art:Cloudy Nebula",b.x,b.y,1024,128)
b.x=b.x-6
end
-- destroy clouds when off screen
for a,b in pairs(clds) do
if b.x<-500 then
table.remove(clds, a)
end
end
end
cloud=class() -- cloud timer class
function cloud:start(dur) -- start a tween with length of dur
tween.delay(dur,function() cloud:spawn() end)
end
function cloud:spawn()
table.insert(clds,vec2(WIDTH+WIDTH/2,700)) -- put cloud in table
clouds:start(4) -- spawn the next cloud in 4 seconds
end