What's the best way to show a series of (dynamic) animations sequentially?

I’m back with more obvious questions!

It feels like I’ve been skirting near the answer to this for days but just haven’t got it working …

I want to show a series of animations that all play sequentially (one after the other). Some of these animations are dynamically generated and so I don’t know the length of each animation - so I can’t use timer.performWithDelay

I’m using Sprite Grabber and I see that sprites can fire events, but I’m a bit confused about the sprite phase “end” vs. the general event phase of “ended”. Either way, I can’t seem to implement anything that works. I’ll paste what I’m trying to do below but really this seems overkill even if it does work. I don’t want to have to fire a function on each sprite clip completing.

THEN i saw there’s a spriteInstance of “animating” (http://developer.coronalabs.com/reference/index/spriteinstanceanimating). This would appear to do the trick … If I could do something like

[blockcode]
if (not spritesequence1.animating) then
spritesequence2:play()
end
if (not spritesequence2.animating) then
spritesequence3:play()
end

… and so on
[/blockcode]

But that doesn’t seem to work either. Do I need a listener here? It could be my messy code botching things up - all this is taking place inside another large function (which is why I’m keen on the most efficient approach to give me the best chance of untangling it all!)

Anyway, here’s my Listener solution, which is all shades of ugly and messed up:

[blockcode]
local animationend = false
local function continue()
if sprite.phase == “end” then
animationend = true
end
end

spritesequence1:addEventListener(“sprite”, continue)

– then later on I check

if animationend == true then
spritesequence2:play()
end

[/blockcode]

So to recap! I need a neat way of saying “when this sprite has reached the end of its animation, go ahead and play this next sprite”

Thanks in advance!
Andrew [import]uid: 132606 topic_id: 31314 reply_id: 331314[/import]

For this kind of thing I use a “state machine” where the state machine function is being called by timer functions, transition onComplete’s and by sprite event listeners, and is basically a series of if state==“something” elseif state==“somethingelse” statements.

Also - for each sprite you know how long it takes to animate (since you’re the one that specifies the length of animation) so you *could* do the timer or timed transition call at the end of it. [import]uid: 160496 topic_id: 31314 reply_id: 125148[/import]

Hi Mike, thanks for the quick reply. I mostly understand what you’re getting at and I’ve been *trying* to make something like that work. Would you mind awfully giving me some sample code for the “sprite event listener”? My problem seems to be that if I place the listener outside of the function playing these animations, then the state change doesn’t seem to be able to be read within that function … ?

Also, while I know how long each sprite is, I don’t know *which* sprite I’m calling in (hence the “dynamic” in brackets in my post), so since I don’t know which of the various random sprites is playing, I don’t know how long to wait before playing the next clip. Does that make sense?

Aside from which, it just feels more natural to be able to write something that can be applied so flexibly as “when x has finished, play y” - after all, what happens if I change the speed of an animation later and forget to update the delay timers buried lower down my file? [import]uid: 132606 topic_id: 31314 reply_id: 125153[/import]

Here’s a try :

local animation = -- create your sprite and setup sequences with all states and speeds  
animation.AnimationData = {  
 DefaultState = "idle0",  
 CurrentState = nil,  
 NextState = nil,  
 States = {  
 idle0 = { Speed = 800, NextState = "idle1" },  
 idle1 = { Speed = 800, NextState = "idle2" },  
 idle2 = { Speed = 800, NextState = "idlestep" },  
 idlestep = { Speed = 400, NextState = "idle0" },  
 walk = { Speed = 500, NextState = "walk" },  
 attack = { Speed = 800, NextState = "attack" },  
 dead = { Speed = 800, NextState = "dead" }  
 }  
}  
animation:addEventListener("sprite", function (event)  
 if (event.phase == "end") then  
 local sprite = event.sprite  
 local anim = sprite.AnimationData  
 if (anim) then  
 local next\_state = anim.NextState or anim.States[anim.CurrentState or anim.DefaultState].NextState  
 anim.CurrentState = next\_state  
 anim.NextState = nil  
 sprite:prepare(next\_state)  
 sprite:play()  
 end  
 end  
end)  

So the idea is just to have linked list of states that define valid sequences. Like the idle sequence can be few cycles of just sitting there and then having one step and begin again. Then in your game logic you’d set .NextState to something when you switch to a new state sequence

DISCLAIMER: that is based on working code, but heavily simplified so will not work in practise. Finding my bugs is left as an excercise of the reader. Also don’t code like that with never ending “or”-statements :slight_smile: [import]uid: 46570 topic_id: 31314 reply_id: 125158[/import]

Hi Matias,

Thank you so much for obliging with some sample code - that certainly helps visualise the “state machine” and it’s safe to say I would never have got to that point on my own. I’m still trying to pick my way through your code and fully understand what it’s doing … I get a bit lost around lines 20-25. I’ve deduced that in general the function is working out the next state and preparing to play it, but I’m not sure how … also, how does the if(anim) check that you’ve not already fully cycled through all states?

No need to answer right away. I’m going to try and implement this code and fiddle around with it until I understand what’s going on.

Oh but one question immediately came to mind. What happens if I want to mix animations from two separate sprite sheets in this state machine? It seems based on taking data from a single sheet, is that right?

Thanks so much for your time, it’s very helpful. [import]uid: 132606 topic_id: 31314 reply_id: 125160[/import]

For this kind of thing I use a “state machine” where the state machine function is being called by timer functions, transition onComplete’s and by sprite event listeners, and is basically a series of if state==“something” elseif state==“somethingelse” statements.

Also - for each sprite you know how long it takes to animate (since you’re the one that specifies the length of animation) so you *could* do the timer or timed transition call at the end of it. [import]uid: 160496 topic_id: 31314 reply_id: 125148[/import]

Hi Mike, thanks for the quick reply. I mostly understand what you’re getting at and I’ve been *trying* to make something like that work. Would you mind awfully giving me some sample code for the “sprite event listener”? My problem seems to be that if I place the listener outside of the function playing these animations, then the state change doesn’t seem to be able to be read within that function … ?

Also, while I know how long each sprite is, I don’t know *which* sprite I’m calling in (hence the “dynamic” in brackets in my post), so since I don’t know which of the various random sprites is playing, I don’t know how long to wait before playing the next clip. Does that make sense?

Aside from which, it just feels more natural to be able to write something that can be applied so flexibly as “when x has finished, play y” - after all, what happens if I change the speed of an animation later and forget to update the delay timers buried lower down my file? [import]uid: 132606 topic_id: 31314 reply_id: 125153[/import]

Here’s a try :

local animation = -- create your sprite and setup sequences with all states and speeds  
animation.AnimationData = {  
 DefaultState = "idle0",  
 CurrentState = nil,  
 NextState = nil,  
 States = {  
 idle0 = { Speed = 800, NextState = "idle1" },  
 idle1 = { Speed = 800, NextState = "idle2" },  
 idle2 = { Speed = 800, NextState = "idlestep" },  
 idlestep = { Speed = 400, NextState = "idle0" },  
 walk = { Speed = 500, NextState = "walk" },  
 attack = { Speed = 800, NextState = "attack" },  
 dead = { Speed = 800, NextState = "dead" }  
 }  
}  
animation:addEventListener("sprite", function (event)  
 if (event.phase == "end") then  
 local sprite = event.sprite  
 local anim = sprite.AnimationData  
 if (anim) then  
 local next\_state = anim.NextState or anim.States[anim.CurrentState or anim.DefaultState].NextState  
 anim.CurrentState = next\_state  
 anim.NextState = nil  
 sprite:prepare(next\_state)  
 sprite:play()  
 end  
 end  
end)  

So the idea is just to have linked list of states that define valid sequences. Like the idle sequence can be few cycles of just sitting there and then having one step and begin again. Then in your game logic you’d set .NextState to something when you switch to a new state sequence

DISCLAIMER: that is based on working code, but heavily simplified so will not work in practise. Finding my bugs is left as an excercise of the reader. Also don’t code like that with never ending “or”-statements :slight_smile: [import]uid: 46570 topic_id: 31314 reply_id: 125158[/import]

Hi Matias,

Thank you so much for obliging with some sample code - that certainly helps visualise the “state machine” and it’s safe to say I would never have got to that point on my own. I’m still trying to pick my way through your code and fully understand what it’s doing … I get a bit lost around lines 20-25. I’ve deduced that in general the function is working out the next state and preparing to play it, but I’m not sure how … also, how does the if(anim) check that you’ve not already fully cycled through all states?

No need to answer right away. I’m going to try and implement this code and fiddle around with it until I understand what’s going on.

Oh but one question immediately came to mind. What happens if I want to mix animations from two separate sprite sheets in this state machine? It seems based on taking data from a single sheet, is that right?

Thanks so much for your time, it’s very helpful. [import]uid: 132606 topic_id: 31314 reply_id: 125160[/import]