What do you think about this work of code?

So what I wanted for you guys to give me an opinion on is this.

So in one of my games that’s in progress i have a score counter that works like this,

local score = 0 local scoreTxt = display.newText( "0", 0, 0, "NexaRust", 20 ) scoreTxt.x = centerX scoreTxt.y = centerY - actualH/2 + 40 sceneGroup:insert(scoreTxt) local function updateScore(event) score = score + 1 scoreTxt.text = string.format("%d", score ) end 

So to work my score i add a timer which makes my score go +1 every frame.

Like this.

local scoreTimer = timer.performWithDelay( 1, updateScore, -1 ) 

But every frame is just not enough for my game so i added another one like this.

local scoreTimerTwo = timer.performWithDelay( 1, updateScore, -1 ) 

 So now there are TWO timers that update the same score and its twice as fast and its perfect for my game. 

So my question is – Is this okay to do?

–SonicX278

You’d be better off using an enterFrame event paired with transition.to() like this:

local scoreTxt = display.newText( "0", 0, 0, "NexaRust", 20 ) scoreTxt.score = 0 function scoreTxt.enterFrame(self) -- Automatically remove this listener when the object is destroyed if( self.removeSelf == nil ) then Runtime:removeEventListener( "enterFrame", self ) return end self.text = math.round( self.score ) end Runtime:addEventListener( "enterFrame", scoreTxt ) function scoreTxt.setScore( self, newScore, updateTime ) transition.cancel( self ) transition.to( self, { score = newScore, time = updateTime } ) end

later…

scoreTxt:setScore( 100, 1000 )

Note.  I didn’t actually answer the question you asked, so…

Sure you can do that. but I wouldn’t say it is a good idea.  Now, you’ve got two timers to track and eventually cancel.  This is a lot of bookkeeping to solve a problem that can be solved in a cleaner way. 

PS - If you’re puzzling over that ‘auto  remove’ code in the enterFrame() function above, be aware that:

  • calling display.remove() or obj:removeSelf() destroys the Corona side of an object and leaves a stub table.
  • A stub will hang around till Lua can clean it up (garbage collection).  Thus, enterFrame() may keep getting called.
  • When you destroy an object, the stub no longer has a reference to the method removeSelf().  So, checking for it tells us if that object is valid or not.

You might find that adding to your score using a direct “+2 points per frame” causes problems because different devices will run your game at different speeds.  

If device A is running at 50fps then someone playing that game will get 100 points every second.  

If device B is running at 20fps then that user will only get 40 points per second.  

It might be better to track the delta time between frames, and use that to calculate how many points to add in an event listener.

So now there are TWO timers

So my question is – Is this okay to do?

Yes, but unnecessary - you could just add “+2” instead of “+1” in a single timer and accomplish the same thing more efficiently.

And echoing @Alan QuizTix, if this is a time-based score you might want to use real time rather than counting frames.

So do you think i should/could make it so the score updates every millisecond? With this code.

 local score = 0 local scoreTxt = display.newText( "0", 0, 0, "NexaRust", 20 ) scoreTxt.x = centerX scoreTxt.y = centerY - actualH/2 + 40 sceneGroup:insert(scoreTxt) local function updateScore(event) score = score + 2 scoreTxt.text = string.format("%d", score ) end local scoreTimer = timer.performWithDelay( 1, updateScore, -1 ) 

–SonicX278 

it still won’t fire more often than once per frame

pretend “timer.performWithDelay” was named “timer.performOnNextPossibleFrameAfterAtLeastThisManyMilliseconds”

get it?

You could use event.time to update your score, to account for the problems the others have mentioned. Assuming the multiplier will remain fixed (otherwise you’d want an incremental approach), it would look something like this:

 local score = 0 local scoreTxt = display.newText( "0", 0, 0, "NexaRust", 20 ) scoreTxt.x = centerX scoreTxt.y = centerY - actualH/2 + 40 sceneGroup:insert(scoreTxt) local Time0 = system.getTimer() -- set this at start of each level, round, etc. -- or get on first frame (event.count == 1 / Time0 == nil) local Res = 1000 / 30 -- milliseconds per frame (30 or 60 fps) local function updateScore(event) local updates = math.floor((event.time - Time0) / Res) score = updates \* 2 -- or whatever your multiplier is scoreTxt.text = string.format("%d", score ) end local scoreTimer = timer.performWithDelay( 1, updateScore, -1 )

You’d be better off using an enterFrame event paired with transition.to() like this:

local scoreTxt = display.newText( "0", 0, 0, "NexaRust", 20 ) scoreTxt.score = 0 function scoreTxt.enterFrame(self) -- Automatically remove this listener when the object is destroyed if( self.removeSelf == nil ) then Runtime:removeEventListener( "enterFrame", self ) return end self.text = math.round( self.score ) end Runtime:addEventListener( "enterFrame", scoreTxt ) function scoreTxt.setScore( self, newScore, updateTime ) transition.cancel( self ) transition.to( self, { score = newScore, time = updateTime } ) end

later…

scoreTxt:setScore( 100, 1000 )

Note.  I didn’t actually answer the question you asked, so…

Sure you can do that. but I wouldn’t say it is a good idea.  Now, you’ve got two timers to track and eventually cancel.  This is a lot of bookkeeping to solve a problem that can be solved in a cleaner way. 

PS - If you’re puzzling over that ‘auto  remove’ code in the enterFrame() function above, be aware that:

  • calling display.remove() or obj:removeSelf() destroys the Corona side of an object and leaves a stub table.
  • A stub will hang around till Lua can clean it up (garbage collection).  Thus, enterFrame() may keep getting called.
  • When you destroy an object, the stub no longer has a reference to the method removeSelf().  So, checking for it tells us if that object is valid or not.

You might find that adding to your score using a direct “+2 points per frame” causes problems because different devices will run your game at different speeds.  

If device A is running at 50fps then someone playing that game will get 100 points every second.  

If device B is running at 20fps then that user will only get 40 points per second.  

It might be better to track the delta time between frames, and use that to calculate how many points to add in an event listener.

So now there are TWO timers

So my question is – Is this okay to do?

Yes, but unnecessary - you could just add “+2” instead of “+1” in a single timer and accomplish the same thing more efficiently.

And echoing @Alan QuizTix, if this is a time-based score you might want to use real time rather than counting frames.

So do you think i should/could make it so the score updates every millisecond? With this code.

 local score = 0 local scoreTxt = display.newText( "0", 0, 0, "NexaRust", 20 ) scoreTxt.x = centerX scoreTxt.y = centerY - actualH/2 + 40 sceneGroup:insert(scoreTxt) local function updateScore(event) score = score + 2 scoreTxt.text = string.format("%d", score ) end local scoreTimer = timer.performWithDelay( 1, updateScore, -1 ) 

–SonicX278 

it still won’t fire more often than once per frame

pretend “timer.performWithDelay” was named “timer.performOnNextPossibleFrameAfterAtLeastThisManyMilliseconds”

get it?

You could use event.time to update your score, to account for the problems the others have mentioned. Assuming the multiplier will remain fixed (otherwise you’d want an incremental approach), it would look something like this:

 local score = 0 local scoreTxt = display.newText( "0", 0, 0, "NexaRust", 20 ) scoreTxt.x = centerX scoreTxt.y = centerY - actualH/2 + 40 sceneGroup:insert(scoreTxt) local Time0 = system.getTimer() -- set this at start of each level, round, etc. -- or get on first frame (event.count == 1 / Time0 == nil) local Res = 1000 / 30 -- milliseconds per frame (30 or 60 fps) local function updateScore(event) local updates = math.floor((event.time - Time0) / Res) score = updates \* 2 -- or whatever your multiplier is scoreTxt.text = string.format("%d", score ) end local scoreTimer = timer.performWithDelay( 1, updateScore, -1 )