After searching for pausable timers for corona I found only several implementations. Beebe used enterFrame listener to implement his own timers and transitions. Transition manager has memory leaks and lacks some functionality. So I decided to wrap around existing corona timers and transitions into my own implementation.
Remember to free the memory used by this module, I advice you you to call cleanTimersAndTransitions() function with an infinite timer (say one or two seconds delay).
UPD 2012-04-14: Version 1.2 released
UPD 2011-08-05: Version 1.1.2 released
Here is the code.
tnt.lua
-- Pauseable timers and transitions with speed adjustment -- Author: Lerg -- Release date: 2012-04-14 -- Version: 1.2 -- License: MIT -- Web: http://developer.anscamobile.com/code/pausable-timers-and-transitions-speed-adjustment -- -- USAGE: -- Import this module with a desired name, for example: -- tnt = require('tnt') -- Then you create timers and transitions with the same logic as before: -- timer1 = tnt:newTimer(1000, function () print('tick') end, 1, {name = 'Tick Timer', userData = 'User data', onEnd = function (event) print(event.name .. ' has completed') end}) -- trans1 = tnt:newTransition(object, {time = 1000, x = 480, name = 'Slide Transition', userData = 'User data', cycle = 10, backAndForth = true, onEnd = function (object, event) print(event.name .. ' has completed') end}) -- Name and userData arguments are optional. userData can be anything. -- onEnd callback (or object listener) is fired once timer or transition has finished it's job completely, after all ticks or transition cycles. -- With cycle param you can tell transition to loop. 0 - infinite times. You can also set backAndForth param. -- Every instance has pause(), resume() and cancel() methods. -- You can manage all timers and transitions with function like tnt:pauseAllTimers(), tnt:resumeAllTransitions() etc. -- For speed adjustment first pause all timers and transitions, then modify tnt.speed to say 0.5, which means 2 times faster -- and lastly resume all paused instances. -- -- LIMITATIONS: -- Doesn't work with delta transitions. Easings will start over after each pausing, it can be fixed, but I don't need it at the moment, -- so didn't implemented. Fix would be to set up custom easings and pass elapsed time to each easing function. -- -- CONTRIBUTORS: -- CluelessIdeas (www.cluelessideas.com), TMApps (www.timemachineapps.com/blog) -- -- CHANGELIST: -- 1.2: -- [Feature] Added cycling support for transitions. Both repeating and "back and forth" loops. Infinite and finite. -- [Feature] Added onEnd listener support - callback to be called when timer or transition elapsed completely (repeative timers and transitions) -- [Feature] Added table listeners support. Events are timer, timerEnd, transition, transitionEnd -- [Feature] Added speed constants tnt.NORMAL, tnt.FAST and tnt.SLOW - feel free to use them or add your own. -- 1.1.2: -- [Bug] Transitions are wrongfully decided to be already ended. -- [Feature] Added name and userData params for transtitions just like for timers. -- [Feature] Added LuaDoc. -- [Feature] Added default value for the count argument. -- 1.1.1: -- [Bug] Quick bugfix on remainingTime calculations. -- 1.1: -- [Bug] onComplete function is not getting called for transitions when pausing right before the event. -- [Bug] Timers are not counting resting time from resuming till next pausing (before next tick). -- [Feature] Added userData and name to actual timers instances, they are accessible through event callback function argument, like event.userData and event.name. -- [Feature] Added cleanTimersAndTransitions() function which frees the memory on demand (you can call it every couple of seconds) -- -- I can be found on the corona IRC channel. -- Module table local _M = {} -- Game speed: 1 - normal, 0.5 - fast, 2 - slow _M.speed = 1 _M.NORMAL = 1 _M.FAST = 0.5 _M.SLOW = 2 -- Every instance is hold here local allTimers = {} local allTransitions = {} -- Cache local tInsert = table.insert local tRemove = table.remove -- Pausable timers -- @param duration number Transition duration. -- @param callback function Function to be called on the each tick. -- @param count number How many times to tick, 0 - unlimited. Default is 1. -- @param params table Extra parameters. Optional. -- name string The name for the timer. Available in the callback. -- userData table Any user data. Available in the callback. -- onEnd function A callback to call when timer is elapsed completely (count wise). function _M:newTimer (duration, callback, count, params) -- Timer handler local tH = {} tH.speed = self.speed tH.start = system.getTimer() tH.duration = duration tH.callback = callback tH.count = count or 1 tH.counter = 0 tH.isInfinite = (count == 0) if params then tH.name = params.name tH.userData = params.userData tH.onEnd = params.onEnd end tH.shouldRemove = false tH.paused = false tH.intervalStartTime = tH.start tH.remainingTime = duration -- Internal function which fires up the actual callback function -- @param event Corona's timer event local function callbackWrapper (event) local tH_callback = tH.callback if tH_callback then event.userData = tH.userData event.name = tH.name if type(tH_callback) == 'function' then tH_callback(event) elseif type(tH_callback) == 'table' and type(tH_callback.timerEnd) == 'function' then tH_callback:timerEnd(event) end if not tH.isInfinite then tH.counter = tH.counter + 1 if tH.counter >= tH.count then tH:cancel() local onEnd = tH.onEnd if type(onEnd) == 'function' then onEnd(event) elseif type(onEnd) == 'table' and type(onEnd.timerEnd) == 'function' then onEnd:timerEnd(event) end end end tH.remainingTime = tH.duration tH.intervalStartTime = system.getTimer() else tH:cancel() end end tH.t = timer.performWithDelay(tH.duration * self.speed, callbackWrapper, tH.count) -- Cancels running timer and prepares for the resuming function tH:pause () if self.t then timer.cancel(self.t) end if not self.paused then self.paused = true self.pausingTime = system.getTimer() self.remainingTime = self.remainingTime - (self.pausingTime - self.intervalStartTime) if self.remainingTime < 0 then self.remainingTime = 0 end end end -- Initiates a fresh timer if paused function tH:resume () if self.paused then self.paused = false if not self.isInfinite then -- Timer elapsed if self.counter >= self.count then self:cancel() else local function callbackDoubleWrapper (event) callbackWrapper(event) local ticksRemains = self.count - self.counter if ticksRemains > 0 then self.t = timer.performWithDelay(self.duration * _M.speed, callbackWrapper, ticksRemains) self.speed = _M.speed else self:cancel() end end self.intervalStartTime = system.getTimer() self.t = timer.performWithDelay(self.remainingTime * _M.speed, callbackDoubleWrapper, 1) self.speed = _M.speed end else local function callbackDoubleWrapper (event) callbackWrapper(event) self.t = timer.performWithDelay(self.duration * _M.speed, callbackWrapper, 0) end self.intervalStartTime = system.getTimer() self.t = timer.performWithDelay(self.remainingTime * _M.speed, callbackDoubleWrapper, 1) self.speed = _M.speed end end end -- Cancels actual timer instance and marks this handler to be removed function tH:cancel () if self.t then timer.cancel(self.t) end self.shouldRemove = true self.callback = nil end tInsert(allTimers, tH) return tH end -- Pauses everything in the allTimers table function _M:pauseAllTimers() local i local allTimersCount = #allTimers if allTimersCount > 0 then for i = allTimersCount, 1, -1 do local child = allTimers[i] if child.shouldRemove then tRemove(allTimers, i) else child:pause() end end end end -- Resumes everything in the allTimers table function _M:resumeAllTimers() local i local allTimersCount = #allTimers if allTimersCount > 0 then for i = allTimersCount, 1, -1 do local child = allTimers[i] if child.shouldRemove then tRemove(allTimers, i) else child:resume() end end end end -- Cancels everything in the allTimers table function _M:cancelAllTimers() local i local allTimersCount = #allTimers if allTimersCount > 0 then for i = allTimersCount, 1, -1 do local child = allTimers[i] child:cancel() tRemove(allTimers, i) end end end -- Pausable transitions -- @param object table An object for which transition is applied. -- @param params table Transition parameters. -- name string The name for the transition. Available in the onComplete function. Optional. -- userData table Any user data. Available in the onComplete function. Optional. -- cycle number How many times to repeat transition. 0 - infinite. Optional. -- backAndForth boolean Should it be back and forth cycling? Optional. -- onEnd function A callback to call when transition is completed completely (count wise). Optional. function _M:newTransition(object, params) -- Transition handler local tH = {name = params.name, userData = params.userData, originalTime = params.time, cycleCount = 0} local elapsed, elapsedCount, currentCountRemains local onComplete = params.onComplete local onEnd = params.onEnd local cycleTransition = params.cycle or 1 local backAndForthCycling = params.backAndForth or false local initialValues = {} for k, v in pairs(params) do if k ~= 'onComplete' and k ~= 'onEnd' and k ~= 'time' and k ~= 'transition' and k ~= 'delta' and k ~= 'name' and k ~= 'userData' and k ~= 'cycle' and k ~= 'backAndForth' then initialValues[k] = object[k] end end -- This function is called for each completed transition to mark it's handler for removal local function callbackWrapper () if type(onComplete) == 'function' then onComplete(object, {userData = tH.userData, name = tH.name}) elseif type(onComplete) == 'table' and type(onComplete.transition) == 'function' then onComplete:transition(object, {userData = tH.userData, name = tH.name}) end local doRepeat = false if cycleTransition > 0 then tH.cycleCount = tH.cycleCount + 1 if tH.cycleCount >= cycleTransition then tH:cancel() if type(onEnd) == 'function' then onEnd(object, {userData = tH.userData, name = tH.name}) elseif type(onEnd) == 'table' and type(onEnd.transitionEnd) == 'function' then onEnd:transitionEnd(object, {userData = tH.userData, name = tH.name}) end else doRepeat = true end elseif cycleTransition == 0 then doRepeat = true end if doRepeat then transition.cancel(tH.t) tH.params.time = tH.originalTime tH.start = system.getTimer() tH.elapsed = nil for k, v in pairs(initialValues) do if not backAndForthCycling then object[k] = v else tH.params[k] = v initialValues[k] = object[k] end end tH.t = transition.to(object, tH.params) end end tH.params = {} -- Make a shallow copy of the user's params so they are not messed up in the user's space for k, v in pairs(params) do tH.params[k] = v end tH.params.onComplete = callbackWrapper tH.params.time = tH.originalTime * self.speed tH.t = transition.to(object, tH.params) tH.start = system.getTimer() tH.speed = self.speed -- Stops current transiton and prepares for the resuming function tH:pause() if self.t then self.elapsed = (system.getTimer() - self.start) / self.speed transition.cancel(self.t) else self:cancel() end end -- Initiates a fresh transition if paused function tH:resume() if self.elapsed and not self.shouldRemove then -- Current speed local s = _M.speed self.params.time = (self.originalTime - self.elapsed) * s self.t = transition.to(object, self.params) self.start = system.getTimer() - self.elapsed * s self.speed = s self.elapsed = nil end end -- Cancels actual transition instance and marks this handler to be removed function tH:cancel() if self.t then transition.cancel(self.t) end self.shouldRemove = true end tInsert(allTransitions, tH) return tH end -- Pauses everything in the allTransitions table function _M:pauseAllTransitions() local i local allTransitionsCount = #allTransitions if allTransitionsCount > 0 then for i = allTransitionsCount, 1, -1 do local child = allTransitions[i] if child.shouldRemove then tRemove(allTransitions, i) else child:pause() end end end end -- Resumes everything in the allTransitions table function _M:resumeAllTransitions() local i local allTransitionsCount = #allTransitions if allTransitionsCount > 0 then for i = allTransitionsCount, 1, -1 do local child = allTransitions[i] if child.shouldRemove then tRemove(allTransitions, i) else child:resume() end end end end -- Cancels everything in the allTransitions table function _M:cancelAllTransitions() local i local allTransitionsCount = #allTransitions if allTransitionsCount > 0 then for i = allTransitionsCount, 1, -1 do local child = allTransitions[i] child:cancel() tRemove(allTransitions, i) end end end -- Deletes unused instances (frees memory) function _M:cleanTimersAndTransitions() local i local allTimersCount = #allTimers if allTimersCount > 0 then for i = allTimersCount, 1, -1 do local child = allTimers[i] if child.shouldRemove then tRemove(allTimers, i) end end end local allTransitionsCount = #allTransitions if allTransitionsCount > 0 then for i = allTransitionsCount, 1, -1 do local child = allTransitions[i] if child.shouldRemove then tRemove(allTransitions, i) end end end end return _M
I described the usage in the module itself and it's pretty straightforward. If you'll have any questions, feel free to ask.
Hi,
although I had implemented my own pausable timers, I started to used this due to it can also be used with transitions.
However, after making several days of testing, I realized that the timers code doesn't work properly when multiple pause-resume are executed.
There are two problems:
1. in resume method when a timer is not infinite and not(self.counter >= self.count), the attribute paused is not set to false until the doublecallback function is executed. So the pausingTime variable cannot set in pause method until the callback funcion will be executed.
2. the delay time used to resume a timer is based on pausingTime and the lastTickTime. But this relation doesn't fulfil in multiple pause-resume.
A better and more general approch to calculate the resting time for a timer can be found in
http://www.timemachineapps.com/blog/2011/07/timers-with-pause-in-corona-sdk/?lang=en
Anyway, I have fixed this code, how could I upload this code? Other improvements implemented are:
- callback invocation with event parameter.
- the inclusion of userdata in the timers in order to use them in the callback function. This is essential when you work with multiple timers working the same callback function.
Regards
(deleted)
Hello!
Right, I also had a feedback regarding this bug and regarding memory usage (tables are not cleared by themselves).
I fixed an issue with transitions (onComplete is not getting called when you pause right before it). And had no time to fix the timer thing (after resuming if you pause again it wouldn't count this elapsed time). Although I know the approach how to fix this, I would appreciate if you'll send me your modifications fixing those bugs and describing new features.
Can you contact me on the corona IRC channel? Or post a link to the pastebin. I'll review your code and make an update for the module.
Thanks
Hello Lerg,
I have posted the code in pastebin, since I don't use the corona IRC channel. This is your code fixed, so you only have to replace it. I have used the approach of my old pausable timers, which are explained in my blog entry.
Other improvements that I had in my pausable timers and that I've also included in you code are:
- Another parameter for user data. This information is included in the corona timer, and it is accessible through the event parameter of the callback function. As I told this is fundamental when you use several timers simultaneously and you need to identify what are the involved game elements.
- In order to use it, callback function has to be invoked with the event parameter. This is also added to your code.
On the other hand, we have also developed pausable transitions, if you want, we can also review your code in order to fix possible errors.
Finally, we be grateful to you for including a reference in this entry to our blog as authors of this fixing and the others new features.
Regards.
TMApps
I am happy to announce that I've just released a new version of this module (1.1.1).
I got your code, tmapps, cleaned it up a bit and merged into the source. Thank you, indeed I had to setup remainingTime variable.
There are also other bugfixes and improvements.
thanks! by the way, remainingTime was setup in the line 28 of my code: tH.remainingTime = tH.duration :-)
Regards
tmapps, I know, I was talking about your remainingTime var. duration instead of tH.duration is just a bit faster.
Love this file, been using for a lot of the games I'm working on! Found a small issue...
When I pass a callback for a timer, you don't pass back the "event" param when you call the callback. So I wasn't able to get event.count when using your file. You can fix this by changing the following in the "_M:newTimer" function...
1 2 3 4 5 6 7 8 9 10 11 | local function callbackWrapper () if tH.callback then tH.callback() if not tH.isInfinite then tH.counter = tH.counter + 1 end tH.lastTickTime = system.getTimer() else tH.shouldRemove = true end end |
to..
1 2 3 4 5 6 7 8 9 10 11 | local function callbackWrapper (event) if tH.callback then tH.callback(event) if not tH.isInfinite then tH.counter = tH.counter + 1 end tH.lastTickTime = system.getTimer() else tH.shouldRemove = true end end |
All I did was add an event param and pass that event to " tH.callback(event)"
Hi Edgar. Thanks.
Please look at the top of the page, there is already this implementation, I had an update.
Hi Lerg, first of all thanks for the amazing work!!!
I'm sorry but i have a (maybe) dumb question, why at the end of the :new function you return tH instead of tH.t (the actual timer)?
@liongera
Because tH is a wrapper around the actual timer tH.t. This wrapper has :pause, :resume and :cancel methods, which actual timer hasn't.
I see, thanks so much Lerg :)
I was trying to mantain the timer.performWithDelay interface, and i was struggling a little bit, but i got it working, thanks again!
Found another small issue...
You need to change the following at the start of "function _M:newTimer"
1 | tH.count = count |
to...
1 | tH.count = count == nil and 1 or count |
The first line was giving me issues when I paused a timer in which I didn't pass a count (because count would be nil). Usually when you use Corona's timer.performWithDelay() if you do not pass in a count it assumes you only want to fire the function once.
Thanks edgar for pointing this out.
However I think the more correct line would be just
tH.count = count or 1
as usual default value assignment in Lua. We don't need to treat 'false' as a valid value.
Thanks, that will be in the next release, which is coming out soon.
Yay! A new version have been just released! 1.1.2 - even more stable transitions, added name and user data for transitions just like for timers.
Moving to perfection!
Thanks everyone for testing and advices.
It's looking great! I'll be sure to use this updated version on my next game and let you know if any issues/bugs come up!
Thanks again for putting this together, really helping me out on a lot of games :).
One request I have, this came up during one of the last games I built.
I was making a game in which something swings back and fourth using your class, in which I wanted to increase the motion in which it swings back and fourth. You could do this by simply changing .speed, but I had an issue. I was also using your class to make other things move in the game which I didn't want the speed to change. The request I'm trying to make is... could you implement this class such that you are allowed to change the speed of individual timers/transitions? For the problem I just described, I just requires your file twice, and used a separate object for my main game transition/timers and another one for the swinging object. But it would be nice if it had built in functionality to just change the .speed of any timer/transition.
Let me know if this make sense, would be happy to clarify some more :)
BTW, love the updated comments in the code. Makes it much easier to read :)
Edgar, yeah, this is really what I haven't though about yet. And it's very easy to implement.
Other thing, would you like to have an ability to modify timers parameters on the fly?
Like
t = tnt:newTimer(1000, func, 0)
Then somewhen
t.duration = 5000
t.count = 1
And call for the changes to take the effect
t:update()
Maybe also some resetting function?
t:reset() - and it starts to tick from the beginning
Same for transitions. Actually you can modify transitions params even now
t = tnt:newTransition(object, {time = 1000, x =480})
t.params.x = 320
t:pause()
t:resume()
Is it interesting?
That would be interesting! In my other game I actually wanted to modify my transition parameters on the fly, I didn't know you could do that! So I ended up making an ugly hack instead :(. Perhaps you could make an update params function to make it more obviously that you can change parameters on the fly?
Another thing that would be nice, this comes from the Tween library in Flash, is if you could have a "loop" parameter. If it's true the tween loops, if false it doesn't. This would save some coding on thing that just move back and fourth.
If you want to get more ideas on how to make your class more powerful and robust in regards to the transitions take a look at the green sock library...
http://www.greensock.com/get-started-tweening/
It's the most advance tween library I seen. It's written in AS3 (which is very similar to Lua). If you take a look at the link you can even see examples right there in the page. You can get a better example of how power the library is by looking at their banner here... http://www.greensock.com/ it was all done using their library. I used this library in the past when I was doing Flash development, it was really great working with it. Made a lot of things easier, just like this class you wrote :)
I'm always a big fan of code that helps me write less code, so if you can get this up to the level of green sock you might even be able to sell it, I'd buy a copy :)
Tweening library is quite a work.
It might be better to create one on top of this module rather then extending it.
But basic tweening is affordable at this level, so will do something.
This looks very good, but I can't get it to cancel an individual timer.
I'm using
handle = th:newTimer(10000, myfunction )
handle:cancel()
is this correct?
Hi Lerg,
Thanks very much for your very useful pausable timer module.
I had a use where I wanted to know how much time had passed and how much time was remaining. So I wrote the following:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | -- Returns the two values: timesofar amd timeremaining function tH:getTimes () local remainingTime if self.paused then remainingTime = self.remainingTime else local pausingTime = system.getTimer() remainingTime = self.remainingTime - (pausingTime - self.intervalStartTime) if remainingTime < 0 then remainingTime = 0 end end return (self.duration - remainingTime), remainingTime end |
Note that it doesn't alter any variables other than those local to the function so can't introduce any bugs into your routines.
I haven't tested at different speeds!
Perhaps you'll know whether it will work at different speeds?
If you do decide to include in a later version then in honour of the "non-repeated code" principle you will note that it repeats some code from the pause function so you may wish to move the repeated lines out to another shared routine. I'll leave it in your good hands ;-)
Suhada
idealconceptz, it is correct. If you have any problems you can try to catch me on the IRC channel so we can discuss it.
SUHADA, thanks. I'll add a similar function in the next release. As for speed only the duration variable should be multiplied with the speed.
Hello-
I am getting this error when using your code-
...awesome_timers_and_transitions_manager_from_lerg.lua:85: attempt to c
all field 'callback' (a table value)
stack traceback:
[C]: in function 'callback'
...awesome_timers_and_transitions_manager_from_lerg.lua:85: in function
'_listener'
Any idea what could cause that?
If this helps, I am declaring my callback function like this:
local xxxxxx = {}
function xxxxxx:timer (event)
--code
end
Thanks
Hi dellagd,
Table listeners aren't supported yet. You should use a closure
tnt:newTimer(1000, function (event) xxxxxx:timer(event) end)
Table callers will be in the next version.
Hi, here's a nice little addition:
It allows you to queue up a series of transition parameters to be performed in sequence on the same display object. A list of parameter lists is passed to it. Only the onComplete from the final set of parameters is called, at the end of all the transitions.
Note that currently this is slightly inconsistent with your original code, as it does not (currently) make a local clone of the parameter data.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | -- Pausable transition sequence -- @param object table An object for which transition is applied. -- @param params_array table An array of transition parameters function _M:newTransitionSequence(object, params_array) local tsH = {} tsH.params_array = params_array -- the only user onComplete we keep is -- the last one in the params array local currentTransition = nil for i = 1,(#params_array-1) do params_array[i].onComplete = function(target) currentTransition = _M:newTransition(object, params_array[i+1]) end end currentTransition = _M:newTransition(object, params_array[1]) function tsH:cancel() currentTransition:cancel() end function tsH:pause() currentTransition:pause() end return tsH end |
Demo of use:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | local trans_sequence = { { time = 2000, x = 10, y = 20, rotation = 360 }, { delay = 1000, transition = easing.easeIn, time = 3000, xScale = 0.001, yScale = 0.001 }, { time = 1000, alpha = 0 } } tm:newTransitionSequence(my_sprite, trans_sequence) |
theo1, thanks. That might be useful.
I will probably build a twinning library on top of this module.
In my development version I have ability to cycle transitions: repeat and "back and forth".
Great work Lerg!
Can you explain how to use your file with something like this :
transition.to( obj,{ delay = 8500, time=1500, alpha=1.0, onComplete=nextPart } )
I use director of Ricardo Rauber, and sometimes when I change a scene, a transition of a former scenes executes.
pasztelfort, hi.
I couldn't understand what do you mean, but I guess 'delay' param causes you problems, It's not well supported by my module.
Try to use it like this instead:
tnt:newTimer(8500, function () tnt:newTransition(obj, {time = 1500, alpha = 1, onComplete = nextPart}) end)
If you have other questions you can find me on the IRC channel.
Lerg > your guess was right ! I'm sorry I was not clear enough.
Your solution is very nice. Thanks.
When onComplete callback is fired inside :newTransition, userData inside the callback is "nil". I wonder if this is a bug or a work in progress . This happens when the callback is a method of an object. Otherwise, a regular function can access the userData without a problem.
Is there any news on this ?
zeeero.coool, userData in transitions is a bit misleading, because it doesn't return an "event" object, instead it returns the actual object being transitioned.
I don't how to proceed with it. Either uncomment lines in callbackWrapper function or don't use this functionality at all.
Amazing work. Thank you Lerg
This is really great, thank you so much for sharing and keeping it updated, Lerg!
Hi!
Did something change with newTimer() in v1.2 because now I get errors with userData in my code:
... in function 'tH_callback' ... attempt to index field 'userData' (a nil value)
Line is as follows:
1 2 3 4 5 | function spawnEnemy(event) local class = event.userData.enemyClass . . . |
It worked with the previous version the way I was using userData:
1 | tnt:newTimer(e.spawnTime, spawnEnemy, 1, 'timer', { enemyClass = e.class, enemyName = e.name, enemyIndex = enemy_index, enemyWave = current_wave }) |
Solved:
I was little bit too hasty with posting here... This just shows how new I'm still to Lua but after looking into the code I realized that this is the way to do it now:
1 | tnt:newTimer(e.spawnTime, spawnEnemy, 1, { userData = {enemyClass = e.class, enemyName = e.name, enemyIndex = enemy_index, enemyWave = current_wave} }) |
Hi, hytka81,
Sorry, I forgot to mention that I've changed a bit the way how to deal with userData, but you figured this out!
Thank you Lerg for all your work. Your reply #29 was very helpful too since I was previously using a delay with transition.to.
This is a must have. Thanks for sharing.