Unityで制限時間やN秒に一回行う処理をサクッと実装

# はじめに

少し前に書いたコードがメモ帳にあったので、Qiitaを始めたついでに投稿してみる
カウントダウンやカウントアップができる簡単なタイマークラス **KitchenTimer** を作ってみた

**「N秒に1回　〜する」** のような処理を簡単に試したい時に使う。

# 結論
完成形がこちら。使い方は下の方で説明。

```KitchenTimer.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;

/// Unity で簡単に使えるタイマークラス
public class KitchenTimer: MonoBehaviour
{
    //-----------------------------------------------------    
    /// [CountUp] 0秒からカウントアップ, u秒毎にeveryUが呼ばれる
    public void CountUp(float u, Action<float> everyU)
    {
        isCountUp = true;
        this.currentTime = 0; // 正確に保持している秒
        StartCoroutine(this.IECountUp(u, everyU));
    }

    //-----------------------------------------------------
    /// [CountDown] c秒からカウントダウン, d秒毎にeveryDが呼ばれる
    public void CountDown(float c, float d, Action<float> everyD, Action timeOver)
    {
        isCountUp = false;
        this.currentTime = c; // 正確に保持している秒
        StartCoroutine(this.IECountDown(c, d, everyD, timeOver));
    }

    //-----------------------------------------------------
    /// [StopCount] カウントをストップ
    public float StopCount()
    {
        this.isTimeRunning = false;
        return this.currentTime;
    }

    //-----------------------------------------------------
    /// [RestartCount] ストップのあとで、もう一度カウント再開
    public void RestartCount()
    {
        this.isTimeRunning = true;
        if(isCountUp)
        {
            StartCoroutine(this.IECountUp(this.tempInterval, this.tempAction));
        }
        else
        {
            this.CountDown(this.currentTime, this.tempInterval, this.tempAction, this.tempTimeOver);
        }
    }

    //-----------------------------------------------------
    /// [Wait] W秒間待ったあとで実行
    public void Wait(float w, Action action)
    {
        this.action = action;
        Invoke("DoAction",w);
    }
    
    //-----------------------------------------------------
    /// [Repeat] 実行 -> r秒待つ -> 実行　を繰り返す
    public void Repeat(float r, Action action)
    {
        this.action = action;
        InvokeRepeating("DoAction",0f, r);
    }

    //-----------------------------------------------------
    /// [WaitAndRepeat] Wait と Repeat を両方する
    public void WaitAndRepeat(float waitTime, float intervalTime, Action action)
    {
        this.action = action;
        InvokeRepeating("DoAction", waitTime, intervalTime);
    }

    //-----------------------------------------------------
    /// [Dispose] 使い終わった タイマー を捨てる
    public void Dispose()
    {
        CancelInvoke();
        Destroy(this);
    }

    //*******************************************************************
    //                その他
    //*******************************************************************

    /// 時間を保持するプロパティ
    private float currentTime;

    /// カウントを進めて良いかどうか(時間が進んでいるか)
    private bool isTimeRunning = true;

    /// カウントアップ中かダウン中か
    private bool isCountUp;

    /// メソッドを保持するプロパティ
    private Action action;

    // 中断中のカウントの変数を一時保持する
    private float tempInterval;
    private Action<float> tempAction;
    private Action tempTimeOver;

    /// 保持したメソッドを実行する
    private void DoAction()
    {
        this.action();
    }

    /// カウントアップ用のIE
    private IEnumerator IECountUp(float u, Action<float> everyU)
    {
        // 中断用の一時変数を保存
        this.tempAction = everyU;
        this.tempInterval = u;

        var count = 0f; 
        while(currentTime < 600f) // 限界は600秒までとする
        {
            if (isTimeRunning)
            {
                this.currentTime += Time.deltaTime;
                count += Time.deltaTime;
            }
            else
            {
                break;
            }
            if(count > u)
            {
                everyU(this.currentTime);
                count -= u;
            }
            yield return null;
        }
        if (isTimeRunning) 
        { 
            // 最後に忘れず、一時変数をnullに
            this.tempAction = null;
        }
    }
    
    /// カウントダウン用のIE
    private IEnumerator IECountDown(float c, float d, Action<float> everyD, Action timeOver)
    {
        // 中断用の一時変数を保存
        this.tempInterval = d;
        this.tempAction = everyD;
        this.tempTimeOver = timeOver;

        var count = 0f; 
        while(currentTime > 0)
        {
            if (isTimeRunning)
            {
                this.currentTime -= Time.deltaTime;
                count += Time.deltaTime;
            }
            else
            {
                break;
            }
            if(count > d)
            {
                everyD(this.currentTime);
                count -= d;
            }
            yield return null;
        }
        if (isTimeRunning)
        {
            timeOver(); // 中断以外でここに来た場合はタイムオーバーということ
        
            // 最後に忘れず、一時変数をnullに
            this.tempAction = null;
            this.tempTimeOver = null;
        }
    }
}

```

# 使い方

## 1. **KitchenTimer.cs** をProjectウィンドウの中の好きなところに置く

## 2. timerインスタンスの生成
新しいタイマーを作る。
※このスクリプトを貼り付けたオブジェクトが消滅すると、タイマーもなくなるので注意。

```    
    var timer = this.gameObject.AddComponent<KitchenTimer>(); 
```

### カウントアップ(0, 1, 2 ...)  timer.StartCountUp
1.0秒に1回、処理をする例
time には、その時の時間が少数まで入っている。 「1.13」 「2.04」
場合に合わせて四捨五入して使う

```
    timer.StartCountUp(1.0f, 
        (time) => 
        {
            // ここに1秒に1回やりたい処理を書く
            Debug.Log(time);
        }
    );
```

### カウントダウン(30, 29, 28 ...)  timer.StartCountDown
残り30秒からスタートして、1秒に1回処理をする例。

```
    timer.StartCountDown(30.0f, 1.0f, 
        (time) => 
        {
            // ここに1秒に1回やりたい処理を書く
            Debug.Log(time);
        }, 
        () => 
        {
            // 30.0秒のカウントダウンが終わったらここに来る
            Debug.Log("時間切れ");
        }
    );
```

### 一時ストップ  timer.Stop

```
    timer.Stop(
        (time)=>
        {
            // time にはタイマーが止まった時の時間が入っている
            Debug.Log(time);
        }
    );
```

### ストップ時点から再開  timer.StartFromStopPoint

```
    timer.StartFromStopPoint();
```

### 最初から測り直し  timer.ResetAndReStart

```
    timer.ResetAndReStart();
```

### タイマーを捨てる(メモリ開放を明示的にしたいとき)      timer.Dispose

```
    timer.Dispose();
```


# さいごに
時間の単位は全て **「秒」** です。変換クラスと上手いこと合わせて使ってやってください。
以上です！

それにしても「キッチンタイマー」て。。。変な命名にハマる時期ってありますよね。。