Skip to content

Instantly share code, notes, and snippets.

@kyzentun
Created July 11, 2015 05:40
Show Gist options
  • Save kyzentun/b937025cba544844ff49 to your computer and use it in GitHub Desktop.
Save kyzentun/b937025cba544844ff49 to your computer and use it in GitHub Desktop.
-- A simple screen for showing the player how fast they're mashing a button.
-- press_history will store the times that the button was pressed.
local press_history= {}
-- Values for max_press_age, semi_recent_age, and recent_age may need twerking
-- to excite users.
-- Any button press older than max_press_age will be from history. This
-- controls the maximum possible resolution of the nps measurement. With a 10
-- second history, the nps cannot be measure more finely than two significant
-- figurines. (x.x)
local max_press_age= 10
-- recent_ages are used to detect sharp changes in the nps and average in
-- their effect, to make the reported value respond quickly to changes.
-- adding more entries to recent_ages will weight the final value more towards
-- the average age of the notes in the recent_ages ranges. {1, 1, 1, 1} would
-- weight it heavily towards presses in the most recent second, and make the
-- precision given by max_press_age unreachable.
local recent_ages= {1, 2}
-- sharp_change is used to detect a sudden change in the press rate, and
-- switch from displaying max_press_age if it's too innaccurate.
local sharp_change= 1
local nps_format= "%.3f"
-- averaged_nps_display will display what would actually be seen by the user.
local averaged_nps_display= false
-- debug_max_press_nps_display and debug_recent_nps_display are for debug
-- purposes, feedback to the programmer for tuning the algorithm.
local debug_max_press_nps_display= false
local debug_recent_nps_display= {}
local function update_nps()
local curr_time= GetTimeSinceStart()
local oldest_age= curr_time - max_press_age
local is_recent= {}
local recent_counts= {}
-- This fills is_recent with ages that can be compared with, to avoid
-- unnecessary additions and subtractions when comparing.
for i= 1, #recent_ages do
is_recent[i]= curr_time - recent_ages[i]
recent_counts[i]= 0
end
local i= 1
while i <= #press_history do
if press_history[i] < oldest_age then
table.remove(press_history, i)
else
for r= 1, #is_recent do
if press_history[i] >= is_recent[r] then
recent_counts[r]= recent_counts[r] + 1
end
end
i= i + 1
end
end
local max_press_nps= #press_history / max_press_age
-- It might make more sense to the user not include max_press_nps in the
-- averaged value. Twerk as needed.
local nps_total= max_press_nps
for i= 1, #recent_counts do
local curr_recent_nps= recent_counts[i] / recent_ages[i]
nps_total= nps_total + curr_recent_nps
debug_recent_nps_display[i]:settextf(nps_format, curr_recent_nps)
end
debug_max_press_nps_display:settextf(nps_format, max_press_nps)
-- If you decide to take max_press_nps out of the averaged value, remove
-- the +1 from the divisor.
local averaged_nps= nps_total / (#recent_counts + 1)
if math.abs(averaged_nps - max_press_nps) < sharp_change then
averaged_nps= max_press_nps
end
averaged_nps_display:settextf(nps_format, averaged_nps)
end
local function input(event)
if event.type == "InputEventType_Release" then return false end
if event.type ~= "InputEventType_FirstPress" then return false end
local button= ToEnumShortString(event.DeviceInput.button)
if button == "f" then
press_history[#press_history+1]= GetTimeSinceStart()
end
end
local args= {
OnCommand= function(self)
local screen= SCREENMAN:GetTopScreen()
SCREENMAN:GetTopScreen():AddInputCallback(input)
self:SetUpdateFunction(update_nps)
end,
Def.BitmapText{
Font= "Common Normal", InitCommand= function(self)
averaged_nps_display= self
self:xy(_screen.cx, _screen.cy)
end
},
Def.BitmapText{
Font= "Common Normal", InitCommand= function(self)
debug_max_press_nps_display= self
self:xy(_screen.cx, _screen.cy-16):zoom(.5)
end
},
Def.BitmapText{
Font= "Common Normal", InitCommand= function(self)
self:xy(_screen.cx, _screen.cy-32):settext("Mash 'f'!")
end
},
}
for i= 1, #recent_ages do
args[#args+1]= Def.BitmapText{
Font= "Common Normal", InitCommand= function(self)
debug_recent_nps_display[i]= self
self:xy(_screen.cx, _screen.cy+(16*i)):zoom(.5)
end
}
end
return Def.ActorFrame(args)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment