Transitions in for loop

Hello,

I want transitions to be started one after another and setup (sequentially) in for loop.

So I made the following example:

for i = 1, 10, 1 do local img = display.newImage("img/a.png") transition.to(img, {time = 999, x = 100, y = 100, delay = ((i - 1) \* 1000), onStart = function() print(("iter %d started"):format(i)) end, onComplete = function() print(("iter %d ended"):format(i)) end}) end

Unfortunately, output looks like this:

iter 1 started iter 1 ended iter 2 started iter 3 started iter 2 ended iter 3 ended iter 4 started iter 4 ended iter 5 started iter 5 ended iter 6 started iter 7 started iter 6 ended iter 7 ended iter 8 started iter 8 ended iter 9 started iter 10 started iter 9 ended iter 10 ended

That means I cannot guarantee transition execution order using delay parameter (unless delay is big enough and what is big enough? 10ms delta or 1000ms delta?)

So the only option to go is onComplete parameter, but I just don’t know how to chain these transitions in for loop using onComplete so that execution order is 100% one by one. Assume every transition is performed on unique object with different parameters.

when you say ‘one after another’ do you mean sequentially or overlapping?

transition A --> transition B --> transition C --> etc.

  t0                   t1                      t2

where t1 == t0 + duration of transition A, and so on

or

transition A start at t0

transition B start at t0

transition C  start at t0

If you want the prior, just use onComplete to start the next transition.

MAY HAVE TYPOS

for i = 1, 10, 1 do local img = display.newImage("img/a.png") img.iter = 1 function img.onComplete(self) self.iter = self.iter + 1 print( "iter " .. tostring(self.iter) .. " completed" ) self:onStart() end function img.onStart(self) print( "iter " .. tostring(self.iter) .. " started" ) transition.to( self, { x = 100, y = 100, time = 1000, onComplete = self, onStart = self } ) end img:onStart() end

I meant sequentially.

It’s interesting code, however, I cannot run it. Please note, that transition object and params are/must be different on each iteration.

It looks like 

onStart = self

causes stack overflow. But even if we remove it, the loop is infinite.

Why the output in my first post is not sequential? Transitions are timed sequentially (see code in the first post, please). I understand that timing is not precise as an absolute value, but I thought it should strictly respect timing as relative value… is it a bug or feature?

In all likelihood it is a bug in your code.

The transition.* library is quite robust.  

Honestly, I’m very unclear on what you’re trying to do.  Can you make a small sample project just a main.lua file a short demo of what you’re trying to do?  Then share it here via link.

Also, there may be an issue with you directly using i like that, try this and see if it makes any difference.

You are using i in two ways:

  • Immediate evaluation to set the delay times.

  • (possible) delayed evaluation in the closures.  I can’t remember how Lua handles evaluating a out-scoped local variable (created by a loop) in a closure, when that variable has dropped out of scope by the time the closure executes.

@Lua experts. Is i (in the for loop) a single variable or a new variable on each iteration?

for i = 1, 10, 1 do local img = display.newImage("img/a.png") local count = i; -- new variable made each loop, thus disconnecting i from scope & visibility in -- closures below transition.to(img, {time = 999, x = 100, y = 100, delay = ((count - 1) \* 1000), onStart = function() print(("iter %d started"):format(count)) end, onComplete = function() print(("iter %d ended"):format(count)) end}) end

Additionally, I’d be curious to see what this prints:

for i = 1, 10, 1 do local img = display.newImage("img/a.png") local iter = i local began = system.getTimer() local function onStart() local dt = system.getTimer() - began print( string.format("iter %d started @ %f", iter, began) ) end local function onComplete() local curT = system.getTimer() local dt = curT - began print( string.format("iter %d ended @ %f; dt: %f", iter, curT, dt ) ) end transition.to(img, { time = 999, x = 100, y = 100, delay = ((i - 1) \* 1000), -- i is safe in this context; immediate evaluation onStart = onStart, onComplete = onComplete }) end

I’m new to Lua/Corona, so first of all I try to understand how can I chain transitions sequentially using time and delay parameters.

And so far I failed to do so, because my transitions are mixed judging by produced output.  

So if it’s a bug in my code I’d really like to see where it is. And my code in the first post in the content of main.lua - short demo and nothing more :slight_smile:

Oh, now it’s even more interesting… Could you please explain the output?

"iter 1 started @ 3300.500000" "iter 1 ended @ 4321.600000; dt: 1021.100000" "iter 2 started @ 3300.700000" "iter 3 started @ 3300.700000" "iter 2 ended @ 5331.600000; dt: 2030.900000" "iter 3 ended @ 6308.200000; dt: 3007.500000" "iter 4 started @ 3300.800000" "iter 4 ended @ 7319.100000; dt: 4018.300000" "iter 5 started @ 3300.900000" "iter 5 ended @ 8319.700000; dt: 5018.800000" "iter 6 started @ 3301.000000" "iter 7 started @ 3301.000000" "iter 6 ended @ 9335.200000; dt: 6034.200000" "iter 7 ended @ 10319.300000; dt: 7018.300000" "iter 8 started @ 3301.100000" "iter 9 started @ 3301.200000" "iter 8 ended @ 11352.400000; dt: 8051.300000" "iter 10 started @ 3301.300000" "iter 9 ended @ 12335.100000; dt: 9033.900000" "iter 10 ended @ 13335.500000; dt: 10034.200000"

Output shows that iteration 2 and 3 were started simultaneously…

I’ve executed my code on two computers (in Corona Simulator) - both showed “mixed” transitions

There was an issue in your code therefore we saw simultaneous executions…

I’ve modified it slightly to the following:

for i = 1, 10, 1 do local img = display.newImage("img/a.png") local iter = i local function onStart() print( string.format("iter %d started @ %f", iter, system.getTimer()) ) end local function onComplete() print( string.format("iter %d ended @ %f", iter, system.getTimer() ) ) end transition.to(img, { time = 999, x = 100, y = 100, delay = ((i - 1) \* 1000), -- i is safe in this context; immediate evaluation onStart = onStart, onComplete = onComplete }) end

Output:

"iter 1 started @ 3164.300000" "iter 1 ended @ 4167.600000" "iter 2 started @ 4167.700000" "iter 2 ended @ 5177.500000" "iter 3 started @ 5177.600000" "iter 4 started @ 6171.300000" "iter 3 ended @ 6198.400000" "iter 4 ended @ 7201.300000" "iter 5 started @ 7201.400000" "iter 6 started @ 8184.300000" "iter 5 ended @ 8214.000000" "iter 7 started @ 9168.400000" "iter 6 ended @ 9208.400000" "iter 7 ended @ 10202.400000" "iter 8 started @ 10202.400000" "iter 9 started @ 11186.500000" "iter 8 ended @ 11214.400000" "iter 10 started @ 12178.300000" "iter 9 ended @ 12210.300000" "iter 10 ended @ 13197.200000"

Maximum “overlap” is 40ms! (from the output above)

This makes sense to me.  The output is produced on the next FRAME, so even if the transition has ended, that code may execute a bit later.  This will cause the overlaps you’re seeing.

The only correct way to chain transitions is by starting the subsequent transition in the onComplete of the just finished transition.

You cannot rely on delays and times for perfect alignment.

PS - This is the stinky thing about mixing frame based code with timed code.  

Did I get it correctly: output of print function will be produced (shown) in the next frame? But strings should be formatted in the current frame anyway (including time that should be evaluated in the current frame too). So in general we should not care when the output appears as long as the formatted strings contain correct information. And from that I see that iteration 4 starts before iteration 3 ends.

Where can I read more about frame based code vs timed code? I think all code is a frame based code even if it’s timed (perfectly or not).

The entire closure is executed in the frame, that includes calling print, evaluating values in print statement, everything in the closure.

No Lua code is called at the very moment transition time elapses.  The execution is scheduled to happen in the next frame (when the Lua interpreter takes over to handle listeners, etc.).

Timed versus Frame

You misunderstand me I think. 

Corona is essentially a simulation.  It has a bunch of tasks to execute.  It executes those tasks in a specific order every ‘cycle’.  

While I have not seen the actual source code, I can say with some certainty that the basic cycle (which is equivalent to one frame) is split into two major parts:

A. Timed Part - Timer advancement, transition advancement, physics, …, all of the non-Lua behind the scenes stuff.  In this half/part of the cycle, events will occur and be detected that cause Corona to schedule the execution of listeners, functions in timers, etc.  All of this scheduled Lua work occurs in the next half/part of the cycle.  

The timed part can take  a variable amount of time, but will take only the amount of time it needs and then gives up the rest of the time in the frame to the Lua interpreter.

B. Frame Part  (or what we in our scripts see of it) - Here, all the Lua excution occurs.  The Lua interpreter is started up and give a load of work to do based on what happened in the timed part. 

This part also can run for an unknown amount of time and will keep running till all the scripts are complete.  

Tip: When you see frame rate drops and your graphics/physics are light, it is a good bet you’re doing too much work in the scripts that hold up this part of a frame.

Back to your original post…

All I’m saying above is that the timers and transitions are executing and completing in part A.  However the scripts associated with that completion is not executed immediately.  It waits till part B.  So your scheduled transition duration will be shorter than the actual duration between when the transition started, and when the onComplete closure executes.

Also, I’ll say it once more…

You simply CANNOT schedule transitions in a loop (all in the same execution cycle) and have any hope that they will execute perfectly back to back.  

You must instead, execute one transition, and start the next consecutive one in the onComplete of the ending transition.

I do wish that some time in the past, a core engineer had drawn us a high level diagram of a single frame cycle and what it looks like.

However, my estimation of how this works is based on prior experience coding engines and helping maintain them.

PS - I’m doing a terrible job with the nomenclature.  So that could be part of the problem.  My apologies if I’ve made this more confusing.

I’ve understood (I think) everything that you said. And I understand that if I set transition to be for example 1 second long it simply won’t be for many reasons, but the engine will try to do its best to achieve desired or specified time. Same applies to delay.

My main concern is the following: if I specify that Transition A should be ended at 11000ms, and Transition B should be ended at 11001ms, I want that TransitionA.onComplete is executed BEFORE TransitionB.onComplete. And I don’t care that these callbacks are not fired perfectly at 11000 and 11001 (it’s impossible), all I want is the correct order. And from engine point of view I don’t understand why this is hard to do (you just compare two numbers and fire callbacks in the order based upon transition params comparision).

Anyway, thank you very much for your help! :slight_smile:

And also would be really nice to have an answer from someone who has access to sources of the engine. I’m not really convinced that this is correct behavior. Maybe they do it like this for performance reasons, I don’t know. 

I’m quite sure that even if you do this, you’ll get ordered completions:

local obj = display.newCircle( 10, 10, 10 ) transition.to( obj, { x = 100, onComplete = function() print("A") end } ) transition.to( obj, { y = 100, onComplete = function() print("B") end } ) transition.to( obj, { alpha = 0, onComplete = function() print("C") end } ) transition.to( obj, { xScale = 2, onComplete = function() print("D") end } )

This must print,

A

B

C

D

after about 1000 ms, or there is a bug.

The queuing order doesn’t change and is in-order.

It prints A, B, C, D. 

when you say ‘one after another’ do you mean sequentially or overlapping?

transition A --> transition B --> transition C --> etc.

  t0                   t1                      t2

where t1 == t0 + duration of transition A, and so on

or

transition A start at t0

transition B start at t0

transition C  start at t0

If you want the prior, just use onComplete to start the next transition.

MAY HAVE TYPOS

for i = 1, 10, 1 do local img = display.newImage("img/a.png") img.iter = 1 function img.onComplete(self) self.iter = self.iter + 1 print( "iter " .. tostring(self.iter) .. " completed" ) self:onStart() end function img.onStart(self) print( "iter " .. tostring(self.iter) .. " started" ) transition.to( self, { x = 100, y = 100, time = 1000, onComplete = self, onStart = self } ) end img:onStart() end