Another noob question (timers and spawning actors)

Hello all!

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

Thanks in advance!

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)

b.x=b.x-3600*DeltaTime

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

That works perfect!

Thanks much for the help!

I’m confused on what the below is actually doing:

If counter%cloudFrequency==0 then

What is the % calculation doing? And how does it equal zero?

It’s cool that it works, but I don’t understand how it works.

% 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.

modulus is like this: a % b is the same as b * ((a / b) - math.floor(a / b))

so 1%2 returns 1, 30%25 returns 5, etc.

This is useful for looping, because as as a increases, it loops back when it reaches b.

For example: b = 10 and a is increasing (a = a + 1), a % b will return: 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1, 2, 3, 4, etc.

So saying if a % b == 0 will happen every time a has gone b times, so in my above example it would be true every 10 steps.

Hope this helps clear it up for you

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.

Interesting, that makes sense!

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!

You shouldn’t have a problem, if you are worried simply reset counter every time a cloud is spawned so the number will never be high.

@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

@SkyTheCoder Thanks. That just goes to show how often I use Tweens.

You guys are awesome!

I’m going to have to read up on Tweens, no idea what they are.

@Crumble- try this

http://coolcodea.wordpress.com/2013/07/18/995-tweens-what-are-they/

Woah Tweens are powerful stuff. Looks like I will be using them a lot for moving objects and looping.

Thanks for that