@ Rob In this case performance would take a backseat to correctness.
The big hang-up is being able to tell whether you’re inside a transition, timer, or garden-variety enterFrame listener. From what Walter says in the “Frame loop execution order” thread (which I’d missed!!), it sounds like timers and transitions fire, followed by any user-supplied enterFrame listeners. Presumably the latter dispatch in insertion order.
Adding an UpdateID() listener at startup is perfectly reasonable, which could for instance increment an ID each frame starting from 0. The trouble is that the timer and transition listeners are already in line ahead of us, so if we want them to respect the ID we need a split-personality getter:
local ID = -1 -- Master ID: which frame is this? function M.GetFrameID (before\_update) if before\_update then return ID + 1 else return ID end end Runtime:addEventListener("enterFrame", function() ID = ID + 1 end)
This is okay if we’ll only be querying the ID in top-level logic, where we know our circumstances, for instance from the body of a timer, but becomes less flexible when we need it, say, in a function called by that same timer, and would need to propagate this context. This is especially onerous if we’d like our routines to be shared freely among timers, transitions, and enterFrame listeners.
Adding a second enterFrame listener would let us do an “are we in enterFrame?” query, but we then need to take special care to keep all (ID-aware) listeners bookended by this and the ID updater, so it just trades one problem for another. (I guess one could monkey-patch Runtime:addEventListener() to always move this second listener to the end…)
Usage-wise, the idea would be for modules to keep their own copy of the ID and then compare it to the “master” value for consistency. On first request (if any) per frame, the local copy is detected as out of sync; the code in question will resync its local ID, and also do any necessary upkeep at the same time. The actual value of the ID is largely irrelevant; it merely serves to detect changes, so updates need not be every frame and arbitrarily many independent systems can be ID’d.
As a contrived example: In some strategy game every unit, when about to perform an action, calls some GetAvailableMoves() function. The first time this is called on a given frame, it does some hefty work, like performing a bunch of A* searches and such to calculate prospective paths. Further calls, however, can simply return this already-available result. This changes the code shape a bit, so that instead of, say:
-- stuff UpdateAvailableMoves() UpdateUnits() -- more stuff
we would have
-- stuff UpdateUnits() -- UpdateAvailableMoves() called on-demand -- more stuff
Often this is a bit of a wash, but can occasionally simplify a few things. For instance, if we don’t update any unit, we don’t need to add extra handling to not update the available moves. Or we might have different types of units needing different systems.
(All that said, I’m hardly losing sleep over this stuff. I more or less gave up these techniques after failing to pry execution order details out of Mr. Beebe… so it’s been a while. :))
As for how it might be used in the context of the original question:
local my\_id, up\_to local frame\_ms = math.floor(1000 / display.fps) -- might want to err low, say by scaling by .9 or so function TimeLeft () local current = system.getFrameID() local now = system.getTimer() if my\_id ~= current then -- out-of-date? my\_id = current up\_to = now + frame\_ms -- guess about when frame should end -- not so accurate if called later in frame, unfortunately end return math.max(up\_to - now, 0) -- alternatively, and with fewer gotchas: -- return math.max(system.getStartOfFrame() + frame\_ms - now, 0) end -- stuff while TimeLeft() \> 10 do -- still have decent slice left? DoStuff() end
Uff, and that was a lot more writing than I meant to do. 