This document is about: FUSION 2
SWITCH TO

TickTimer

はじめに

一定時間待った後に特定のロジックを実行できると、便利なことが多いです。Fusionでこれを行うには、TickTimerが、安全・便利で、帯域幅効率の良い方法です。TickTimerは、名前に反して実際はタイマーではなく、格納している目標ティック値と、シミュレーションの現在ティック値とを比較する構造体です。

現在ティック(NetworkRunnerから取得)と目標ティック(TickTimer初期化時に設定)を使用して、TickTimerは、他のタイマーと同じような情報を提供し、以下のような質問に答えることができます。

  • タイマーは時間切れか?
  • タイマーは実行中か?
  • タイマーの残り時間/ティックは?

TickTimerは、floatintを増減するかわりに目標ティックを使用します。ゲームの状態に含まれる([Networked]が付いている)場合でも、値は変わらず、タイマーのロジックはローカルで動作するため、帯域幅消費は少なく済みます。

注意TickTimer.CreateFromSeconds()で初期化した場合も、TickTimerは目標ティックを使用します。ティックレートや経過時間によっては、目標ティックの計算が正確でなくなる可能性があります。(例えば、TickTimer.CreateFromSeconds(1.37f)

API

  • CreateFromSeconds:新しいTickTimerを返す(目標ティックは、指定した秒数と現在ティックから計算される)
  • CreateFromTicks:新しいTickTimerを返す(目標ティックは、指定したティックと現在ティックから計算される)
  • RemainingTimeTickTimerが終了するまでの残り時間を、秒数で返す
  • RemainingTicksTickTimerが終了するまでの残り時間を、ティックで返す
  • IsRunningTickTimerの目標ティックが現在のティックより大きい場合は、trueを返す
  • ExpiredTickTimerが実行中かつ目標ティックが現在ティック以下の場合は、trueを返す
  • ExpiredOrNotRunningTickTimerdefaultTickTimer.None、または実行中かつ目標ティックが現在ティック以下の場合は、trueを返す

TickTimerのリセット

現在ティックが目標ティックより大きくなった後、TickTimer.Expired()は、毎ティックtrueを返します。これを一度だけ取得したい場合は、最初にtrueが返された後にTickTimerをリセットすると便利です。リセットは、TickTimerTickTimer.Nonedefaultを代入するだけです。

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