Problem with sprite animations and timing (bug ? desync ?)

Hey there !

I’ve got an issue with managing sprites and “timing performances”. I’m using the latest daily build (which should be the 2015.8.27)

I’ve got a sprite, on which I’m applying a sprite listener. Once the animation is finished, it’s supposed to call a function. But “sometimes”, it seems to cause some errors. Here is a simple exemple (download the exemple here or see the attachement file).

local mySprite local sheetData\_item = {width=80, height=80, numFrames=10, sheetContentWidth=400, sheetContentHeight=160} local mySheet\_item = graphics.newImageSheet("spritesheet\_paint.png", sheetData\_item) local sequenceData\_item ={{name = "normalRun", start=1, count=10, time=1000, loopCount=1}} -- Creating and placing the sprite mySprite = display.newSprite(mySheet\_item, sequenceData\_item) mySprite.x = display.contentWidth\*0.5 mySprite.y = display.contentHeight\*0.5 -- Callback function local function callbackFunction() print("callbackFunction()") end -- Callback function local function callbackFunction2() print("callbackFunction2()") end -- Dealing with the animation local function spriteListener(event) if event.phase == "ended" then -- Calling the callback function print("ended") callbackFunction() end end mySprite:addEventListener("sprite", spriteListener) mySprite:play(); -- Timer timer.performWithDelay(1001, callbackFunction2)

This should run fine : once launched, this script should play the animation, and in the console, it should (will) print this :

callbackFunction() callbackFunction2()

The problem is when the devices is slow (because lot of stuff might be running in the app, or because it’s simply too old), the animation doesn’t match the timing. The animation seems to skip frames, which means the spriteListener is triggered too early compared to the timer.

And on the simulator, there is actually one way to test this : launch the previous script and pause immediatly the simulator (Ctrl + Shift + Down). Wait for like 2 seconds and unpause the simulator and you’ll see that the animation will be instantly finished. While the timer will end correctly a little bit later.

On device, this becomes a huge problem when a lot of things are running (physics, enterFrame, or other stuff running in the background).

In the end, it seems that sprite listener aren’t that reliable. I really need to know if an animation has ended in order to call other functions and it breaks a lot of things. Is there an other way ? Am I doing something wrong ? Is this a bug ?

Hi @evanspro,

Well, when the app is “overloaded,” there’s just an inherent possibility that frame rates will not match up exactly on time or with perfect consistency. This is just how it is, unfortunately. You can take steps to implement “delta time” as outlined in this tutorial, but otherwise Corona runs as best as it can on the frame rate you specify, but if the hardware lags, so will the frame rates (slightly).

As for the sprite listeners, there’s no logical reason why it should be unreliable. When the sprite finishes animating, it will trigger the function… it’s not like Corona is running some separate internal timer on the notion of “when the sprite is supposed to be finished”. When it reaches the last frame, the listener will fire.

Best regards,

Brent

Hey brent ! 

Thanks for the answer ! Unfortunately, there’s something I still don’t understand : you’re saying that “the frame rate will not match up exactly on time or with perfect consistency”. Which seems normal to me, but what I would expect in the case the app is “overloaded” is that the framerate would also slow down, just like timers or even physics.

On device, timers do slow down a little when the app is too busy, but sprites only skips frames in order to “keep the rhythm”, which creates some inconsistencies. When I pause my exemple on the simulator and then resume, I can see the timer is actually paused and resumes as it should be, but the sprite seems to reach the “ending phase” almost immediately when resumed, by simply skipping frames.

The sprite listeners are not “unreliable” as I said : they do detect the ending phase but, since there is no way to know if all the frames have been played, the ending is not reliable in terms of “timing”. (not sure if I’m clear :slight_smile: ).

That said, I still haven’t tested the “delta time” method, but in the case of a sprite, I’m not sure if it will work : since I’m not trying to detect if the sprite has reached a particular frame, and since the ending phase is always detected (just too early). … Or does that means I should change the “timescale” dynamically depending on the delta time (which could be pretty heavy in my case…) ?

Hi @evanspro,

I think (but I can’t remember for sure) that the “ended” phase will be triggered when the final frame is shown, but that means it will be triggered at the very moment it shows, not at the “total end of the animation”.

I’m still not sure what you’re attempting to do by running both a timer AND an “ended” frame detect on the sprite. Why are these two separate processes in your design?

Brent

Hey Brent,

The reason I’m asking this is because in my game, every time a level starts, there’s a camera travelling showing where you have to go, some text message appearing on screen and several sprites animations.

Once this “sequence” is over, the player has control of the game. For now, I’m giving he control to the player once one sprite animation has ended. Unfortunately, if the device is overloaded, the sprite animation reaches the ended phase BEFORE everything else (while it’s supposed to be longer than anything else since his sprite properties says it should last 3000ms). I really thought that the ended phase would reach the phase after everything else, every time. But because the way Corona deals with sprite animation, it doesn’t work.

So, in my case, it’s kinda game breaking… I think a quick solution would be to add several flags for every timer / transitions / animation, and once all thoses flags have been set to true, I can finally give the player control.

Hi @evanspro,

Can you pass control to the player after some other thing happens? What about after the “camera” reaches its location?

Hey Brent !

Well, because of the way my game is built, it’s kind of complicated : my “greeting message” (the text appearing at the beginning) is made of several sprites while the camera movement and speed depends on the size of the level, which means its duration changes every time. And there’s also the player animation… But as said earlier, in some _rare _occasions, this wasn’t reliable.

So right now : I can’t rely on ONE timer to determine if the player is allowed to control the game. In fact, I really think I should have done this from the beginning : everytime a timer / sprite animation / camera transition ends, a variable will be incremented. And only when this variable will be equal to let’s say 5, then the player is allowed to control the game.

It should work that way :slight_smile:

Thanks a lot for your time !

PS : Can’t really mark this topic as solved since there’s still this “frame skipping” issue when a device is overloaded… :wink:

Hi @evanspro,

Well, when the app is “overloaded,” there’s just an inherent possibility that frame rates will not match up exactly on time or with perfect consistency. This is just how it is, unfortunately. You can take steps to implement “delta time” as outlined in this tutorial, but otherwise Corona runs as best as it can on the frame rate you specify, but if the hardware lags, so will the frame rates (slightly).

As for the sprite listeners, there’s no logical reason why it should be unreliable. When the sprite finishes animating, it will trigger the function… it’s not like Corona is running some separate internal timer on the notion of “when the sprite is supposed to be finished”. When it reaches the last frame, the listener will fire.

Best regards,

Brent

Hey brent ! 

Thanks for the answer ! Unfortunately, there’s something I still don’t understand : you’re saying that “the frame rate will not match up exactly on time or with perfect consistency”. Which seems normal to me, but what I would expect in the case the app is “overloaded” is that the framerate would also slow down, just like timers or even physics.

On device, timers do slow down a little when the app is too busy, but sprites only skips frames in order to “keep the rhythm”, which creates some inconsistencies. When I pause my exemple on the simulator and then resume, I can see the timer is actually paused and resumes as it should be, but the sprite seems to reach the “ending phase” almost immediately when resumed, by simply skipping frames.

The sprite listeners are not “unreliable” as I said : they do detect the ending phase but, since there is no way to know if all the frames have been played, the ending is not reliable in terms of “timing”. (not sure if I’m clear :slight_smile: ).

That said, I still haven’t tested the “delta time” method, but in the case of a sprite, I’m not sure if it will work : since I’m not trying to detect if the sprite has reached a particular frame, and since the ending phase is always detected (just too early). … Or does that means I should change the “timescale” dynamically depending on the delta time (which could be pretty heavy in my case…) ?

Hi @evanspro,

I think (but I can’t remember for sure) that the “ended” phase will be triggered when the final frame is shown, but that means it will be triggered at the very moment it shows, not at the “total end of the animation”.

I’m still not sure what you’re attempting to do by running both a timer AND an “ended” frame detect on the sprite. Why are these two separate processes in your design?

Brent

Hey Brent,

The reason I’m asking this is because in my game, every time a level starts, there’s a camera travelling showing where you have to go, some text message appearing on screen and several sprites animations.

Once this “sequence” is over, the player has control of the game. For now, I’m giving he control to the player once one sprite animation has ended. Unfortunately, if the device is overloaded, the sprite animation reaches the ended phase BEFORE everything else (while it’s supposed to be longer than anything else since his sprite properties says it should last 3000ms). I really thought that the ended phase would reach the phase after everything else, every time. But because the way Corona deals with sprite animation, it doesn’t work.

So, in my case, it’s kinda game breaking… I think a quick solution would be to add several flags for every timer / transitions / animation, and once all thoses flags have been set to true, I can finally give the player control.

Hi @evanspro,

Can you pass control to the player after some other thing happens? What about after the “camera” reaches its location?

Hey Brent !

Well, because of the way my game is built, it’s kind of complicated : my “greeting message” (the text appearing at the beginning) is made of several sprites while the camera movement and speed depends on the size of the level, which means its duration changes every time. And there’s also the player animation… But as said earlier, in some _rare _occasions, this wasn’t reliable.

So right now : I can’t rely on ONE timer to determine if the player is allowed to control the game. In fact, I really think I should have done this from the beginning : everytime a timer / sprite animation / camera transition ends, a variable will be incremented. And only when this variable will be equal to let’s say 5, then the player is allowed to control the game.

It should work that way :slight_smile:

Thanks a lot for your time !

PS : Can’t really mark this topic as solved since there’s still this “frame skipping” issue when a device is overloaded… :wink: