Time based animation

I’m playing with the horse animation found in the sample code. I added two buttons to stop and start the background animations. The buttons respectively remove and add an event listener. They both work, however, the start button does not take into account elapsed time, which results in a jump to where the animation was before pressing the stop button.

I found this snippet of code at: http://bit.ly/fpwqja (under Lost or Missing Time), which resolves this issue for application suspends and resume…

local tSuspend  
local function onSuspendResume( event )  
 if "applicationSuspend" == event.type then  
 tSuspend = system.getTimer()  
 elseif "applicationResume" == event.type then  
 -- add missing time to tPrevious  
 tPrevious = tPrevious + ( system.getTimer() - tSuspend )  
 end  
end  
   
Runtime:addEventListener( "system", onSuspendResume );  

How can this code be modified to work with an event handler? Here’s the code that I added to the existing horse animation code…

local ui = require("ui")  
  
local buttonHandler\_stop = function( event )  
Runtime:removeEventListener("enterFrame", move)  
print( "Stop" )  
end  
  
local buttonStop = ui.newButton{  
 default = "button.png",  
 over = "buttonOver.png",  
 onEvent = buttonHandler\_stop,  
 text = " Stop",  
 size = 12,  
 emboss=true  
}  
local buttonHandler\_start = function( event )  
Runtime:addEventListener("enterFrame", move)  
print( "Start" )  
end  
  
local buttonStart = ui.newButton{  
 default = "button.png",  
 over = "buttonOver.png",  
 onEvent = buttonHandler\_start,  
 text = " Start",  
 size = 12,  
 emboss=true  
}  
  
buttonStop.x = 100; buttonStop.y = 300  
buttonStart.x = 175; buttonStart.y = 300  

Any help would be greatly appreciated! [import]uid: 12397 topic_id: 6748 reply_id: 306748[/import]

I was able to apply the “lost time” solution by adding this code to the start button…

local buttonHandler\_start = function( event )  
 if event.phase == "release" then  
 tPrevious = tPrevious + ( system.getTimer() - tPause )  
 Runtime:addEventListener("enterFrame", move)  
 print( "Start" )  
 end  
end  

Then this code to the stop button…

local buttonHandler\_stop = function( event )  
 if event.phase == "release" then  
 tPause = system.getTimer()  
 Runtime:removeEventListener("enterFrame", move)  
 print( "Stop" )  
 end  
end  

Then I added a new variable and assigned an initial value (because I changed the animation’s initial state to paused)…

local tPause = system.getTimer()  

Next challenge is to change the scrolling directions… any advice on where to start?

[import]uid: 12397 topic_id: 6748 reply_id: 23570[/import]

Solved!
I duplicated the original function move(event), changed some expressions and coordinates, and renamed. I’m totally new to Corona/Lua and programming in general, so I haven’t a clue if these code alterations fall within best practices, though they do appear to work in the simulator.

Original code…

local xOffset = ( 0.2 \* tDelta )  

New code…

local xOffset = -( 0.2 \* tDelta )
then change each element’s expression and respective coordinates (18 elements in the horse animation sample).

Original code…

 if moon.x \> 480 + moon.width / 2 then  
 moon:translate ( -480\*2 , 0)  
 end  

New code…

 if moon.x \< -480 + moon.width / 2 then  
 moon:translate ( 480\*2 , 0)  
 end  

Here’s the new button…

local buttonLeft = ui.newButton{  
 default = "ArrowLeft.png",  
 over = "ArrowLeftOver.png",  
 onEvent = buttonHandler\_left,  
 x = 20,  
 y = 170,  
 text = " L",  
 size = 12,  
 emboss=true  
}  

Here’s the new event handler to call the altered and renamed function (moveLeft)…

local buttonHandler\_left = function( event )  
 if event.phase == "release" and activeButton ~= "left" then  
 activeButton = "left"  
 movePause()  
 moveStart()  
 spriteFlip()  
 statusActive = true  
 end  
end  

Here’s the code to start the animation

local function moveStart()  
 tPrevious = tPrevious + ( system.getTimer() - tPause )  
 if activeButton == "left" then   
 Runtime:addEventListener("enterFrame", moveLeft)  
 elseif activeButton == "right" then   
 Runtime:addEventListener("enterFrame", moveRight)  
 end  
end  

Here’s the code to pause the animation

local function movePause()  
 if statusActive == true then  
 tPause = system.getTimer()  
 if activeButton == "left" then  
 Runtime:removeEventListener("enterFrame", moveRight)  
 elseif activeButton == "right" then  
 Runtime:removeEventListener("enterFrame", moveLeft)  
 end  
 end  
end  

Question: Does anyone have any tips on how to tidy up the code any further?

Question: Can anyone help me translate the following snippet into plain english? I can copy and paste all day, but I’d really like to grasp the underlying logic.

 if moon.x \< -480 + moon.width / 2 then  
 moon:translate ( 480\*2 , 0)  
 end  

In my feeble mind, I thinks it’s saying… if the moon’s horizontal position (left edge) is less than -480 + 37.5, move moon to the new position of 960, i.e. if the moon drops off-screen to the left then reposition off-screen to the right… is this accurate? Why does the code divide the width of the moon then add to the position off-screen?

Here’s the entire contents of the altered main.lua in case anyone’s interested…

require "sprite"  
  
-- Hide status bar  
display.setStatusBar(display.HiddenStatusBar)  
  
-- Static image  
local background = display.newImage("background.png")   
background:setReferencePoint(display.TopLeftReferencePoint)  
  
-- initial display of images to be scrolled  
local moon = display.newImage("moon.png", 22, 19)   
  
local mountain\_big = display.newImage("mountain\_big.png", 132-240, 92)   
local mountain\_big2 = display.newImage("mountain\_big.png", 132-720, 92)   
local mountain\_sma = display.newImage("mountain\_small.png", 84, 111)  
local mountain\_sma2 = display.newImage("mountain\_small.png", 84 - 480, 111)  
  
local tree\_s = display.newImage("tree\_s.png", 129-30, 151)   
local tree\_s2 = display.newImage("tree\_s.png", 270 + 10,151)  
local tree\_l = display.newImage("tree\_l.png", 145, 131)   
  
local tree\_s3 = display.newImage("tree\_s.png", 129-30 - 320, 151)   
local tree\_s4 = display.newImage("tree\_s.png", 270 + 10 - 320,151)  
local tree\_l2 = display.newImage("tree\_l.png", 145 - 320, 131)   
  
local tree\_s5 = display.newImage("tree\_s.png", 129 - 30 - 640, 151)   
local tree\_s6 = display.newImage("tree\_s.png", 270 + 10 - 640,151)  
local tree\_l3 = display.newImage("tree\_l.png", 145 - 640, 131)   
  
local fog = display.newImage("Fog.png", 0, 214)   
local fog2 = display.newImage("Fog.png",-480,214)  
  
-- Background coordinates  
background.x = 0  
background.y = 0  
  
-- A sprite sheet with a horse  
local uma = sprite.newSpriteSheetFromData( "uma.png", require("uma").getSpriteSheetData() )  
  
local spriteSet = sprite.newSpriteSet(uma,1,8)  
  
sprite.add(spriteSet,"uma",1,8,1000,0)  
  
local spriteInstance = sprite.newSprite(spriteSet)  
  
spriteInstance:setReferencePoint(display.BottomRightReferencePoint)  
spriteInstance.x = 480  
spriteInstance.y = 320  
  
local tree\_l\_sugi = display.newImage("tree\_l\_sugi.png", 23, 0)   
local tree\_l\_take = display.newImage("tree\_l\_take.png", 151, 0)   
  
local wallOutSide = display.newRect(480,0,200,320)  
wallOutSide:setFillColor(0,0,0)  
  
local wallOutSide2 = display.newRect(-200,0,200,320)  
wallOutSide2:setFillColor(0,0,0)  
  
-- New variable to store time for lost time resetting  
local tPrevious = system.getTimer()  
  
-- Time-based animation that scrolls elements to the right  
local function moveLeft(event)  
  
 local tDelta = event.time - tPrevious  
 tPrevious = event.time  
  
-- Speed and direction of scroll (positive scrolls right)  
 local xOffset = ( 0.2 \* tDelta )  
  
-- Initial image positioning  
 moon.x = moon.x + xOffset\*0.05  
  
 fog.x = fog.x + xOffset  
 fog2.x = fog2.x + xOffset  
  
 mountain\_big.x = mountain\_big.x + xOffset\*0.5  
 mountain\_big2.x = mountain\_big2.x + xOffset\*0.5  
 mountain\_sma.x = mountain\_sma.x + xOffset\*0.5  
 mountain\_sma2.x = mountain\_sma2.x + xOffset\*0.5  
  
 tree\_s.x = tree\_s.x + xOffset  
 tree\_s2.x = tree\_s2.x + xOffset  
 tree\_l.x = tree\_l.x + xOffset  
  
 tree\_s3.x = tree\_s3.x + xOffset  
 tree\_s4.x = tree\_s4.x + xOffset  
 tree\_l2.x = tree\_l2.x + xOffset  
  
 tree\_s5.x = tree\_s5.x + xOffset  
 tree\_s6.x = tree\_s6.x + xOffset  
 tree\_l3.x = tree\_l3.x + xOffset  
  
 tree\_l\_sugi.x = tree\_l\_sugi.x + xOffset \* 1.5  
 tree\_l\_take.x = tree\_l\_take.x + xOffset \* 1.5  
  
-- Reposition images once they fall off-screen   
 if moon.x \> 480 + moon.width / 2 then  
 moon:translate ( -480\*2 , 0)  
 end  
 if fog.x \> 480 + fog.width / 2 then  
 fog:translate( -480 \* 2, 0)  
 end  
  
 if fog2.x \> 480 + fog2.width / 2 then  
 fog2:translate( -480 \* 2, 0)  
 end  
  
 if mountain\_big.x \> 480 + mountain\_big.width / 2 then  
 mountain\_big:translate(-480\*2 , 0)  
 end  
 if mountain\_big2.x \> 480 + mountain\_big2.width / 2 then  
 mountain\_big2:translate(-480\*2 , 0)  
 end  
 if mountain\_sma.x \> 480 + mountain\_sma.width / 2 then  
 mountain\_sma:translate(-480\*2,0)  
 end  
 if mountain\_sma2.x \> 480 + mountain\_sma2.width / 2 then  
 mountain\_sma2:translate(-480\*2,0)  
 end  
  
 if tree\_s.x \> 480 + tree\_s.width / 2 then  
 tree\_s:translate(-480\*2 , 0)  
 end  
 if tree\_s2.x \> 480 + tree\_s2.width / 2 then  
 tree\_s2:translate(-480\*2 , 0)  
 end  
 if tree\_l.x \> 480 + tree\_l.width / 2 then  
 tree\_l:translate(-480\*2 , 0)  
 end  
  
 if tree\_s3.x \> 480 + tree\_s3.width / 2 then  
 tree\_s3:translate(-480\*2 , 0)  
 end  
 if tree\_s4.x \> 480 + tree\_s4.width / 2 then  
 tree\_s4:translate(-480\*2 , 0)  
 end  
 if tree\_l2.x \> 480 + tree\_l2.width / 2 then  
 tree\_l2:translate(-480\*2 , 0)  
 end  
  
 if tree\_s5.x \> 480 + tree\_s5.width / 2 then  
 tree\_s5:translate(-480\*2 , 0)  
 end  
 if tree\_s6.x \> 480 + tree\_s6.width / 2 then  
 tree\_s6:translate(-480\*2 , 0)  
 end  
 if tree\_l3.x \> 480 + tree\_l3.width / 2 then  
 tree\_l3:translate(-480\*2 , 0)  
 end  
  
 if tree\_l\_sugi.x \> 480 + tree\_l\_sugi.width / 2 then  
 tree\_l\_sugi:translate(-480\*4,0)  
 end  
  
 if tree\_l\_take.x \> 480 + tree\_l\_take.width / 2 then  
 tree\_l\_take:translate(-480\*5,0)  
 end  
end  
  
-- Time-based animation that scrolls elements to the left  
local function moveRight(event)  
  
 local tDelta = event.time - tPrevious  
 tPrevious = event.time  
  
-- Speed and direction of scroll (negative scrolls left)  
 local xOffset = -( 0.2 \* tDelta )  
  
-- Initial image positioning  
 moon.x = moon.x + xOffset\*0.05  
  
 fog.x = fog.x + xOffset  
 fog2.x = fog2.x + xOffset  
  
 mountain\_big.x = mountain\_big.x + xOffset\*0.5  
 mountain\_big2.x = mountain\_big2.x + xOffset\*0.5  
 mountain\_sma.x = mountain\_sma.x + xOffset\*0.5  
 mountain\_sma2.x = mountain\_sma2.x + xOffset\*0.5  
  
 tree\_s.x = tree\_s.x + xOffset  
 tree\_s2.x = tree\_s2.x + xOffset  
 tree\_l.x = tree\_l.x + xOffset  
  
 tree\_s3.x = tree\_s3.x + xOffset  
 tree\_s4.x = tree\_s4.x + xOffset  
 tree\_l2.x = tree\_l2.x + xOffset  
  
 tree\_s5.x = tree\_s5.x + xOffset  
 tree\_s6.x = tree\_s6.x + xOffset  
 tree\_l3.x = tree\_l3.x + xOffset  
  
 tree\_l\_sugi.x = tree\_l\_sugi.x + xOffset \* 1.5  
 tree\_l\_take.x = tree\_l\_take.x + xOffset \* 1.5  
  
-- Reposition images once they fall off-screen   
 if moon.x \< -480 + moon.width / 2 then  
 moon:translate ( 480\*2 , 0)  
 end  
 if fog.x \< -480 + fog.width / 2 then  
 fog:translate( 480 \* 2, 0)  
 end  
  
 if fog2.x \< -480 + fog2.width / 2 then  
 fog2:translate( 480 \* 2, 0)  
 end  
  
 if mountain\_big.x \< -480 + mountain\_big.width / 2 then  
 mountain\_big:translate(480\*2 , 0)  
 end  
 if mountain\_big2.x \< -480 + mountain\_big2.width / 2 then  
 mountain\_big2:translate(480\*2 , 0)  
 end  
 if mountain\_sma.x \< -480 + mountain\_sma.width / 2 then  
 mountain\_sma:translate(480\*2,0)  
 end  
 if mountain\_sma2.x \< -480 + mountain\_sma2.width / 2 then  
 mountain\_sma2:translate(480\*2,0)  
 end  
  
 if tree\_s.x \< -480 + tree\_s.width / 2 then  
 tree\_s:translate(480\*2 , 0)  
 end  
 if tree\_s2.x \< -480 + tree\_s2.width / 2 then  
 tree\_s2:translate(480\*2 , 0)  
 end  
 if tree\_l.x \< -480 + tree\_l.width / 2 then  
 tree\_l:translate(480\*2 , 0)  
 end  
  
 if tree\_s3.x \< -480 + tree\_s3.width / 2 then  
 tree\_s3:translate(480\*2 , 0)  
 end  
 if tree\_s4.x \< -480 + tree\_s4.width / 2 then  
 tree\_s4:translate(480\*2 , 0)  
 end  
 if tree\_l2.x \< -480 + tree\_l2.width / 2 then  
 tree\_l2:translate(480\*2 , 0)  
 end  
  
 if tree\_s5.x \< -480 + tree\_s5.width / 2 then  
 tree\_s5:translate(480\*2 , 0)  
 end  
 if tree\_s6.x \< -480 + tree\_s6.width / 2 then  
 tree\_s6:translate(480\*2 , 0)  
 end  
 if tree\_l3.x \< -480 + tree\_l3.width / 2 then  
 tree\_l3:translate(480\*2 , 0)  
 end  
  
 if tree\_l\_sugi.x \< -480 + tree\_l\_sugi.width / 2 then  
 tree\_l\_sugi:translate(480\*4,0)  
 end  
  
 if tree\_l\_take.x \< -480 + tree\_l\_take.width / 2 then  
 tree\_l\_take:translate(480\*5,0)  
 end  
end  
  
spriteInstance:prepare("uma")  
spriteInstance:play()  
  
-- Recapture spent time when app is suspended then resumed  
local tSuspend  
local function onSuspendResume( event )  
 if "applicationSuspend" == event.type then  
 tSuspend = system.getTimer()  
 elseif "applicationResume" == event.type then  
 -- add missing time to tPrevious  
 tPrevious = tPrevious + ( system.getTimer() - tSuspend )  
 end  
end  
   
Runtime:addEventListener( "system", onSuspendResume );  
  
local ui = require("ui")  
  
-- Variable for lost time capture  
local tPause = system.getTimer()  
  
-- Variable for chosen button  
local activeButton  
  
-- Variable for active state  
local statusActive = false  
  
-- Function purpose: prep for lost time capture; pause scene via event listerner removal  
local function movePause()  
 if statusActive == true then  
 tPause = system.getTimer()  
 if activeButton == "left" then  
 Runtime:removeEventListener("enterFrame", moveRight)  
 elseif activeButton == "right" then  
 Runtime:removeEventListener("enterFrame", moveLeft)  
 end  
 end  
end  
  
-- Function purpose: capture lost time for smooth transitions; start scene via event listener  
local function moveStart()  
 tPrevious = tPrevious + ( system.getTimer() - tPause )  
 if activeButton == "left" then   
 Runtime:addEventListener("enterFrame", moveLeft)  
 elseif activeButton == "right" then   
 Runtime:addEventListener("enterFrame", moveRight)  
 end  
end  
  
-- Function purpose: horizontally flip and reposition sprite  
local function spriteFlip()  
 if activeButton == "left" then  
 spriteInstance.x = 480  
 spriteInstance.xScale = 1  
 elseif activeButton == "right" then  
 spriteInstance.x = 200  
 spriteInstance.xScale = -1  
 end  
end  
  
-- Button event handlers  
local buttonHandler\_left = function( event )  
 if event.phase == "release" and activeButton ~= "left" then  
 activeButton = "left"  
 movePause()  
 moveStart()  
 spriteFlip()  
 statusActive = true  
 end  
end  
  
local buttonHandler\_right = function( event )  
 if event.phase == "release" and activeButton ~= "right" then  
 activeButton = "right"  
 movePause()  
 moveStart()  
 spriteFlip()  
 statusActive = true  
 end  
end  
  
-- Display navigation buttons  
local buttonLeft = ui.newButton{  
 default = "ArrowLeft.png",  
 over = "ArrowLeftOver.png",  
 onEvent = buttonHandler\_left,  
 x = 20,  
 y = 170,  
 text = " L",  
 size = 12,  
 emboss=true  
}  
  
local buttonRight = ui.newButton{  
 default = "ArrowRight.png",  
 over = "ArrowRightOver.png",  
 onEvent = buttonHandler\_right,  
 x = 460,  
 y = 170,   
 text = "R",  
 size = 12,  
 emboss=true  
}  

Thanks for reading!

Comments? [import]uid: 12397 topic_id: 6748 reply_id: 23750[/import]

When you see width or height divided by 2, it’s because the x & y (by default) refers to the center of the object, not the top/left coords of the obj [import]uid: 6175 topic_id: 6748 reply_id: 34218[/import]

I copied the main.lua code as is and added buttons. Only the horse is animated, everything else is static. Looks like some of the code is missing or not all is in the main.lua shown here. (Ex. buttonHandler_stop isn’t in the main.lua code.) Does your code work exactly as shown here in main.lua? [import]uid: 40033 topic_id: 6748 reply_id: 33985[/import]