Using Timer to show seconds.milliseconds

So I am trying to display a timer in my game.  I was able to figure that out but I wanted more precise times than just full seconds.  For example instead of 9 seconds, 9.112 seconds.  I was able to figure out how to do it with 1/10 of a second but when I try 1/100 or 1/1000 it doesn’t work, the timing is off.  I would like to have 1/1000 of a second to  work.  Below is my code with 1/10 of a second that seems to work fine.  Thanks for the help!

    local txt_counter = display.newText( number, 0, 0, native.systemFont, 50 )

    function fn_counter()

        number = number + .1

        txt_counter.text = number 

    end

    txt_counter.x = 150

    txt_counter.y = 288

    txt_counter:setTextColor( 255, 255, 255 )

    level01Timer = timer.performWithDelay(100, fn_counter, 0)

    group:insert( txt_counter )

I would think that you would have issues trying to get milliseconds in this way.  

My understanding of timer.preformWithDelay is that if the delay timer is less than the time between frames, then it will actually wait until the next frame to call the function (not sure if I’ve worded that simply enough).  

If your game is running at 60fps, each frame is approx 16ms. If you want to set your timer to every 1/100 of a second, that would be 10ms. So if you were to just add .01 to your timer text, it would quickly be out of sync.  

I would use the enterFrame function instead. Your timer won’t go up in single milliseconds (it will increase by however many ms have passed in each frame), but nobody would be able to read that fast anyway:

local prevFrameTime, currentFrameTime --both nil local deltaFrameTime = 0 local totalTime = 0 local txt\_counter = display.newText( totalTime, 0, 0, native.systemFont, 50 ) txt\_counter.x = 150 txt\_counter.y = 288 txt\_counter:setTextColor( 255, 255, 255 ) group:insert( txt\_counter )

local function enterFrame(e)     local currentFrameTime = system.getTimer()     --if this is still nil, then it is the first frame     --so no need to perform calculation     if prevFrameTime then         --calculate how many milliseconds since last frame         deltaFrameTime = currentFrameTime - prevFrameTime     end     prevFrameTime = currentFrameTime     --this is the total time in milliseconds     totalTime = totalTime + deltaFrameTime     --multiply by 0.001 to get time in seconds     txt\_counter.text = totalTime \* 0.001 end  

Thanks for the reply!  If I am using storyboard where exactly do I put this enterFrame function because it doesn’t seem to work.  I put it in create scene.

enterFrame is a function needs to be called every frame (hence the name).  

I usually declare it outside of my storyboard scenes, and then add a runtime listener in my enterScene function, and remove it in my exitScene, but obviously you’d need to do whatever suits your needs.

Here’s a rough guide to how I do it:

local prevFrameTime, currentFrameTime --both nil local deltaFrameTime = 0 local totalTime = 0 --forward declare this variable outside of the functions --so that multiple functions can access it local txt\_counter --this is the enterFrame function, which will be added --as an event listener to the Runtime object function scene:enterFrame(event) local currentFrameTime = system.getTimer() --if this is still nil, then it is the first frame --so no need to perform calculation if prevFrameTime then --calculate how many milliseconds since last frame deltaFrameTime = currentFrameTime - prevFrameTime end prevFrameTime = currentFrameTime --this is the total time in milliseconds totalTime = totalTime + deltaFrameTime --multiply by 0.001 to get time in seconds txt\_counter.text = totalTime \* 0.001 end function scene:createScene(event) --DON'T put the word local before creating the text object, --otherwise enterFrame function will not be able to access it txt\_counter = display.newText( totalTime, 0, 0, native.systemFont, 50 ) txt\_counter.x = 150 txt\_counter.y = 288 txt\_counter:setTextColor( 255, 255, 255 ) group:insert( txt\_counter ) end function enterScene(event) Runtime:addEventListener("enterFrame", scene) end function exitScene(event) Runtime:removeEventListener("enterFrame", scene) end

Thanks I figured it out, shouldn’t the Runtime:addEventListener(“enterFrame”, scene) be this though Runtime:addEventListener(“enterFrame”, enterFrame)?

Not in my example, because enterFrame is a function that belongs to the scene object:

function scene:enterFrame(event) --do stuff end

If I had defined it like this:

local function enterFrame(event) --do stuff end

then your way would be correct.

Glad to hear you’ve got it all working though.

yup I just saw that you changed your previous code thanks for the help, does it matter if it should be scene:enterFrame or just enterFrame?

Doesn’t make any difference really, if it’s easier for you then I would just use enterFrame.

I have one more question for you, is there a way to make it so that it doesn’t look like the timer is moving.  It would be nice if it would not blink so much if you know what I mean?

Do you mean the text is kind of fidgeting left/right?  

If so this is caused by the reference points not being reset when the text property is changed. To fix it just add this to the end of your enterFrame function:

function enterFrame(e) --do your stuff as before txt\_counter.text = totalTime \* 0.001 --then --if using Corona build 2076 or later txt\_counter.anchorX, txt\_counter.anchorY = 0.5, 0.5 --for earlier builds use txt\_counter:setReferencePoint(display.CenterReferencePoint) --and then set the x and y position again, to the same values you originally used txt\_counter.x = 150 txt\_counter.y = 288

You will possibly need to adjust the anchor / setReferencePoint values based on how you have originally setup your text object (i.e. if it currently has a TopLeft ref point then use that instead of Center).

This works perfect thanks again for all of your help!

So I am coming across a weird problem when I build for an actual android device.  The timer does some weird stuff at the beginning, like adding .0000000000000001 and flashes a few times and then it is showing 6 decimal places instead of 3 like it was doing for the simulator. It is also adding 9999999 after the 6 decimal places, very strange. Any idea what is going on here?  Everything worked perfectly in the simulator but now on a device it is wigging out.

Timer t = new Timer();
//Set the schedule function and rate
t.scheduleAtFixedRate(new TimerTask() {

public void run()
{
//Called each time when 1000 milliseconds (1 second) (the period parameter)
runOnUiThread(new Runnable() {

public void run()
{
TextView tv = (TextView) findViewById(R.id.timer);
tv.setText(String.valueOf(time));
time += 1;

}

});
}

},
//Set how long before to start calling the TimerTask (in milliseconds)
0,
//Set the amount of time between each execution (in milliseconds)
1000);

Thanks
Jeff Jones
Lead at Mobile Application Development Company
 

Where exactly does this code go if I am using storyboard?  This doesn’t look like lua code.

It’s not Lua it’s Obj-C, so not very useful unless you’re using Enterprise, which I’m guessing you’re not.  

To answer your question using Lua:

I think it’s simply floating point rounding which is giving you these extra decimal places. Try using this function to round to X number of decimal places:

--rounds input number n to X decimal places local function roundDec(n, X) local d = 10 ^ X return math.round(n \* d) / d end function enterFrame(e) txt\_counter.text = roundDec(totalTime \* 0.001, 3) end

Ya didn’t think it was lua!  Thanks again for your help it works great on my android device now.  I will be building for IOS this weekend and we will see if there are any other problems I am having with it.  

I would think that you would have issues trying to get milliseconds in this way.  

My understanding of timer.preformWithDelay is that if the delay timer is less than the time between frames, then it will actually wait until the next frame to call the function (not sure if I’ve worded that simply enough).  

If your game is running at 60fps, each frame is approx 16ms. If you want to set your timer to every 1/100 of a second, that would be 10ms. So if you were to just add .01 to your timer text, it would quickly be out of sync.  

I would use the enterFrame function instead. Your timer won’t go up in single milliseconds (it will increase by however many ms have passed in each frame), but nobody would be able to read that fast anyway:

local prevFrameTime, currentFrameTime --both nil local deltaFrameTime = 0 local totalTime = 0 local txt\_counter = display.newText( totalTime, 0, 0, native.systemFont, 50 ) txt\_counter.x = 150 txt\_counter.y = 288 txt\_counter:setTextColor( 255, 255, 255 ) group:insert( txt\_counter )

local function enterFrame(e)     local currentFrameTime = system.getTimer()     --if this is still nil, then it is the first frame     --so no need to perform calculation     if prevFrameTime then         --calculate how many milliseconds since last frame         deltaFrameTime = currentFrameTime - prevFrameTime     end     prevFrameTime = currentFrameTime     --this is the total time in milliseconds     totalTime = totalTime + deltaFrameTime     --multiply by 0.001 to get time in seconds     txt\_counter.text = totalTime \* 0.001 end  

Thanks for the reply!  If I am using storyboard where exactly do I put this enterFrame function because it doesn’t seem to work.  I put it in create scene.

enterFrame is a function needs to be called every frame (hence the name).  

I usually declare it outside of my storyboard scenes, and then add a runtime listener in my enterScene function, and remove it in my exitScene, but obviously you’d need to do whatever suits your needs.

Here’s a rough guide to how I do it:

local prevFrameTime, currentFrameTime --both nil local deltaFrameTime = 0 local totalTime = 0 --forward declare this variable outside of the functions --so that multiple functions can access it local txt\_counter --this is the enterFrame function, which will be added --as an event listener to the Runtime object function scene:enterFrame(event) local currentFrameTime = system.getTimer() --if this is still nil, then it is the first frame --so no need to perform calculation if prevFrameTime then --calculate how many milliseconds since last frame deltaFrameTime = currentFrameTime - prevFrameTime end prevFrameTime = currentFrameTime --this is the total time in milliseconds totalTime = totalTime + deltaFrameTime --multiply by 0.001 to get time in seconds txt\_counter.text = totalTime \* 0.001 end function scene:createScene(event) --DON'T put the word local before creating the text object, --otherwise enterFrame function will not be able to access it txt\_counter = display.newText( totalTime, 0, 0, native.systemFont, 50 ) txt\_counter.x = 150 txt\_counter.y = 288 txt\_counter:setTextColor( 255, 255, 255 ) group:insert( txt\_counter ) end function enterScene(event) Runtime:addEventListener("enterFrame", scene) end function exitScene(event) Runtime:removeEventListener("enterFrame", scene) end

Thanks I figured it out, shouldn’t the Runtime:addEventListener(“enterFrame”, scene) be this though Runtime:addEventListener(“enterFrame”, enterFrame)?

Not in my example, because enterFrame is a function that belongs to the scene object:

function scene:enterFrame(event) --do stuff end

If I had defined it like this:

local function enterFrame(event) --do stuff end

then your way would be correct.

Glad to hear you’ve got it all working though.