Quick Links: Download Gideros Studio | Gideros Documentation | Gideros Development Center | Gideros community chat | DONATE
Timer pausing — Gideros Forum

Timer pausing

asakharovasakharov Member
edited May 2013 in Bugs and issues
No one is concerned that a particular timer can not be resumed?
Why does the API has methods Timer.pauseAll () - Timer.resumeAll (), has a method Timer:pause(), but method Timer:resume() is absent?

This leads, for example, that after the call pauseAll(), even newly created and starting timers are not running.
It would be desirable to have a mechanism for pause and resume of a particular timers.

Why do we need methods Timer:stop and Timer:reset? Now, if we can not call the resume(), the two methods are essentially the same...
«1

Comments

  • ar2rsawseenar2rsawseen Maintainer
    @asakharov if you stop a timer and then start it again, it actually resumes.
    You need to use Timer:reset() to reset the timer and start it from the beginning

    Likes: AniketK

    +1 -1 (+1 / -0 )Share on Facebook
  • I tried to do the stop and start. and the timer always starts anew
  • ar2rsawseenar2rsawseen Maintainer
    edited May 2013
    Here is an example where even after stopping, the counter resumes and stops at 100
    local t = Timer.new(100, 100)
     
    t:addEventListener(Event.TIMER, function(self, e)
    	print(self:getCurrentCount())
    end, t)
     
    stage:addEventListener(Event.MOUSE_DOWN, function()
    	if t:isRunning() then
    		t:stop()
    	else
    		t:start()
    	end
    end)
     
    t:start()
  • function stopTimer(timer)
    if (timer ~= nil and timer:isRunning() == true) then
    timer:stop()
    timer._suspended = true
    end
    end

    function isTimerSuspended(timer)
    return timer ~= nil and timer._suspended ~= nil and timer._suspended == true
    end

    function resumeTimer(timer)
    if (isTimerSuspended(timer) == true) then
    timer:start()
    timer._suspended = false
    end
    end


    Then i do stopTimer(self.MyTimer)
    and resumeTimer(self.MyTimer)
    And it starts from the beginning
  • ar2rsawseenar2rsawseen Maintainer
    Here is the same test, using your code:
    function stopTimer(timer)
     if (timer ~= nil and timer:isRunning() == true) then
     timer:stop()
     timer._suspended = true
     end
    end
     
    function isTimerSuspended(timer)
     return timer ~= nil and timer._suspended ~= nil and timer._suspended == true
    end
     
    function resumeTimer(timer)
     if (isTimerSuspended(timer) == true) then
     timer:start()
     timer._suspended = false
     end
    end
     
    local t = Timer.new(100, 100)
    t:addEventListener(Event.TIMER, function(self, e)
    	print(self:getCurrentCount())
    end, t)
     
    stage:addEventListener(Event.MOUSE_DOWN, function()
    	if t:isRunning() then
    		stopTimer(t)
    	else
    		resumeTimer(t)
    	end
    end)
     
    t:start()
    Timer resumes and stops at 100
  • Here is my test:

    local t = Timer.new(5000)

    function test()
    print("hello, world")
    end

    t:addEventListener(Event.TIMER, test)

    stage:addEventListener(Event.MOUSE_DOWN, function()
    if t:isRunning() then
    t:stop()
    print("stoped")
    else
    t:start()
    print("started")
    end
    end)

    t:start()


    Stop timer every 4 seconds, and you never see "hello, world" print
  • ar2rsawseenar2rsawseen Maintainer
    Ok I see what you mean, then no you can't pause the Timer between the ticks and resume it that way. Even Timer:pauseAll does not do this.
    The timer will start from the beginning of the tick.
  • asakharovasakharov Member
    edited May 2013
    No, puaseAll do this. Check this

    local t = Timer.new(5000)

    function test()
    print("hello, world")
    end

    t:addEventListener(Event.TIMER, test)
    local paused = false
    stage:addEventListener(Event.MOUSE_DOWN, function()
    if paused == false then
    Timer.pauseAll()
    print("stoped")
    paused = true
    else
    Timer.resumeAll()
    print("started")
    paused = false
    end
    end)

    t:start()
  • ar2rsawseenar2rsawseen Maintainer
    Ok let me check with @atilim
    maybe no one just requested the feature before
  • Thanks, I will wait for the result.
    And what about this "after the call pauseAll(), even newly created and starting timers are not running."
    This is expected behavior or not? As for me, I did not expect this.
  • ar2rsawseenar2rsawseen Maintainer
    @asakharov basically it sets some kind of global variable, which each timer to check if it is paused or not. Well and All applies to All timers, even newly created, I think it is an expected behavior, as long as they resume running when you resumeAll timers
  • As a programmer, I can understand that it is easier to make a global variable timer-blocker than do the array of existing timers and resume his alone.
    But as the user of API, I wonder why if I were in the one scene, clicked on pause (called pauseAll), and then went to another menu scene... And then I see that at this scene all created timers are stopped.. I find it illogical. I create a new timer - so I know what I'm doing and it should work...
    Of course, to fix it - I can just add resumeAll to the constructor of scene base class.

    Imagine that I have put the game on pause. And then in the pause menu'm trying to use a timer. It will not work ...
  • ar2rsawseenar2rsawseen Maintainer
    Well the main purpose is as you said, to put the game on pause, that is why none of Timers should run, even new one.
    But about scenes, well there is no way for timer to know if the scene has changed, in the end scene is just a sprite object added and removed from stage and does not differ from any other sprite object.
  • "Imagine that I have put the game on pause. And then in the pause menu'm trying to use a timer. It will not work ..."
    You can come up with different example when you want to pause all timers except one. or start a new timer after a rest stop.
    In any case - is uncomfortable constraint.
  • ar2rsawseenar2rsawseen Maintainer
    edited May 2013
    Actually I would suggest using Timers as less as possible, because, at least in my experience, it is much faster and more efficient to use enterframe event.

    And well, I think that if it is stated pauseAll, then it means to pause all, even new ones.

    In case when you will want a specific Timer to run, you would have to stop all others one by one.
  • In my game one object has 4 timers. At the same time, may be a dozen objects on the screen. Do you think that the use by 4 checks in each of dozen onEnterFrame better than the timer every 10 seconds?

    If I had the opportunity to pause timers, one by one, I would have done. Stop them - does not suit me.
  • I understand that I can write this code in a different way.
    But if I need to call a function every 5 seconds .. what could be better than a timer? Why should I do special counters and additional conditions in onEnterFrame.
    And I do not understand what is wrong can happen if add resume and pause methods to timer.
  • @ar2rsawseen, Do you think that there are no problems with the timers?
  • ar2rsawseenar2rsawseen Maintainer
    I can't remember the case, I just remember that by rewriting one app from Timers to enterframe I got a good performance improvement on older mobile phones.

    If you don't experience anything, than most probably it is ok, maybe even something was updated in Gideros to make them work better.

    About pause/resume as I said, I'll discuss it with @atilim ;)
  • asakharovasakharov Member
    edited May 2013
    If onEnterFram better then Timer and everybody knows about it, then why do we still have no helpers for the timers in onenterframe
    something like this
    function MySprite:init()
      self.Timer = Timer2.new()
      self.Timer:addListener(MySprite.onCreateEnemy, self, 5000)
      self.Timer:addListener(MySprite.onCreateHelper, self, 10000)
      ...
    end
     
    function MySprite:onEnterFrame()
      self.Timer:increment()
      .....
    end
     
    ----------------------
     
    function Timer:init(callback, object, delay)
    ...
     
    function Timer:increment()
      ++self.Counter
      foreach iter in self.Listeners do
         if (self.Couter % iter == 0) then
           iter.Callback(iter.Object)
         end
      end
    end
  • My version of the onEnterFrameBased timer:
    --[[
    	Timer based on onEnterFrame event
    ]]
     
    FrameTimer = Core.class()
     
    function FrameTimer:init(callback, object, delay, repeatCount)
    	self.Callback = callback
    	self.Object = object
    	self.Delay = delay
    	self.RepeatCount = repeatCount ~= nil and repeatCount or 0
     
    	self.Paused = false
    	self:reset()
    end
     
    function FrameTimer:start()
    	self.Stopped = false
    end
     
    function FrameTimer:stop()
    	self.Stopped = true
    	self.Counter = 0
    end
     
    function FrameTimer:pause()
    	self.Paused = true
    end
     
    function FrameTimer:resume()
    	self.Paused = false
    end
     
    function FrameTimer:reset()
    	self.Counter = 0
    	self.HitCounter = 0
    	self.Stopped = true
    end
     
    function FrameTimer:setDelay(value)
    	self.Delay = value
    end
     
    function FrameTimer:increment()
    	if ((self.Paused == true) or (self.Stopped == true)) then return end
     
    	self.Counter = self.Counter + 1
    	if ((self.Counter % self.Delay) == 0) then
    		self.Callback(self.Object)
    		self.HitCounter = self.HitCounter + 1
    		if (self.HitCounter ~= nil and self.HitCounter == self.RepeatCount) then
    			self:stop()
    		end
    	end
    end
     
    --------------- Timers group controller ---------------
     
    TimersController = Core.class()
     
    function TimersController:init()
    	self.Timers = {}
    end
     
    function TimersController:convertToFpsTicks(delay)
    	return delay / 1000 * application:getFps()
    end
     
    function TimersController:addTimer(name, callback, object, delay, repeatCount)
    	self.Timers[name] = FrameTimer.new(callback, object, self:convertToFpsTicks(delay), repeatCount)
    end
     
    function TimersController:hasTimer(name)
    	return self.Timers[name] ~= nil
    end
     
    function TimersController:enumerate(functor)
    	for key, value in pairs(self.Timers) do
    		functor(value)
    	end
    end
     
    function TimersController:increment()
    	self:enumerate(FrameTimer.increment)
    end
     
    function TimersController:pause(name)
    	if (self:hasTimer(name) == true) then
    		self.Timers[name]:pause()
    	end
    end
     
    function TimersController:resume(name)
    	if (self:hasTimer(name) == true) then
    		self.Timers[name]:resume()
    	end
    end
     
    function TimersController:start(name)
    	if (self:hasTimer(name) == true) then
    		self.Timers[name]:start()
    	end
    end
     
    function TimersController:stop(name)
    	if (self:hasTimer(name) == true) then
    		self.Timers[name]:stop()
    	end
    end
     
    function TimersController:reset(name)
    	if (self:hasTimer(name) == true) then
    		self.Timers[name]:reset()
    	end
    end
     
    function TimersController:setDelay(name, value)
    	if (self:hasTimer(name) == true) then
    		self.Timers[name]:setDelay(self:convertToFpsTicks(value))
    	end
    end
     
    function TimersController:pauseAll()
    	self:enumerate(FrameTimer.pause)
    end
     
    function TimersController:resumeAll()
    	self:enumerate(FrameTimer.resume)
    end
     
    function TimersController:startAll()
    	self:enumerate(FrameTimer.start)
    end
     
    function TimersController:stopAll()
    	self:enumerate(FrameTimer.start)
    end
     
    function TimersController:resetAll()
    	self:enumerate(FrameTimer.reset)
    end
  • And the example of its use
    --[[
    	Base game object.
    ]]
     
    GameObject = Core.class(Sprite)
     
    function GameObject:init()
    	self.Paused = false
    	self.Timers = TimersController.new()
    	self:addEventListener(Event.ENTER_FRAME, self.onTimer, self)
    end
     
    ----------------- Events ------------------
     
    function GameObject:onTimer()
    	self.Timers:increment()
    end
     
    ------------------ Stubs ------------------
     
    function GameObject:getTypeName()
    	TraceAndThrow("Undefind class type name. Please declare name in appropriate game object.")
    end
     
    -------------- Pause control --------------
     
    function GameObject:pause()
    	if (self.Paused == false and self.onEnterFrame ~= nil) then
    		self:removeEventListener(Event.ENTER_FRAME, self.onEnterFrame, self)
    	end
     
    	self.Timers:pauseAll()	
    	self.Paused = true
    end
     
    function GameObject:resume()
    	if (self.Paused == true) then
    		self:addEventListener(Event.ENTER_FRAME, self.onEnterFrame, self)
    	end
     
    	self.Timers:resumeAll()
    	self.Paused = false
    end
     
    function GameObject:isPaused()
    	return self.Paused
    end
     
    ----------------- Traces -----------------
     
    function GameObject:tracePosition()
    	TraceDebug("[" .. self:getTypeName() .. "] - " .. "My position is: x = " .. math.round(self:getX()) .. "; y = " .. math.round(self:getY()) .. ";")
    end
     
    function GameObject:trace(message)
    	TraceDebug("[" .. self:getTypeName() .. "] - " .. message)
    end
  • But still I would like to be able to pause a specific timer
  • I agree with asakharov. The behavior for pausing and resuming a specific timer should be consistent with the behavior of pausing and resuming all timers.

    If I have a power up that lasts for 8 seconds and I pause and resume at 7 seconds using Timer:pauseAll() and Timer:resumeAll() it will behave properly. When it resumes the power up will last 1 more second and then end.

    However, if I pause and resume that individual power up timer using Timer:stop() and Timer:start() it will run for another 8 seconds when resumed. If I pause and resume it every 7 seconds it will run indefinitely.

    I'm using literally hundreds of timers for sprite animations and have never seen a performance hit. For the same reason, I don't always want to pauseAll but rather pause individual timers. Is there any chance of getting this fixed?

    Likes: asakharov

    +1 -1 (+1 / -0 )Share on Facebook
  • It seems that there is no chance. @ar2rsawseen promised to ask the @atilim more then 2 weeks ago. But, apparently, was not asked. :(
    For myself, I developed new Timer based on OnEnterFrame event. Productivity gains have not noticed, but I reached my goal with constistent pausing and resuming.
  • ar2rsawseenar2rsawseen Maintainer
    Actually I've delivered the message, and @atilim said he'd look at it
  • atilimatilim Maintainer
    yes we've discussed with @ar2rsawseen and we'll add Timer:pause with the next version. Then Timer:start can be used for both starting stopped or paused timer.

    Likes: Platypus

    +1 -1 (+1 / -0 )Share on Facebook
  • @atilim, @ar2rsawseen, thank you! Our life is a little bit simplified

    Likes: Platypus

    +1 -1 (+1 / -0 )Share on Facebook
  • amaximovamaximov Member
    edited September 2013
    Any update on native pause/resuming of timers?

    Here is something kind of sketchy I came up with today. Its a class that extends the regular timer class but adds the pause functionality. Might be hard to understand reasons for some of the code but I found a small bug regarding manipulating a Timer object within its own event listener ( Maybe something to do with threads?)

    The class also has a feature you can specify with an additional 3rd table with extra keys.

    Current supported feature is a new event called Event.TIMER_INFO which can be enabled via "enterFrameUpdates" key being true in the optional table.

    Example code:
     
    local myTimer = Timer.new(2000, 0, {enterFrameUpdates = true})
    myTimer:addEventListener(Event.TIMER_INFO, function(event)
    print(event.timeLeft)
    print(event.percentLeft)
    -- May give wrong values if you change the timer delay.
    --Gives negative values for timers that loop(Bug). Will work on this soon.
    end)
    myTimer:start()
    Anyways here is the code and I'll update on this thread when I manage to fix some bugs.

    Excuse any terrible writing above, I'm very tired :)
    lua
    lua
    GTimer.lua
    5K
  • amaximovamaximov Member
    edited September 2013
    Quick update before sleep tonight:

    Fixed (I think!) bug where Event.TIMER_INFO event get negative values for looping timers. Also fixed issue with changing the timer delay. Current implementation lets the current "run" with old delay to finish, and then sets the new delay. I plan on making the setDelay function simply extend the time, but I will need to experiment how the native Gideros function works.

    Question to Gideros Team:

    Does calling Timer:setDelay(theDelay) stop and start the timer? How does this behave? For example does it add on time as I intend to do with my custom library or does it work another way?
    lua
    lua
    GTimer.lua
    5K
Sign In or Register to comment.