[Unity] 複数のタイマーチャネルを持たせる

Unityのタイマー関連の情報はTimeクラスのstaticメンバより取得できます。
以下のように使用されることが多いでしょう。

[SerializeField]
float delay = 1;

void Update() {
    // 最初のdelay秒は何もしない
    delay -= Time.deltaTime;
    if ( delay > 0 ) {
        return;
    }

    // 時間を表示
    ShowTime(Time.time);
}

前回のフレームからの経過時間はTime.deltaTime、ゲーム起動からの経過時間はTime.timeをReadすることで取得できます。

しかし、これらはstaticメンバのため、以下のように時間の進行速度を変化させるとゲーム全体に影響を及ぼしてしまいます。

void Start() {
    // 時間の進行速度を半分にする
    Time.timeScale = 0.5f;
}

ある特定のオブジェクトだけ時間の進行速度を変えたい場合(ポーズも含む)、複数のタイマーがあると有難いです。
しかし、そのような機能はUnityには元々存在しないため、自力で実装する必要があります。

これはちょっとした工夫で簡単に実装できます。
複数のタイマーを管理するスクリプトの例を以下に記します。

MultipleTimer.cs

using UnityEngine;
using System;
using System.Collections;

/// <summary>
/// 複数のタイマーチャネルを管理する
/// </summary>
public class MultipleTimer: MonoBehaviour {
    /// <summary>
    /// 各タイマーチャネルを管理する構造体
    /// </summary>
    [Serializable]
    public class Channel {
        public float time;
        public float timeScale;

        [HideInInspector]
        public float deltaTime;
    };

    /// <summary>
    /// タイマーチャネル配列
    /// </summary>
    public Channel[] channel;

    /// <summary>
    /// タイマーチャネルの状態を更新する
    /// </summary>
    private void Update() {
        for ( int i = 0 ; i < channel.Length ; ++i ) {
            var ch = channel[i];

            ch.deltaTime = Time.deltaTime * ch.timeScale;
            ch.time += ch.deltaTime;
        }
    }
}

次の部分で複数のタイマーを管理しています。

    /// <summary>
    /// 各タイマーチャネルを管理する構造体
    /// </summary>
    [Serializable]
    public class Channel {
        public float time;
        public float timeScale;

        [HideInInspector]
        public float deltaTime;
    };

    /// <summary>
    /// タイマーチャネル配列
    /// </summary>
    public Channel[] channel;

time、timeScale、deltaTimeをメンバに持つ構造体の配列を定義しています。

これらのメンバーの値はUpdate()メソッド内で毎フレーム更新しています。

    private void Update() {
        for ( int i = 0 ; i < channel.Length ; ++i ) {
            var ch = channel[i];

            ch.deltaTime = Time.deltaTime * ch.timeScale;
            ch.time += ch.deltaTime;
        }
    }

deltaTimeやtimeの値をtimeScaleの値から独自に計算しています。
この計算処理はUnityタイマーのそれを真似ています。

上記で実装したタイマーは以下のように使用できます。

MultipleTimer timer;

void Start() {
    timer = FindObjectOfType<MultipleTimer>();
}

void Update() {
    // チャネル1のタイマーでx軸方向に移動
    var ch1 = timer.channel[0];
    transform.position += new Vector3(50, 0, 0) * ch1.deltaTime;

    // チャネル2のタイマーでy軸方向に移動
    var ch2 = timer.channel[1];
    transform.position += new Vector3(0, 50, 0) * ch2.deltaTime;
}

これで以下のようにGameObjectそれぞれに独立したタイマーを持たせて動かせるようになりました。

少々面倒ですが、複数のタイマーを使用する場合、タイマー管理スクリプトから情報を取得するように開発者が気を付けるようにすれば良いでしょう。

■参考サイト
Unity – Scripting API:Time