Hi, so I have a currency that I want to make a per second stat with. When you are getting the currency there is a per second measurement next to it which tells you how much you are getting per second. So for example, in a game with combat you might want to have a DPS counter at the side to see how much damage you’re doing per second. How would I achieve this?
You can’t calculate an instantaneous income per second / income per second at a point in time, because computers work discretely as opposed to continuously (see ^1). So unless income is based on some “rate” variable, e.g. money += income_rate * dt
, you’ll have to do an average over some period of time.
You probably don’t want to average over the entire time the player has played the game, unless you want an “average income across entire playthrough” stat. A time period on the order of a a seconds to a minute is more appropriate. This kind of average where you don’t take all the data, but only e.g. the latest 5 seconds of data, is called a “moving average” or “rolling average”.
Since you know how much money a player has at different times, your data points consists of pairs of (time, money). So if you’re storing values every second and want a 5-second rolling average, the first 7 data points might look like
time | money |
---|---|
0 | 100 |
1 | 20 |
2 | 25 |
3 | 30 |
4 | 35 |
5 | 40 |
6 | 20 |
The 5-second rolling average over the latest data would be the change in money / change in time for those 5 sconds, so avg = (data[7].money - data[2].money) / (data[7].time - data[2].time)
.
In code, you could do like this:
function newRollingAverageTracker(fGetValue, frequency, numValues)
local data = table.create(numValues, { Value = 0, Time = 0 } )
local nextIndex = 1
task.spawn(function()
while true do
--Store value
data[nextIndex] = { Value = fGetValue(), Time = tick() }
-- Increment and wrap
nextIndex += 1
if nextIndex > numValues then
nextIndex = 1
end
wait(1/frequency)
end
end
return function()
local firstIndex = nextIndex
local lastIndex = nextIndex - 1
if lastIndex == 0 then lastIndex = numValues end
return (data[lastIndex].Value - data[firstIndex].Value) / (data[lastIndex].Time - data[firstIndex].Time)
end
end
This is a bit complicated, but IMO nicely self-contained. It expects a function as its first value, which should return the instantaneous value of whatever you want to track when it gets called. It returns a function as well, which will give you the rolling average of the last numValues
values whenever it’s called. Internally it uses a circular buffer^2. Here’s how it can be used:
local getMoneyPerSecond = newRollingAverageTracker(function() return playerData.Money; end, 1, 5)
local moneyPerSecond = 0
task.spawn(function()
while true do
moneyPerSecond = getMoneyPerSecond()
wait(1)
end
end)
[1] Instantaneous speed and velocity (video) | Khan Academy
[2] Circular buffer - Wikipedia