TickTimer
はじめに
一定時間待った後に特定のロジックを実行できると、便利なことが多いです。Fusionでこれを行うには、TickTimer
が、安全・便利で、帯域幅効率の良い方法です。TickTimer
は、名前に反して実際はタイマーではなく、格納している目標ティック値と、シミュレーションの現在ティック値とを比較する構造体です。
現在ティック(NetworkRunner
から取得)と目標ティック(TickTimer
初期化時に設定)を使用して、TickTimer
は、他のタイマーと同じような情報を提供し、以下のような質問に答えることができます。
- タイマーは時間切れか?
- タイマーは実行中か?
- タイマーの残り時間/ティックは?
TickTimer
は、float
やint
を増減するかわりに目標ティックを使用します。ゲームの状態に含まれる([Networked]
が付いている)場合でも、値は変わらず、タイマーのロジックはローカルで動作するため、帯域幅消費は少なく済みます。
注意:TickTimer.CreateFromSeconds()
で初期化した場合も、TickTimer
は目標ティックを使用します。ティックレートや経過時間によっては、目標ティックの計算が正確でなくなる可能性があります。(例えば、TickTimer.CreateFromSeconds(1.37f)
)
API
CreateFromSeconds
:新しいTickTimer
を返す(目標ティックは、指定した秒数と現在ティックから計算される)CreateFromTicks
:新しいTickTimer
を返す(目標ティックは、指定したティックと現在ティックから計算される)RemainingTime
:TickTimer
が終了するまでの残り時間を、秒数で返すRemainingTicks
:TickTimer
が終了するまでの残り時間を、ティックで返すIsRunning
:TickTimer
の目標ティックが現在のティックより大きい場合は、true
を返すExpired
:TickTimer
が実行中かつ目標ティックが現在ティック以下の場合は、true
を返すExpiredOrNotRunning
:TickTimer
がdefault
かTickTimer.None
、または実行中かつ目標ティックが現在ティック以下の場合は、true
を返す
TickTimerのリセット
現在ティックが目標ティックより大きくなった後、TickTimer.Expired()
は、毎ティックtrue
を返します。これを一度だけ取得したい場合は、最初にtrue
が返された後に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で正規化された値で返すには、経過ティック数を、タイマーがカウントする総ティック数で割ります。
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