I'd like to find the average input value from a controller over the last n seconds (say the last 0.1 seconds) and do this every frame.
I can see how to do this for the past n frames by storing a list of values for the last n frames and averaging that total every frame, but I want to account for a variable frame rate as the resulting output is driving a character controller and should feel precise and consistent. I tried storing a list of value pairs containing the input value and delta time value for every frame, then counting back through the list of delta time values until they sum to n seconds, then using the total of those input values to work out the average, but it seems a bit clunky and means I have to guess how big the list should be to store enough values, is there a better way?
Answer
Two options:
1. Arithmetic average (worse)
Have a 2-column table that has sufficiently many rows to be able to store all values for your chosen time (0.1 s in your question) at the potentially highest possible, supported frame rate - say 300 fps (unless you limit it to for example 60). 300 fps would mean 30 rows, in order to store all values for the last 0.1 seconds. During each frame, (over)write the oldest one row in the table, in a circular fashion; ie. have an index counter that wraps back to 1 after 30 (or whatever the number of rows is).
Each row holds a time stamp (a time, not a delta time!) and the controller value at that time stamp.
When calc'in the average (sum of values / number of values), per frame apparently, use only rows with a timestamp that is greater than Tnow - 0.1. Ignore other rows, but if all rows are to be ignored, ensure you use at least the most recent row.
Weakness: Potentially much data and calculation, result not necessarily better than [below].
2. Quasi-average (better)
NewValue = [read from controller]
NewShare = min(1, FrameDeltaTime / 0.1)
OldShare = 1 - NewShare
ThisValue = (OldValue * OldShare) + (NewValue * NewShare)
OldValue = ThisValue // Use ThisValue for whatever needed
This gives a nice "rubber band affect", removes some stutter from unstable controllers etc., and above all, it is a minor calculation that produces an equally good result as in /1/ above; additionally itäs easy to trim the 0.1 s into whatever is the good value, by trial and error.
- if FrameDeltaTime is 0.0001, it takes 0.999 of the old and 0.001 of the new
- if FrameDeltaTime is 0.0167 (60 fps), it takes 0.83 of the old and 0.167 of the new. Doing that repeatedly "seeks" towards whatever the controller outputs.
- if FrameDeltaTime is 0.1 it takes 0 of the old and 1 of the new (ie. 0.1 s is the "break point")
- if FrameDeltaTime is 5 (eg. a long blocking load delay) it takes 0 of the old and 1 of the new
No comments:
Post a Comment