TickTimer
はじめに
あるロジックをトリガーする前に、一定時間待機させることは有用です。Fusionでこのような処理を行うには、安全で便利、かつ効率的な方法としてTickTimer
があります。TickTimer
という名前ですが、実際にはタイマーではなく、ターゲットティック値を保存し、現在のシミュレーションティックを使ってチェックを実行する構造体です。
現在のティック(NetworkRunner
から取得した)とターゲットティック(TickTimer
の初期化時に保存した)を使って、TickTimer
は他のタイマーと同じように情報を提供し、次のような質問に答えることができます。
- タイマーが切れましたか?
- タイマーは作動していますか?
- 残り時間/残りティック数は?
TickTimer
は float や int を増減させるのではなく、ターゲットの tick を使用するので、[Networked]
ゲームステートに含まれる場合は、タイマーのロジックがローカルで変化しないため、使用する帯域幅が少なくて済みます。
注: TickTimer
は TickTimer.CreateFromSeconds()
で初期化した場合でもターゲットのティックを使用します。この点とティックレートを考慮すると、例えば TickTimer.CreateFromSeconds(1.37f)
. のように、経過秒数によって計算されたターゲットティックが少し精度を失う可能性があります。
API
CreateFromSeconds
: 指定された Seconds の量と現在のシミュレーションの目盛りを使ってターゲット目盛りを計算し、新しいTickTimer
を返します。CreateFromTicks
: 指定された Ticks と現在のシミュレーションtickを使用して、ターゲットのtickを計算した新しいTickTimer
を返します。RemainingTime
:TickTimer
が終了するまでの残り時間を Seconds で返します。RemainingTicks
:TickTimer
が終了するまでの残り時間を Ticks で返します。IsRunning
:TickTimer
のターゲットティックが現在のシミュレーションティックより大きい場合、trueを返します。Expired
:TickTimer
が実行中で、ターゲットtick が現在のシミュレーションtick よりも小さいか等しい場合に真を返します。ExpiredOrNotRunning
:TickTimer
がdefault
,TickTimer.None
, または実行中で、ターゲットtickが現在のシミュレーションtickより小さいか等しい場合に true を返します。
TickTimer をリセットする
TickTimer
は、シミュレーションティックが保存されたターゲットティックより大きくなった後、tick毎にtrueを返します。これを一度だけ得るには、この条件がtrueと評価された最初のtickの後に TickTimer
をリセットすることが有効です。これは、TickTimer
を TickTimer.None
または 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");
}
}
カスタム機能の作成
TickTimer
はシンプルな機能であるため、そのカスタムバージョンを作成し、より多くの、あるいは異なる機能を実装することは簡単です。
以下の2つの例 - カウントアップと時間の正規化 - は、タイマーが作成された瞬間に最初の目盛りを追加するだけで 実現できます。
注: 以下のコードは、オリジナルの TickTimer
をカスタマイズして簡略化したもので、カスタムバージョンで実装可能な機能を紹介しています。
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;
}
}
カウントアップ
タイマーが作成されてから経過したティック数を取得するには、NetworkRunner
の現在のティックから最初のティックの値を引きます。
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;
}
0と1の間の正規化された値
タイマーの現在の進行状況を0と1の間の正規化された浮動小数点数で返すには、経過したティック数をタイマーがカウントしている総ティック数で割ります。
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);
}
これで、CustomTickTimer
を使ってそれらの値を表示することができるようになりました。
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