Custom timer. Make your version so we can share ways to do things.

@remiduchalard, thank you for sharing your approach :slight_smile:

Your approach is interesting, you only use 1 runtime, for multiple timers. for a scene that uses a lot of them, it looks very useful. Thanks again for sharing.

If I may give you an advice, try not use to many table.insert or table.remove. they are way overkill functions and resources consuming.

if you only need to insert a value in the last place of a table use table[#table+1]=newvalue.

for deleting only if its the last value, ofc: table[#table]=nil

but I think you already know that, for the example and showing how the timer works it’s more than enough :wink:

@StarCrunch, thanks also for your input. Sharing ideas and ways of thinking is always good.

@carloscota what I have wrote is very dirty ( I know it)

When I write some code and I know it will be call many time I try to haven’t linear time of execution per call. What I try to say :

for one use if it’s take 1ms for 10 use I try to have less than 10ms.

When I don’t keep in mind to try to have a log or square graph of execution time I very often finish with something like this Time=Number of use ^(x) (with x>1)

Also when I build my function I try to have a lot of easy access to data (read only or read and write). When I write a code for only myself I try to have as much as possible global value. It’s a bad practice for big project but when you work alone on a bunch of code, it’s help to win a lot of coding time, line of code and performance. You don’t have to make getter and setter.

Hi Carlos,

This is interesting reading.

Trying to grasp OOP myself, whereas metatables are just too alien at this pointĀ  :ph34r:

I think everyone is according that timer need an update :slight_smile:

My improvement of timer are not like yours. I don’t modify how work the timer but how they are managed.

I modify the way I use timer to answer to one problem. The way to pause many of them and resume or cancel them.

I only put all timer in an array.

Mytimer.[thetag]={all timer with theĀ  tag}

Mytimer.noTag={all timer with no tag}

like this I can use them with tag as for transition. But if I remember well sometime I have some error because very often timer depend of gameobject and I resumed timer who do something on a nil object and I haven’t find a way to ā€œkillā€ a timer when a gameObject is destroy.

@anaqim, metatables are not that hard (at least for what need it and what I know).

For example, in my code, I used metatables to initializeĀ variables like this:

local newTimer={} newTimer.prototype = {delay=1000, listener =function() end, interactions=1, autoRemove=true, autoStart=true, elapsedTime=0} newTimer.mt = {\_\_index = newTimer.prototype }

This is like a constructor that I will use each time I create a new table, those tables will have the properties in newTimer.prototype.

for this to work you need need to connect the new tables to that constructor. For that I used:

local o=oIn or {} setmetatable(o, newTimer.mt)

What I’m saying here is that the new table will be connected to newTimer.mt and newTimer.mt is connected to prototype by __index. __index means that will call the prototype variables when the new table returns nil. My EnglishĀ is not that great to explain it better. but with an example I think you will understand. for example, if you create a table:

local a={} setmetatable (a, newTimer.mt) print (a.delay)

will return 1000. why? if you remove the setmetatableĀ line, it will return nil. the setmetable line redirects the nil to the prototype table. this is only possible because you definedĀ newTimer.mt = {__index = newTimer.prototype } –  the __index is that what makes possible that redirect. this line says that…when a table defined with setmetable…if it returns nil it will point to the newTimer.prototype.

The old way of doing things would be something like this:

function newTimer.new(paramsIn) local params=paramsIn or {} local delay\_var=params.time or 1000 local func\_var=params.func or function() end local interactions\_var=params.interactions or 1 local startTime=system.getTimer() local interaction=1

@remiduchalard, didn’t understand your response. My exercise is to share how each person createsĀ an independent function, not what is used for. I could had used any other function as anĀ example, just made this one because I read on another post about timers, and people saying that used custom timers, just resolved to do one myself, nothing more. I always used Corona timer without a problem, and for my needs, it always worked without a single problem. When I check Corona Widgets code, it’s always cleaner and better designed, than if I was the one doing it. I’m not comparing my code to Corona implementation. Just comparing styles between users, hoping we all learn better technics and with that better coding. I love a function well written, is like reading a good book. I always smile when I read a good block code.

Btw, using timers as transitions doesn’t look to me, good programming practice, but without seeing your actual code I can’t comment on that.

*edit* maybe I should had used a simple example…

A lot of my timers update a single object that has a chance of being destroyed underneath me, leading to tests inside like

if object.parent then -- still alive? (seems to be okay test unless in stuff like snapshot) -- I've had issues with the more common object.removeSelf if "widgets" -- has been required as that seems to monkey-patch the display object -- metatable, for purposes of that very method in fact...

The upshot being that auto-killing the timer if said object dies can be useful. I don’t know what a good interface would be, though.

I’ve found it useful to put coroutines in timers, e.g. see here and here from my Corona Geek materials.

Hi,

Unless I am mistaken, this seems more like a conversation about metatables and OOP. In which case either a better title to the thread might be needed, or a new thread created.

Moved:Ā https://www.develephant.com/2018/06/a-simple-intro-to-metatables-and-classes/

-dev

@dev nice authoritative post!Ā Ā 

Here is the simple version… 

Without metatables all objects created from the same ā€œclassā€ will share the same memory address and thus the same values.Ā 

local a = person:create() local b = person:create()

Now b is just a pointer to a.Ā  Therefore a == b.

Using metatables ensures that each instance has a separate memory address and therefore different values.

local a = person:create() local b = person:create()

Now a ~= b and is a separate instance.

there are some great points being made here, but also some of it appears to be getting misread and/or miscommunicated (and/or maybe I myself am misreading and just nitpicking semantics - entirely possible!)

setmetatable all by itself won’t create a new instance, it just returns its first parameter - the table to which the metatable was applied.

if you call it like ā€œsetmetatable({}, _mt)ā€, it’s the ā€œ{}ā€ that actually creates the new table, then setmetatable merely returns it.

if the table passed to setmetatable already exists, all you get back is its original reference, fe:

\_mt = { \_\_index=whatever} -- condensed form makes it appear setmetatable created something: local t = setmetatable({},\_mt) -- but longhand form reveals it's just the table constructor: local t1 = {} -- creation local t2 = setmetatable(t1,\_mt) -- meta-decoration of t1 returns t1 print(t1,t2) -- t1==t2

hth

@Dev, this post was not about OOP or metatables, but starting to look one.

I could not explain it better how metatables work and why and when to use them. Kudos for your explanation.

My point on this post was about sharing different approaches to the same problem. If this was a test, the task would be: ā€œcreate your custom timer, show it here.ā€

Each person have its ownĀ way how to program, sharing different approaches would be nice to see and with hope, learn from it. I used metatable, just because IĀ was learning it and it looked easier to initialize variables using it. The question was not about metatables…if you do a custom timer without using them that’s your way, it’s not wrong or right. In fact,Ā I’ve another version without using oop or metatables.

*edit* - changed my answer to @starCrunch

@StarCrunch, my pause/resume code was just to prove that my timer actually works as it should be, it’s not optimized or the best implementation of it. I’m thinking of changing my pause function.Ā newTimer.pause(X) where X is the time it will pause and it will resume after X time. You gave me some good ideas for improving my code :). Thanks.

@SGS, your explanation is not correct from what I learned about metatables. ā€œWithout metatables all objects created from the same ā€œclassā€ will share the same memory address and thus the same valuesā€

This is not true (or IĀ didn’t understand what you meant), and Dev explained it on his great ā€œmini tutorialā€. You can create a class without metatables and create different objects from it, I used for about 5 years without a problem.

@davebollinger, yeap your right.

Still, again, this post was not aboutĀ OOP or metatables…read the title pleaseĀ or the text below the code or my @Dev response.

Regards,

Carlos.

@carloscosta

The thread is about share your custom timer.

But I think it need a previous question. What are timer problem? And then How to improve it?

In this thread there is a few answer :

  • A way to be define timer before use it

  • A way to reuse timer

  • A way to be manage globally

  • Link timer to game object

@remiduchalard, there is no timer problem. Just reinventing the wheel for learning propose, nothing more. Regarding improving. All code can be improved, even the one you think it can’t be :wink:

The ā€œtaskā€ I’m asking is really simple. Think about the Corona timer.performWithDelay. Try to mimic it, and improve it if you like it for your needs, justify it. But at least do what corona timer does. Do your own way.

I tried to mimic the timer.performWithDelay with some ā€œbonus featuresā€.

I also changed how parameters work in the ā€œtimerā€ function because all my functions accept parameters the same way, with a table.

Corona sometimes uses tables (new version of display.newText) others not really (display.newImage, etc.). I just try to make them all the same way. So it’s easier for me to not forget how they work. I really have a bad memory.

I added the ā€œelapsedTimeā€ variable so it can be used as a counter without any additional code. So, if you need a counter…this timer can do that also.

Added also, ā€œautoremoveā€Ā parameter for simplifying the process when we need a simple timer used 1 time in a scene and you will not need it again. I don’t need this function since I put all my timers in a table and delete them when I leave the scene, but maybe someone else doesn’t do what I do and needs to delete them 1 by 1, this could be useful.

Added ā€œautostartā€. declared to false, you can simply declare all your timers at the beginningĀ of you code…and just use timer1.start() when you need it. It will be a faster call than creating a new timer. In intensive fps apps this could be usefulĀ since it will not micro-lag when it’s called. You can also reuse the sameĀ timer, so this means faster access also to the timer since you don’t need to create another timer if you need one later.

If you need a better pause systemĀ or another feature I’m not thinking about just create it :slight_smile:

My goal is not the timer itself, but the way you create it. English is not my first language and I can’t explain it better than this.

If I was a teacher and asked 10 students to make a timer. If none copied, I would get 10 different codes and all doing the same(if I asked specifically what the timer should do). Since I know there are good programmers here, I was just asking to share the code of a common task so we could learn from each other.

Maybe you will use OOP, maybe you won’t. Since Lua is not a real OOP languageĀ if you use it, there are different ways of ā€œsimulatingā€ it. Seeing your implementation itself could be a learning experience.

Maybe you will use metatables or not. That’s not important. What’s important is why you used it the way you did.

@carloscostaĀ - My apologies for highjacking the thread. I apparently missed the point of creating a custom timer since that is not something I would generally do. Corona’s timer object works fine for all my needs.

-dev

@Dev, don’t apologie. Your post was very useful, even if it was not directly related to what I wanted in this thread. I also use Corona timer. Just created a version myself just to show my style in programming (well new style after some new reading), nothing more. I just wanted to learn the other programmer’s styles to compare and we all try to learn from it. I know my request requires some free time from other programmers to actually build a timer and show it here and maybe I’m asking tooĀ much. I don’t blame anyone if this thread will have 0 more custom timers…but hoping that will :wink:

@carloscota Thank you for your explanation. I hadn’t understand all that because it’s also not my first language.

I hadn’t understand it like you explain a few hours ago because timer work perfectly and seem very simple.

If I have to rewrite timer I won’t work like you did (and that’s why your thread is interesting)

To avoid a lot of enterframe event and optimize. I will code something like this(I will complete the code later):

local timerData={} local timerTime={} local timerInPause={} local actualTime=system.getTimer() local lastId=0 local function timerManager(event) local time = system.getTimer() while(time\>timerTime[1] and #timerTime\>=1) timerData.listener() table.remove(timerData,1) table.remove(timerTime,1) end end Runtime:addEventListener("enterFrame", timerManager) function newTimer(data) local absoluteTime=actualTime+data.delay lastId=lastId+1 data.id=lastId -- insert absoluteTime at the good place in the order list timerTime -- insert at the same place data in timerData -- example: -- timerTime={25565,25765,29565} -- timerData={data1,data2,data3} -- data={delay=25675,otherData} -- timerTime={25565,25675,25765,29565} -- timerData={data1,otherData,data2,data3} return data end function setPause(timerToPause) local timerId for key,value in ipairs(timerTime) do if(value==timerToPause.absoluteTime) then while(timerToPause.id!=timerData[key].id and key\<=lastId) do key=key+1 end timerId=key if(key==lastId and timerToPause.id!=timerData[key].id)then return false end break end end local timerDelay=timerToPause.absoluteTime-system.getTimer() timerToPause.delay=timerDelay timerToPause.pauseId=#timerInPause+1 table.insert(timerInPause,timerToPause) table.remove(timerData,timerId) &nbsp;&nbsp;&nbsp;&nbsp;table.remove(timerTime,timerId) end function resume(theTimer) local theTimerToResume=timerInPause[theTimer.pauseId] table.remove(timerInPause,theTimer.pauseId) newTimer(theTimerToResume) return theTimerToResume end ----------------------------------------------- -- to add a timer: MyTimer=newTimer({delay=100,listener=function()end}) setPause(MyTimer) local MyTimerBis=resume(MyTimer)

It’s made a long time ago I hadn’t code in lua but I still love it. Be tolerant

My code missing a lot of thing like manage timer of 0ms

This is not exactly what you are asking for, but similar enough I think it is worth sharing. I didn’t recreate a timer class/library, but rather ā€œpatchedā€ the built-in library to better suit my needs: https://github.com/schroederapps/corona-timer2

Another approach for the timer project (not very different from the first version I created):

local newTimer={} newTimer.mt = {\_\_index = {delay=1000, listener =function() end, interactions=1, elapsedTime=0, startTime=0, counter\_interaction=1, pausedTime=0} } function newTimer.new(oIn) local o=oIn or {} setmetatable(o, newTimer.mt) o.updateTime=function() local startTime=o.startTime local time = system.getTimer() o.elapsedTime=(time+o.pausedTime-startTime) if startTime and o.counter\_interaction and time+o.pausedTime \>= startTime+o.counter\_interaction\*o.delay then o.counter\_interaction=o.counter\_interaction+1 o.listener(o) if o.counter\_interaction\>o.interactions and o.interactions~=0 then newTimer.cancel(o) -- newTimer.removeSelf(o) -- uncoment this lines if you want to autoremove the timer after it finish. end end end newTimer.resume(o) return o end function newTimer.removeListener(self) if self then Runtime:removeEventListener("enterFrame", self.updateTime) end end function newTimer.pause(self) if self then self.pausedTime=system.getTimer() newTimer.removeListener(self) end end function newTimer.cancel(self) newTimer.removeListener(self) end function newTimer.removeSelf(self) local s=self if s then if s.updateTime then newTimer.removeListener(s) end for n,m in pairs (s) do s[m]=nil s[n]=nil end s.listener=nil s=nil end return nil end function newTimer.resume(self) if self then self.startTime=system.getTimer() Runtime:addEventListener("enterFrame", self.updateTime) end end ------------------------------------------------------------------------------ code examples ----------------------------- local timer1 local delay=500 local interactions=5 local counter=1 local stop\_variable=3 local function showTime() print("Will stop in: "..interactions-counter) if counter==stop\_variable then print ("simulating changing scene without canceling the timer -- even if the timer is still running, removing the timer will not provide an error. in corona timer would give an error because timer was not paused before it was deleted.") print ("showing that timer is not empty before i call the removeSelf()") print ("\_\_\_\_\_\_\_") if timer1 then for n,m in pairs (timer1) do print (n,m) end end print ("\_\_\_\_\_\_\_") newTimer.removeSelf(timer1) print ("check if timer is empty after is removed from memory") print ("\_\_\_\_\_\_\_") if timer1 then for n,m in pairs (timer1) do print (n,m) end end timer=nil print ("\_\_\_\_\_\_\_") end counter=counter+1 end timer1=newTimer.new({delay=delay, listener=showTime, interactions=interactions})

@carloscosta Ā (Responding mostly to your pre-edit comments.) I brought these up as some notable ā€œspecial caseā€ timers, say for some extra variants likeĀ  newTimer.performWithDelayAndMonitorObject and newTimer.wrapCoroutineAndPerformWithDelay. The second one is only really useful if it can fire more than once, of course. I definitely wasn’t recommending any more esoteric internals for the ā€œbasicā€ timer.Ā  :slight_smile:

Another special case I just remembered is letting the timer fire once immediately. Think clicking on a button that scrolls a window or increments a number, then does so again at intervals. ( newTimer.performOnceThenWithDelay?) Usually you can just first call the function you’ll pass as the body but it gets a little weird if you need to useĀ  counter and friends. (Again, only makes sense with repeats.)