Instantly share code, notes, and snippets.
Created
July 11, 2015 05:40
-
Star
(0)
0
You must be signed in to star a gist -
Fork
(0)
0
You must be signed in to fork a gist
-
Save kyzentun/b937025cba544844ff49 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
-- 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