
ポーズ処理は、Unityでゲームを書いていると誰もが頭を抱える部分だと思います。
ネット上にもいくつか落ちていますが、あまり使い勝手の良い物がなかったので、自分で実装してみました。
ソースコード
基本的には、ftvlog様の仕組みが元になっています。
[Unity] ポーズ動作をTime.timeScale=0を使わずに実現する | ftvlog
大きな違いとして以下の点があります。
- static関数を使っていないので、依存関係がシンプル。
- ゲーム中での時が止まる演出など、ポーズ可能なオブジェクトの入れ子表現が出来る。
- カメラやAudioListener等は止まらない
- 無視するGameObjectを指定できる。
- インスペクタからPause/Resumeを切り替えられる。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 |
using UnityEngine; using System.Collections; using System.Collections.Generic; using System; /// <summary> /// Rigidbodyの速度を保存しておくクラス /// </summary> public class RigidbodyVelocity { public Vector3 velocity; public Vector3 angularVeloccity; public RigidbodyVelocity(Rigidbody rigidbody) { velocity = rigidbody.velocity; angularVeloccity = rigidbody.angularVelocity; } } public class Pausable : MonoBehaviour { /// <summary> /// 現在Pause中か? /// </summary> public bool pausing; /// <summary> /// 無視するGameObject /// </summary> public GameObject[] ignoreGameObjects; /// <summary> /// ポーズ状態が変更された瞬間を調べるため、前回のポーズ状況を記録しておく /// </summary> bool prevPausing; /// <summary> /// Rigidbodyのポーズ前の速度の配列 /// </summary> RigidbodyVelocity[] rigidbodyVelocities; /// <summary> /// ポーズ中のRigidbodyの配列 /// </summary> Rigidbody[] pausingRigidbodies; /// <summary> /// ポーズ中のMonoBehaviourの配列 /// </summary> MonoBehaviour[] pausingMonoBehaviours; /// <summary> /// 更新処理 /// </summary> void Update() { // ポーズ状態が変更されていたら、Pause/Resumeを呼び出す。 if (prevPausing != pausing) { if (pausing) Pause (); else Resume (); prevPausing = pausing; } } /// <summary> /// 中断 /// </summary> void Pause() { // Rigidbodyの停止 // 子要素から、スリープ中でなく、IgnoreGameObjectsに含まれていないRigidbodyを抽出 Predicate<Rigidbody> rigidbodyPredicate = obj => !obj.IsSleeping() && Array.FindIndex(ignoreGameObjects, gameObject => gameObject == obj.gameObject) < 0; pausingRigidbodies = Array.FindAll(transform.GetComponentsInChildren<Rigidbody>(), rigidbodyPredicate); rigidbodyVelocities = new RigidbodyVelocity[pausingRigidbodies.Length]; for(int i = 0; i < pausingRigidbodies.Length; i++) { // 速度、角速度も保存しておく rigidbodyVelocities[i] = new RigidbodyVelocity(pausingRigidbodies[i]); pausingRigidbodies[i].Sleep (); } // MonoBehaviourの停止 // 子要素から、有効かつこのインスタンスでないもの、IgnoreGameObjectsに含まれていないMonoBehaviourを抽出 Predicate<MonoBehaviour> monoBehaviourPredicate = obj => obj.enabled && obj != this && Array.FindIndex(ignoreGameObjects, gameObject => gameObject == obj.gameObject) < 0; pausingMonoBehaviours = Array.FindAll(transform.GetComponentsInChildren<MonoBehaviour>(), monoBehaviourPredicate); foreach(var monoBehaviour in pausingMonoBehaviours) { monoBehaviour.enabled = false; } } /// <summary> /// 再開 /// </summary> void Resume() { // Rigidbodyの再開 for(int i = 0; i < pausingRigidbodies.Length; i++) { pausingRigidbodies[i].WakeUp(); pausingRigidbodies[i].velocity = rigidbodyVelocities[i].velocity; pausingRigidbodies[i].angularVelocity = rigidbodyVelocities[i].angularVeloccity; } // MonoBehaviourの再開 foreach(var monoBehaviour in pausingMonoBehaviours) { monoBehaviour.enabled = true; } } } |
使い方
こんな感じでポーズさせたいオブジェクトの階層を作り、一番上のGameObjectにPausableを割り当ててやります。
コメントを残す