123 lines
3.8 KiB
C#
123 lines
3.8 KiB
C#
|
namespace Fusion.Addons.KCC
|
|||
|
{
|
|||
|
using UnityEngine;
|
|||
|
|
|||
|
public sealed class FloatAccumulator
|
|||
|
{
|
|||
|
// PUBLIC MEMBERS
|
|||
|
|
|||
|
public float SmoothingWindow;
|
|||
|
public bool UseDirectionFilter;
|
|||
|
|
|||
|
public float AccumulatedValue => _accumulatedValue;
|
|||
|
|
|||
|
// PRIVATE MEMBERS
|
|||
|
|
|||
|
private SmoothFloat _smoothValues = new SmoothFloat(256);
|
|||
|
private float _accumulatedValue;
|
|||
|
private float _unprocessedValue;
|
|||
|
private float _unprocessedDeltaTime;
|
|||
|
private int _lastAccumulateFrame;
|
|||
|
private int _lastConsumeFrame;
|
|||
|
|
|||
|
// CONSTRUCTORS
|
|||
|
|
|||
|
public FloatAccumulator() : this(default, default) {}
|
|||
|
public FloatAccumulator(float smoothingWindow) : this(smoothingWindow, default) {}
|
|||
|
|
|||
|
public FloatAccumulator(float smoothingWindow, bool useDirectionFilter)
|
|||
|
{
|
|||
|
SmoothingWindow = smoothingWindow;
|
|||
|
UseDirectionFilter = useDirectionFilter;
|
|||
|
}
|
|||
|
|
|||
|
// PUBLIC METHODS
|
|||
|
|
|||
|
public void Accumulate(float value)
|
|||
|
{
|
|||
|
int currentFrame = Time.frameCount;
|
|||
|
if (currentFrame == _lastAccumulateFrame)
|
|||
|
return;
|
|||
|
|
|||
|
float unscaledDeltaTime = Time.unscaledDeltaTime;
|
|||
|
|
|||
|
// Calculate average value if the smoothing window is valid.
|
|||
|
if (SmoothingWindow > 0.0f)
|
|||
|
{
|
|||
|
// Clear all values in opposite direction for instant flip.
|
|||
|
if (UseDirectionFilter == true)
|
|||
|
{
|
|||
|
_smoothValues.FilterValues(value < 0.0f, value > 0.0f);
|
|||
|
}
|
|||
|
|
|||
|
// Add or update value for current frame.
|
|||
|
_smoothValues.AddValue(currentFrame, unscaledDeltaTime, value);
|
|||
|
|
|||
|
// Calculate smooth value.
|
|||
|
value = _smoothValues.CalculateSmoothValue(SmoothingWindow, unscaledDeltaTime);
|
|||
|
}
|
|||
|
|
|||
|
_accumulatedValue += value;
|
|||
|
|
|||
|
_unprocessedValue = value;
|
|||
|
_unprocessedDeltaTime = unscaledDeltaTime;
|
|||
|
_lastAccumulateFrame = currentFrame;
|
|||
|
}
|
|||
|
|
|||
|
public float Consume()
|
|||
|
{
|
|||
|
float consumeValue = _accumulatedValue;
|
|||
|
|
|||
|
_accumulatedValue = default;
|
|||
|
_unprocessedValue = default;
|
|||
|
_unprocessedDeltaTime = default;
|
|||
|
_lastConsumeFrame = Time.frameCount;
|
|||
|
|
|||
|
return consumeValue;
|
|||
|
}
|
|||
|
|
|||
|
public float ConsumeTickAligned(NetworkRunner runner)
|
|||
|
{
|
|||
|
int currentFrame = Time.frameCount;
|
|||
|
|
|||
|
// Revert accumulated value to state before latest accumulation.
|
|||
|
float consumeValue = _accumulatedValue - _unprocessedValue;
|
|||
|
|
|||
|
// In the first call (within single Unity frame) we want to accumulate only "missing" part since last Render to align timing with fixed tick (last Runner.LocalAlpha => 1.0).
|
|||
|
// All subsequent calls return remaining value which is not yet consumed, but again within alignment limits of fixed ticks (0.0 => 1.0 = current => next).
|
|||
|
float baseAlpha = _lastConsumeFrame != currentFrame ? runner.LocalAlpha : 0.0f;
|
|||
|
|
|||
|
// Here we calculate delta time between last Render time (or last tick aligned time) and time of the pending simulation tick.
|
|||
|
float tickAlignedDeltaTime = (1.0f - baseAlpha) * runner.DeltaTime;
|
|||
|
|
|||
|
// The unprocessed value is usually not aligned with ticks, we need to remove delta which is ahead of fixed tick time.
|
|||
|
float tickAlignedValue = _unprocessedValue * Mathf.Clamp01(tickAlignedDeltaTime / _unprocessedDeltaTime);
|
|||
|
|
|||
|
// Accumulate consume value up to next aligned tick time.
|
|||
|
consumeValue += tickAlignedValue;
|
|||
|
|
|||
|
// Decrease remaining unprocessed value by the partial value consumed by accumulation.
|
|||
|
_unprocessedValue -= tickAlignedValue;
|
|||
|
|
|||
|
// Decrease remaining unprocessed delta time by the partial delta time consumed by accumulation.
|
|||
|
_unprocessedDeltaTime -= tickAlignedDeltaTime;
|
|||
|
|
|||
|
// Removed the calculated consume value from total accumulated value. This is a remaining value that will be polled with for next tick.
|
|||
|
_accumulatedValue -= consumeValue;
|
|||
|
|
|||
|
_lastConsumeFrame = currentFrame;
|
|||
|
|
|||
|
return consumeValue;
|
|||
|
}
|
|||
|
|
|||
|
public void Clear()
|
|||
|
{
|
|||
|
_smoothValues.ClearValues();
|
|||
|
|
|||
|
_accumulatedValue = default;
|
|||
|
_unprocessedValue = default;
|
|||
|
_unprocessedDeltaTime = default;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|