今回はアクションゲームのヒットストップ機能を作成してみたいと思います。
つい先日までヒットストップ機能という用語すら知らなかったんですが・・・(^_^;)
大乱闘スマッシュブラザーズとかで相手に攻撃を与えた時にアニメーションをストップしたりスロー再生する機能みたいです。
ツイッターでご依頼を頂いたので作ってみました。
今回はゲーム全体の時間の流れを遅くする方法とキャラクターのアニメーションのスピードを遅くする方法の2パターンを作成していきます。
なぜ2パターン作成したかと言うと、ゲーム全体の流れを遅くするだけの方が簡単に出来るんですが、3プレイヤー以上が同時にプレイ出来るゲームだと関係ないプレイヤーまで遅くなってしまう為、キャラクターのアニメーションを遅くする方法も作りました。
2プレイヤーの対戦格闘ゲーム等ではゲーム全体の流れを遅くするパターンでもいいかもしれません。
今回のアニメーションを遅くするパターンの機能を搭載すると、
↑のような感じで敵に攻撃を与えた時にお互いのキャラクターのアニメーションがスロー再生されるようになります。
後ろのCubeは遅くならずそのままのスピードで移動しています。
ヒットストップ機能の作成
それではヒットストップ機能を作成していきましょう。
今回の機能を作成するには主人公キャラクターにAnimatorControllerを設定し、移動や攻撃が出来るようにしておく必要があります。
AnimatorControllerは
移動プログラムは
を参照してください。
攻撃機能に関しては、
を参照してください。
主人公と敵以外のゲームオブジェクトの作成
ゲーム全体の時間の流れを遅くすると主人公と敵以外のゲームオブジェクトも動きが遅くなり、キャラクターのアニメーションのスピードを遅くする場合だと他のゲームオブジェクトの動きは変わらずそのままです。
それを確認する為に、主人公と敵以外のゲームオブジェクトを配置し常に動かして確認出来るようにしておきます。
ヒエラルキー上にCubeを配置し、HitStopMoveCubeスクリプトを作成し、取り付けます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | using UnityEngine; using System.Collections; public class HitStopMoveCube : MonoBehaviour { private Vector3 defaultPos; void Start() { defaultPos = transform.position; } // Update is called once per frame void Update () { transform.position = defaultPos + new Vector3 (3f * Mathf.Sin (3f * Time.time), 0f, 0f); } } |
ゲームオブジェクトが元の位置からX軸をいったりきたりするだけです。
ゲーム全体の流れを遅くするパターン
ゲーム全体の流れを遅くするパターンのヒットストップ機能を作成していきます。
ゲーム全体の時間の流れはTime.timeScaleの値を変更する事で実現出来るので、主人公の攻撃が敵に当たった時にTime.timeScaleの値を変更するようにします。
時間の流れを管理するTimeManagerスクリプトの作成
TimeScaleを変更するスクリプトを作成しMainCameraに取り付けます。
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 | using UnityEngine; using System.Collections; public class TimeManager : MonoBehaviour { // Time.timeScaleに設定する値 [SerializeField] private float timeScale = 0.1f; // 時間を遅くしている時間 [SerializeField] private float slowTime = 1f; // 経過時間 private float elapsedTime = 0f; // 時間を遅くしているかどうか private bool isSlowDown = false; void Update() { // スローダウンフラグがtrueの時は時間計測 if (isSlowDown) { elapsedTime += Time.unscaledDeltaTime; if (elapsedTime >= slowTime) { SetNormalTime (); } } } // 時間を遅らせる処理 public void SlowDown() { elapsedTime = 0f; Time.timeScale = timeScale; isSlowDown = true; } // 時間を元に戻す処理 public void SetNormalTime() { Time.timeScale = 1f; isSlowDown = false; } } |
timeScaleはTime.timeScaleに設定する値で通常は1なので0.1を設定し10分の1の速さになるようにします。
slowTimeは時間を遅くしておく時間で1秒間はスロー状態が続くようにします。
elapsedTimeはスローになってからの経過時間でslowTime経過したら元の速さに戻します。
敵がダメージを受けた時にTimeManagerクラスのSlowDownメソッドを呼び出しTime.timeScaleの値を変更し、isSlowDownをtrueにします。
isSlowDownがtrueの状態の時はUpdateメソッドで時間を計測し、slowTimeを超えたらSetNormalTimeを呼び出し通常の速さに戻します。
時間計測はTime.deltaTimeで行うとTime.timeScaleの影響を受けて時間の計測も遅くなるので、Time.timeScaleの影響を受けないTime.unscaledDeltaTimeを使用してスローになってからの経過時間を計測します。
敵がダメージを受けた時のスクリプトHitStopZombieDamageの作成
敵がダメージを受けた時のスクリプトを作成し取り付けます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | using UnityEngine; using System.Collections; public class HitStopZombieDamage : MonoBehaviour { [SerializeField] private GameObject damagePrefab; [SerializeField] private TimeManager timeManager; void OnTriggerEnter(Collider col) { if (col.tag == "Player") { var damageParticle = GameObject.Instantiate (damagePrefab, col.ClosestPointOnBounds (col.transform.position), Quaternion.identity) as GameObject; // 全体のタイムスケールを変更する timeManager.SlowDown (); } } } |
damagePrefabはダメージを受けた時のパーティクルのプレハブを設定し、timeManagerは先ほど作成したTimeManager(MainCameraに取り付けた)を設定します。
ダメージ用のパーティクルプレハブにはパーティクルの再生が終わったら自動で自身のゲームオブジェクトが削除されるようなスクリプトを設定しておきます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | using UnityEngine; using System.Collections; public class HitStopDestroyGameObject : MonoBehaviour { private ParticleSystem particle; // Use this for initialization void Start () { particle = GetComponentInChildren <ParticleSystem> (); } // Update is called once per frame void Update () { if (particle.isStopped) { Destroy (gameObject); } } } |
↑のようにパーティクルが止まったらゲームオブジェクトを削除します。
主人公キャラクターの足や武器のコライダにPlayerタグを設定しておきます。
HitStopZombieDamageはPlayerタグを設定した武器等が侵入した時にダメージを受けたと判定し、ダメージ用プレハブをインスタンス化し、ClosestPointOnBoundsに相手のコライダの位置を渡し、自身のコライダの一番近い場所にダメージ用のパーティクルプレハブを表示するようにします。
その後TimeManagerクラスのSlowDownメソッドを呼び出します。
これでゲーム全体の時間を遅くする機能は完成です。
ゲーム全体の時間を遅くするサンプルの確認
機能が出来たので実行してみましょう。
↑のようになりました。
敵に攻撃を与えると主人公、敵、後ろのCubeの動きが遅くなっているのがわかりますね。
キャラクターのアニメーションスピードを遅くするパターン
次はキャラクターのアニメーションスピードを遅くするパターンを作成していきます。
キャラクターのアニメーションスピードを遅くする為にはAnimatorのスピードを操作し、キャラクターアニメーションのスピードを遅くします。
ダメージ用プレハブのパーティクルシステムのスピードも一緒に遅くします。
HitStopZombieDamageスクリプトの変更
HitStopZombieDamageスクリプトを変更します。
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 | using UnityEngine; using System.Collections; public class HitStopZombieDamage : MonoBehaviour { [SerializeField] private GameObject damagePrefab; private HitStopSlowAnim hitStopSlowAnim; void Start() { hitStopSlowAnim = GetComponent<HitStopSlowAnim> (); } void OnTriggerEnter(Collider col) { if (col.tag == "Player") { var damageParticle = GameObject.Instantiate (damagePrefab, col.ClosestPointOnBounds (col.transform.position), Quaternion.identity) as GameObject; damageParticle.GetComponent<HitStopSlowParticle> ().SlowDown (); // 個々のAnimatorのスピードを変更する場合 var playerSlowAnim = col.GetComponentInParent <HitStopSlowAnim> (); if (!playerSlowAnim.IsSlowDown ()) { playerSlowAnim.SlowDown (); } if (!hitStopSlowAnim.IsSlowDown ()) { hitStopSlowAnim.SlowDown (); } } } } |
ダメージを受けた時はダメージ用プレハブをインスタンス化した後、ダメージ用プレハブに設定したHitStopSlowParticleスクリプトのSlowDownメソッドを呼び出します。
HitStopSlowParticleスクリプトはこの後作成しますが、パーティクルの再生速度を遅くする為のスクリプトです。
その後、接触したコライダ(主人公の武器や足等のコライダ)からHitStopSlowAnimスクリプトを取得し、現在スロー再生中でなければスロー再生にします。
ダメージを受けた敵自身のアニメーションスピードも遅くする為同じようにHitStopSlowAnimスクリプトのSlowDownメソッドを呼び出しています。
HitStopSlowAnimスクリプトはこの後作成します。
HitStopSlowParticleスクリプトの作成
HitStopSlowPariticleスクリプトを作成します。
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 | using UnityEngine; using System.Collections; public class HitStopSlowParticle : MonoBehaviour { // 通常時のパーティクルスピード private float normalParticleSpeed; // 遅くした時のパーティクルスピード [SerializeField] private float slowParticleSpeed = 0.1f; // 時間を遅くしている時間 [SerializeField] private float slowTime = 1f; // 経過時間 private float elapsedTime = 0f; // 時間を遅くしているかどうか private bool isSlowDown = false; private ParticleSystem[] particles; void Update() { // スローの時だけ実行 if (isSlowDown) { elapsedTime += Time.unscaledDeltaTime; if (elapsedTime >= slowTime) { SetNormalTime (); } } } // 時間を遅くする処理 public void SlowDown() { elapsedTime = 0f; particles = GetComponentsInChildren <ParticleSystem> (); // とりあえず最初のパーティクルシステムのノーマルスピードを取得 normalParticleSpeed = particles[0].playbackSpeed; // パーティクルプレハブに使用しているパーティクルシステムの全部のスピードを変更 foreach (var particle in particles) { particle.playbackSpeed = slowParticleSpeed; } isSlowDown = true; } // 時間を元に戻す処理 public void SetNormalTime() { foreach (var particle in particles) { particle.playbackSpeed = normalParticleSpeed; } isSlowDown = false; } // 時間を遅くしているかどうかを返す public bool IsSlowDown() { return isSlowDown; } } |
ほとんどTimeManagerスクリプトと同じですね。
今回わたくしが使用したダメージ用のパーティクルのプレハブにはParticleSystemがいくつか設定されている為、全てのParticleSystemを取得し全てのスピードを遅くしています。
ParticleSystemの再生スピードはplaybackSpeedを変更する事で出来ますので、SlowDownやSetNormalTimeメソッドで変更しています。
HitStopSlowAnimスクリプトの作成
キャラクターのアニメーションスピードを変更するスクリプトHitStopSlowAnimスクリプトを作成し、主人公と敵キャラクターに取り付けます。
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 | using UnityEngine; using System.Collections; public class HitStopSlowAnim : MonoBehaviour { // 通常時のアニメーションスピード private float normalAnimSpeed; // 遅くした時のアニメーションスピード [SerializeField] private float slowAnimSpeed = 0.1f; // 時間を遅くしている時間 [SerializeField] private float slowTime = 1f; // 経過時間 private float elapsedTime = 0f; // 時間を遅くしているかどうか private bool isSlowDown = false; private Animator animator; void Start() { animator = GetComponent <Animator> (); normalAnimSpeed = animator.speed; } void Update() { // スローの時だけ実行 if (isSlowDown) { elapsedTime += Time.unscaledDeltaTime; if (elapsedTime >= slowTime) { SetNormalTime (); } } } // 時間を遅くする処理 public void SlowDown() { elapsedTime = 0f; animator.speed = slowAnimSpeed; isSlowDown = true; } // 時間を元に戻す処理 public void SetNormalTime() { animator.speed = normalAnimSpeed; isSlowDown = false; } // 時間を遅くしているかどうかを返す public bool IsSlowDown() { return isSlowDown; } } |
こちらもTimeManagerスクリプトと同じですね。
Animatorのspeedを変更する事でアニメーションのスピードを変更する事が出来ます。
これでキャラクターのアニメーションスピードの変更とパーティクルの再生スピードの変更機能が出来ました。
キャラクターのアニメーションスピードを変更するサンプルの確認
機能が出来たので確認してみましょう。
↑のようになりました。
記事の最初に紹介した動画とまったく同じですが・・・・(^_^;)
キャラクターと敵、パーティクルはスローになりますが、後ろのCubeの速さは変わっていません。
終わりに
今回はダメージを与えた時に2パターンで速さを変えてみました。
アクションゲーム等で必殺技を発動する時に一旦止めたい!
といった時もTime.timeScaleを0にしたりすれば実現出来ますね(^^)/