[Unity] ポーズ動作をTime.timeScale=0を使わずに実現する

ゲーム全体をポーズしたいとき、Time.timeScale=0としてゲームの進行時間を停止すれば簡単に実現できます。
しかし、アニメーションの動作も停止してしまい、ポーズ中に別のアニメーションを動かしたい場合に問題となってきます。
手動で無理やり動かすことも不可能ではないですが・・・

そこで、今回はTime.timeScaleの値を弄らずにポーズ動作を実現する方法について紹介します。

基本的な考え方は、「レンダラ以外のコンポーネントを無効にする」ことです。
アニメーションやコライダー、スクリプトなどのUpdate動作を持つコンポーネントを無効にすると、Update()が呼び出されなくなり、結果として動作を停止させることが出来ます。
レンダラを無効にしないのは、レンダラを無効にするとオブジェクトそのものが描画されなくなってしまうためです。

これらのUpdate()を持つコンポーネントはBehaviourクラスから派生されています。
したがって、これらのクラスのコンポーネントを取得して全て無効にすればよいわけです。

ポーズを行うためのスクリプトを以下にまとめました。
よろしければご自由にお使い下さい。

Pauser.cs

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

public class Pauser : MonoBehaviour {
	static List<pauser> targets = new List<pauser>();	// ポーズ対象のスクリプト
	Behaviour[] pauseBehavs = null;	// ポーズ対象のコンポーネント

	// 初期化
	void Start() {
		// ポーズ対象に追加する
		targets.Add(this);
	}

	// 破棄されるとき
	void OnDestory() {
		// ポーズ対象から除外する
		targets.Remove(this);
	}

	// ポーズされたとき
	void OnPause() {
		if ( pauseBehavs != null ) {
			return;
		}

		// 有効なBehaviourを取得
		pauseBehavs = Array.FindAll(GetComponentsInChildren<behaviour>(), (obj) => { return obj.enabled; });

		foreach ( var com in pauseBehavs ) {
			com.enabled = false;
		}
	}

	// ポーズ解除されたとき
	void OnResume() {
		if ( pauseBehavs == null ) {
			return;
		}

		// ポーズ前の状態にBehaviourの有効状態を復元
		foreach ( var com in pauseBehavs ) {
			com.enabled = true;
		}

		pauseBehavs = null;
	}

	// ポーズ
	public static void Pause() {
		foreach ( var obj in targets ) {
			obj.OnPause();
		}
	}

	// ポーズ解除
	public static void Resume() {
		foreach ( var obj in targets ) {
			obj.OnResume();
		}
	}
}

上記のスクリプトをポーズしたいゲームオブジェクトに追加すると、以下の処理でポーズ・ポーズ解除できます。

    // ポーズ
    Pauser.Pause();
    // ポーズ解除
    Pauser.Resume();

なお、上記スクリプトはRigidbodyのポーズには対応していません。
これはまた次回以降に書きたいと思います。

一度スクリプトを作れば、スクリプト追加という手間だけでポーズ動作が実現できるため簡単だと思います。