When you complete a level in a game, it sometimes shows your score in an animated fashion. Sometimes this involves the numbers “spinning” such that they count up from the
Achieving this effect in Corona involves some simple math. Let’s start with some initial setup:
local scoreText = display.newText( "000000", display.contentCenterX, 60, native.systemFont, 48 )
This code is very straightforward — we just create a text object, scoreText
, with an initial display of 000000, positioned in the
The first thing to understand is that Corona is a
Obviously, game scores can vary greatly — one game may have a maximum score of 100 while another will have a maximum score in the millions. Thus, we don’t want the counter to count up by a fixed amount like +1 on each frame update. If so, a score of 1,000,000 would take too far long to update — approximately 16667 seconds at 60 frames per second — while a score like 100 would finish far too quickly
A better solution is to determine how long the animation should run and then calculate the amount that it should increment on each frame update.
Determining the amount by which to increment the score can be done via a method called linear interpolation. This basically allows you to compute intermediate points between two values based on some criteria. Let’s add a function for this to our existing code:
local scoreText = display.newText( "000000", display.contentCenterX, 60, native.systemFont, 48 ) local function lerp( v0, v1, t ) return v0 + t * (v1 - v0) end
This lerp()
function is straightforward enough. We take the difference between the start and stop values v1 - v0
)t
) which we want to know the increment for v0
).
Now let’s add a incrementScore()
function which will do most of the actual work:
local scoreText = display.newText( "000000", display.contentCenterX, 60, native.systemFont, 48 ) local function lerp( v0, v1, t ) return v0 + t * (v1 - v0) end local function incrementScore( target, amount, duration, startValue ) local newScore = startValue or 0 local passes = (duration/1000) * display.fps local increment = lerp( 0, amount, 1/passes ) local count = 0 local function updateText() if ( count <= passes ) then newScore = newScore + increment target.text = string.format( "%06d", newScore ) count = count + 1 else Runtime:removeEventListener( "enterFrame", updateText ) target.text = string.format( "%06d", amount + (startValue or 0) ) end end Runtime:addEventListener( "enterFrame", updateText ) end
As you can see, this function accepts four parameters:
target
— The text object which should be updated (required).amount
— The amount by which to increment the score counter (required).duration
— The duration of the startValue
— An optional starting value for the score counter. This can be specified if you want to begin counting up from a value greater than 0, for instance if you’re adding to an existing score.Inside the function, we essentially take these steps:
We set a temporary value, newScore
, with a value of the startValue
parameter 0
if startValue
is
Next, we calculate the number of “passes” necessary to reach the final score value. This is determined by the specified duration, converted from milliseconds to seconds, multiplied by the app’s 4000
(4 seconds) and the app runs at 60 frames per second, that results in 4 * 60
240
passes.
Following this, we run the lerp()
function using the calculated values. This returns the amount by which to increment the counter on each pass, and we store that as the increment
variable.
In the next block, we utilize a basic counter variable (count
) and a nested updateText()
function. This function will run once per frame increment
value to the score each time. That updated value is then set as the text
property of the target display object (scoreText
) but not before it’s formatted using string.format() to appear as 6 total digits with leading zeros.
When the counting duration is complete, we stop the updateText()
function and then set the score counter to the final value, once again formatted to appear as 6 total digits with leading zeros.
The proper format (string.format()
) of the score counter value will depend on your particular scenario. If you’re only counting up to a maximum score of 100, you probably shouldn’t format it as 6 digits with "%03d"
(3 digits) would be better than "%06d"
. Also note that formatting is entirely optional and you don’t need to apply any formatting if it’s not desired.
For further exploration of string formatting, please see the Formatting String Values tutorial.
updateText()
function executing on each runtime frame (as noted above, this process is stopped on line 20 when the Running the counting process is simple — just call the incrementScore()
function with the required parameters, for example:
incrementScore( scoreText, 750800, 4000 )
This will make the scoreText
text object count from 000000 up to 750800 over the total timespan of 4 seconds.
Hopefully this tutorial gets you started with a classic “spinning score counter” feature for your game. With a little creativity, you can adjust this to fit nearly any scenario and any maximum score threshold!