Tween sequence issue

Hello

Can anyone tell me what I’m doing wrong here? Each number is meant to grow, then disappear to make way for the next growing number. But only the first number grows because the first tween, t1, seems to tween the size for all of my numbers after the first second. Yet the subject of each tween is clearly different. What silly mistake am I making?

Matt




function setup()
    
    CountDown = {{text = "3", size = 0, opacity = 255}, {text = "2", size = 0, opacity = 255},{text = "1", size = 0, opacity = 255},{text = "GO!", size = 0, opacity = 255}}
    
    t1 = tween(1, CountDown[1], {size = 100}, tween.easing.linear, function()
    sound("Game Sounds One:Bell 2") CountDown[1].opacity = 0 end)
    
    t2 = tween(1, CountDown[2], {size = 100}, tween.easing.linear, function() sound("Game Sounds One:Bell 2") CountDown[2].opacity = 0 end)
    
    t3 = tween(1, CountDown[3], {size = 100}, tween.easing.linear, function() sound("Game Sounds One:Bell 2") CountDown[3].opacity = 0  end)
    
    t4 = tween(1, CountDown[4], {size = 100}, tween.easing.linear,
    function() sound("Game Sounds One:Bell 2")
    CountDown[4].opacity = 0 end)
    
    tween.sequence(t1, t2, t3, t4)
    
end


function draw()

    background(40, 40, 50)
    
    for i = 1, 4 do
        pushStyle()
        textMode(CENTER)
        font("Futura-CondensedExtraBold")
        fill(255, CountDown[i].opacity)
        fontSize(CountDown[i].size)
        text(CountDown[i].text, WIDTH/2, HEIGHT/2)
        popStyle()
    end
    
end

@epicurur101 Not sure if this is the correct way to do this, but it works.


function setup()
    i=1
    CountDown = { {text = "3", size = 0, opacity = 255},
            {text = "2", size = 0, opacity = 255},
            {text = "1", size = 0, opacity = 255},
            {text = "GO!", size = 0, opacity = 255} }
    t1 = tween(1, CountDown[1], {size = 100}, tween.easing.linear, 
        function() sound("Game Sounds One:Bell 2")
            CountDown[1].opacity = 0 i=2 CountDown[1].size=0 end)
    t2 = tween(1, CountDown[2], {size = 100}, tween.easing.linear, 
        function() sound("Game Sounds One:Bell 2")
            CountDown[2].opacity = 0 i=3 CountDown[2].size=0 end)
    t3 = tween(1, CountDown[3], {size = 100}, tween.easing.linear, 
        function() sound("Game Sounds One:Bell 2")
            CountDown[3].opacity = 0  i=4 CountDown[3].size=0 end)
    t4 = tween(1, CountDown[4], {size = 100}, tween.easing.linear,
        function() sound("Game Sounds One:Bell 2")
            CountDown[4].opacity = 0 i=1 end)
    tween.sequence(t1, t2, t3, t4)
end

function draw()
    background(40, 40, 50)
    pushStyle()
    textMode(CENTER)
    font("Futura-CondensedExtraBold")
    fill(255, CountDown[i].opacity)
    fontSize(CountDown[i].size)
    text(CountDown[i].text, WIDTH/2, HEIGHT/2)
    popStyle()
end

I wonder whether tween sequence doesn’t like having callback functions mid-sequence?

This is a bit simpler but it works

function setup()

    CountDown = {text = "3", size = 0, opacity = 255}

    t1 = tween(1, CountDown, {size = 100}, tween.easing.linear, function()
    sound("Game Sounds One:Bell 2") CountDown.size = 0 CountDown.text = "2" end)

    t2 = tween(1, CountDown, {size = 100}, tween.easing.linear, 
    function() sound("Game Sounds One:Bell 2") CountDown.size = 0 CountDown.text = "1" end)

    t3 = tween(1, CountDown, {size = 100}, tween.easing.linear, 
    function() sound("Game Sounds One:Bell 2") CountDown.size = 0 CountDown.text = "0" end)

    t4 = tween(1, CountDown, {size = 100}, tween.easing.linear,
    function() sound("Game Sounds One:Bell 2") CountDown.opacity = 0 end)

    tween.sequence(t1, t2, t3, t4)

end


function draw()

    background(40, 40, 50)

    pushStyle()
    textMode(CENTER)
    font("Futura-CondensedExtraBold")
    fill(255, CountDown.opacity)
    fontSize(CountDown.size)
    text(CountDown.text, WIDTH/2, HEIGHT/2)
    popStyle()

end

I think the problem is that the tween is executed when you call it. So the sequence is just reusing the same tween, but the subject has already converged.I mean, in the first second you play simultaneously t1, t2, t3, t4 and t1 again, so it looks like only t1 is playing, but 5 tweens are actually running. Then you play again t2, then 3, then t4 .
In my code, the subject is always the same and is reset to original size by each callback. Then it works to use the same tween again. Just guessing here.

I wouldn’t use a tween for this, as ordinary code is simpler and clearer

function draw()
    background(0)
    CountDown={"3","2","1","Go!"}
    if ElapsedTime<4 then 
        local a,b=math.modf(ElapsedTime)
        pushStyle()
        textMode(CENTER)
        font("Futura-CondensedExtraBold")
        fill(255, 255*(1-b)) --fades
        fontSize(100*b)  --grows
        text(CountDown[a+1], WIDTH/2, HEIGHT/2)
        popStyle()
        --make sound each time the number of seconds changes
        if not lasta or lasta~=a then sound("Game Sounds One:Bell 2") end
        lasta=a
    end
end

(However, the tween discussion is interesting!)

Just some examples that shows there are several ways to do the same thing. It just depends on how you want to do it.

I agree, but simple is usually better (and would avoid the problems above). After all, tweens are just for loops with fancy effects, and if you aren’t using fancy effects, you might as well use a for loop.

If you did want to use Tweens though…



function setup()

    CountDown = {{text = "3", size = 0, opacity = 255}, {text = "2", size = 0, opacity = 255},{text = "1", size = 0, opacity = 255},{text = "GO!", size = 0, opacity = 255}}
    i=0
    Countdown()
  --  tween.sequence(t1, t2, t3, t4)

end

function Countdown()
    sound("Game Sounds One:Bell 2")
    i = i + 1
    if i<4 then
        tween(1, CountDown[i], {size = 100}, tween.easing.linear, Countdown)
    else
        tween(1, CountDown[i], {size = 100}, tween.easing.linear)
    end  
end

function draw()

    background(40, 40, 50)

   -- for i = 1, 4 do
        pushStyle()
        textMode(CENTER)
        font("Futura-CondensedExtraBold")
        fill(255, CountDown[i].opacity)
        fontSize(CountDown[i].size)
        text(CountDown[i].text, WIDTH/2, HEIGHT/2)
        popStyle()
 --   end

end

I think the issue was that tween.sequence isn’t fond of callbacks mid-sequence

@yojimbo2000 I like the way your tween code works. Nice and simple.

@yojimbo2000 After looking at your code, it can be cut down some more by eliminating a tween call.


function setup()
    textMode(CENTER)
    CountDown = 
        {   {text = "3", size = 0, opacity = 255}, 
            {text = "2", size = 0, opacity = 255},
            {text = "1", size = 0, opacity = 255},
            {text = "GO!", size = 0, opacity = 255}
        }
    i=0
    Countdown()
end

function Countdown()
    --sound("Game Sounds One:Bell 2")
    if i<4 then
        i=i+1
        tween(1, CountDown[i], {size = 100}, tween.easing.linear, Countdown)
    end  
end

function draw()
    background(40, 40, 50)
    font("Futura-CondensedExtraBold")
    fill(255, CountDown[i].opacity)
    fontSize(CountDown[i].size)
    text(CountDown[i].text, WIDTH/2, HEIGHT/2)
end

Thanks for the help, and the alternative ways of doing things. It’s nothing to do with the callback because if you take them out it still doesn’t work. I really don’t understand tween sequences if they all run at once. That’s not a sequence!

Also I’d simplified my code, because in the original I had the “spent” number expanding and fading rapidly to reveal the next number. Alas none of these solutions allow me to do that easily. Is it a nested table issue? Will investigate.

I’m sure I’ll be able to find a solution but if anyone can shed light on the limitations of tween sequence so I know when it should and shouldn’t be in my kitbag that would be great!

My code above fades the number as it grows larger.

How about using tween.reset to restore and reuse the same values each time:

function setup()
    CountDown={text={"3","2","1","GO!"}, size=0, opacity=255}
    i=0
    CountDownNext()
end

function CountDownNext() 
    if i<4 then
        tween.reset(CountDown.tween)
        i = i + 1
        sound("Game Sounds One:Bell 2")
        CountDown.tween=tween(1, CountDown, {size = 100, opacity=0}, tween.easing.linear, CountDownNext)
    end  
end

function draw()
    background(40, 40, 50)
    pushStyle()
    textMode(CENTER)
    font("Futura-CondensedExtraBold")
    fill(255, CountDown.opacity)
    fontSize(CountDown.size)
    text(CountDown.text[i], WIDTH/2, HEIGHT/2)
    popStyle()
end

@yojimbo2000 yes, was just on a run and it all clicked into place! By defining the Tweens you also call them. so tween sequence was doing what it was supposed to, but after they had all been set running. Should have read @Jmv38’s response more closely! Sorry all.

@epicurus101 no, a tween.sequence call should prevent all the tweens contained within it from running at once, otherwise it wouldn’t be much use. Tbh I have no idea why your code doesn’t work. What’s really needed here is a “loop n times” function.

what is not needed in this case is tweens. They are just causing problems!

tween.sequence seems to work as expected only when the subject of sequenced tweens is the same

@Jmv38 yes, I think you must be right.

Here’s a tween.loopN class (edited to stop recursive nesting of callback)

--# Main
function setup()
    CountDown={text={"3","2","1","GO!"}, size=0, opacity=255}
    tw=tween.loopN(1, CountDown, {size = 100, opacity=0}, 4, tween.easing.linear, function() sound("Game Sounds One:Bell 2") end) --loop 4 times
end

function draw()
    background(40, 40, 50)
    pushStyle()
    textMode(CENTER)
    font("Futura-CondensedExtraBold")
    fill(255, CountDown.opacity)
    fontSize(CountDown.size)
    text(CountDown.text[tw.i], WIDTH/2, HEIGHT/2)
    popStyle()
end

--# TweenLoopN
tween.loopN = class() --a class for looping a set number of times

function tween.loopN:init(duration, object, target, times, easing, callback)
    self.i=1
    self.tween=tween(duration, object, target, easing, function() self:next(duration, object, target, times, easing, callback) end)
end

function tween.loopN:next(duration, object, target, times, easing, callback)
    if callback then callback() end
    if self.i<times then
        tween.reset(self.tween)
        self.i = self.i + 1
        self.tween=tween(duration, object, target, easing, function() self:next(duration, object, target, times, easing, callback) end)
    end
end

@Ignatz you’re right in this instance, but if something doesn’t work predictably or intuitively then shouldn’t it be fixed, or at least the documentation updated with warnings? Tweens, when they work, are massively useful and easy to tweak. The ability to easily change the tween easing for example is really incredible. If tween sequence only works predictably when the subject is the same for each tween then that would be good for everyone to know.

@epicurus101 - This code has different subjects and works fine for me

function setup()
    a={b=0}
    c={d=0}
    t1=tween( 3, a, { b=200 }, tween.easing.quadInOut )
    t2=tween( 3, c, { d=200 }, tween.easing.quadInOut )
    tween.sequence(t1,t2)
    print("Red ball should rise, then yellow ball")
end

function draw()
    background(100)
    fill(255,0,0)
    ellipse(400,200+a.b,100)
    fill(255,255,0)
    ellipse(500,200+c.d,100)
end