TickTimer
Introduction
It is often useful to wait for some period of time before triggering certain logic. TickTimer
s are a safe, convenient, and bandwidth efficient way to do this in Fusion. Despite its name, TickTimer
is not really a timer, but rather a struct which stores a target tick value and run checks using the current simulation tick.
Using the current tick (from the NetworkRunner
) and the target tick (saved when initializing the TickTimer
), the TickTimer
can provide information much like any other timer does and answer questions such as:
- has the timer expired?
- is the timer running?
- how much time / many ticks are left?
Since the TickTimer
uses the target tick instead of incrementing or decrementing a float or int, it uses less bandwidth when it is included as part of the [Networked]
game state since it does not change and the timer logic is local.
Note: TickTimer
uses a target tick, even when initializing it with TickTimer.CreateFromSeconds()
. With this and the tick rate in mind, it is possible for the calculated target tick to lose a bit of accuracy depending on the amount of seconds passed, e.g TickTimer.CreateFromSeconds(1.37f)
.
API
CreateFromSeconds
: Returns a newTickTimer
with the target tick calculated using the amount of Seconds provided and the current simulation tick.CreateFromTicks
: Returns a newTickTimer
with the target tick calculated using the amount of Ticks provided and the current simulation tick.RemainingTime
: Returns the remaining time in Seconds until theTickTimer
expires.RemainingTicks
: Returns the remaining time in Ticks until theTickTimer
expires.IsRunning
: Returns true if theTickTimer
target tick is greater than the current simulation tick.Expired
: Returns true if theTickTimer
is running and the target tick is less or equals than the current simulation tick.ExpiredOrNotRunning
: Returns true if theTickTimer
isdefault
,TickTimer.None
, or running and the target tick is less or equals than the current simulation tick.
Reset the TickTimer
A TickTimer
is expired returns true every tick after the simulation tick has become greater than the target tick stored by it. To get this only once, is useful to reset the TickTimer
after the first tick on which this condition is evaluated as true. This can be done by setting the TickTimer
to TickTimer.None
or default
.
C#
[Networked] TickTimer timer { get; set; }
void FixedUpdateNetwork()
{
if (timer.Expired(Runner))
{
// Execute Logic
// Reset timer
timer = TickTimer.None;
// alternatively: timer = default.
Debug.Log("Timer Expired");
}
}
Creating Custom Functionalities
Given the simple nature of TickTimer
, it is easy to create a custom version of it and implement more or different functionalities.
The two following examples - Counting up and Normalized time - are possible simply by adding the initial tick on the moment the timer was created.
Note: The following code is a custom and simplified version of the original TickTimer
to showcase functionalities which can be implemented in custom versions.
C#
public struct CustomTickTimer : INetworkStruct
{
private int _target;
private int _initialTick;
public bool Expired(NetworkRunner runner) => runner.IsRunning && _target > 0
&& (Tick) _target <= runner.Simulation.Tick;
public bool IsRunning => _target > 0;
public static CustomTickTimer CreateFromTicks(NetworkRunner runner, int ticks)
{
if (runner == false || runner.IsRunning == false)
return new CustomTickTimer();
CustomTickTimer fromTicks = new CustomTickTimer();
fromTicks._target = (int) runner.Simulation.Tick + ticks;
fromTicks._initialTick = runner.Simulation.Tick;
return fromTicks;
}
public float NormalizedValue(NetworkRunner runner)
{
if (runner == null || runner.IsRunning == false || IsRunning == false)
return 0;
if (Expired(runner))
return 1;
return ElapsedTicks(runner) / (_target - (float)_initialTick);
}
public int ElapsedTicks(NetworkRunner runner)
{
if (runner == false || runner.IsRunning == false)
return 0;
if (IsRunning == false || Expired(runner))
return 0;
return runner.Simulation.Tick - _initialTick;
}
}
Counting up
To get the elapsed number of ticks since the timer was created, subtract the initial tick value from the NetworkRunner
's current tick.
C#
public int ElapsedTicks(NetworkRunner runner)
{
if (runner == false || runner.IsRunning == false)
return 0;
if (IsRunning == false || Expired(runner))
return 0;
return runner.Simulation.Tick - _initialTick;
}
Normalized value between 0 and 1
To return a normalized float between 0 and 1 for the timer's current progress, divide the number of elapsed ticks by the total number ticks that the timer is counting.
C#
public float NormalizedValue(NetworkRunner runner)
{
if (runner == null || runner.IsRunning == false || IsRunning == false)
return 0;
if (Expired(runner))
return 1;
return ElapsedTicks(runner) / (_target - (float)_initialTick);
}
Now, the CustomTickTimer
can be used to show those values.
C#
[Networked]
public CustomTickTimer MyTickTimer { get; set; }
public void StartTimer()
{
if (Runner.IsServer)
{
MyTickTimer = CustomTickTimer.CreateFromTicks(Runner, 120);
}
}
public override void FixedUpdateNetwork()
{
Debug.Log($"Elapsed {MyTickTimer.ElapsedTicks(Runner)} ticks.");
Debug.Log($"Normalized Value {MyTickTimer.NormalizedValue(Runner)}.");
if (MyTickTimer.Expired(Runner))
{
// Execute Logic
// Reset timer
MyTickTimer = default;
Debug.Log("Timer Finished on tick: " + Runner.Simulation.Tick);
}
}
Back to top