前回までで一応ステージを作れるようになりましたが、まだ動かないオブジェクトしかないのでこのままでは少し味気ない気がします。そこでランダムに移動するゾンビを追加してみましょう(さすがに人間を轢くのはゲームでもまずいと思ったのでゾンビにしました)。
アセットのインポート&設定
まずゾンビのモデルをアセットストアからダウンロードしてインポートします。今回は「PXLTIGER」というところが出している「Zombie」というそのものズバリなアセットを使用しますので(※執筆時点ではアセットストアで「Zombie」と検索すると上の方に出てきます)、プロジェクトにインポートしておいてください。
プレハブの設定
そうしたら、Zombieフォルダに入っているプレハブの設定を行います。最初はAnimatorがアタッチされているだけなので、RigidbodyとColliderをアタッチして設定しましょう(下図)。
※訂正:左のインスペクタの図のBox Colliderのチェックは外しておいてください。(右の図を作るためにチェックしておいたのを外し忘れました)
なお、Colliderを2種類つける理由についてですが、この2種類のColliderは
- Capsule Collider:ゾンビが立っているときのコライダー
- Box Collider:ゾンビが倒れているときのコライダー
となっています。というのも、今回使っているゾンビのアニメーションでは「倒れる」動作のときに(物理演算とは関係なく)モデルを回転させているので、ゾンビが倒れたときにカプセルコライダーをそのまま使うとおかしな倒れ方になってしまうからです。
Animatorの設定
次にAnimatorの設定を行います(もしアニメーターウィンドウが表示されていない場合はメニューの「Window」→「Animation」→「Animator」から表示させてください)。初期状態だと一連のステートがつながった状態になっていますが、これを下の図のように変更し、パラメータ「IsDead」「IsWalk」を追加してください。なお今回は「idle」「walk」「fallingback」ステートのみ使用します。
そうしたら、それぞれのトランジション(図の白い矢印線)を次のように変更します(いずれも「Has Exit Time」のチェックを外してください)。
- idle→walk:ConditionsにIsWalk・trueを追加
- walk→idle:ConditionsにIsWalk・falseを追加
- idle→fallingback、walk→fallingback:ConditionsにIsDead・trueを追加
これでプレハブとAnimatorの設定ができました。
ゾンビのプログラムの検討
さて、次にゾンビを移動させるプログラムを書くわけですが、一口にランダム移動といってもどういう風に移動させたらよいかを考えます。移動のさせ方は色々あると思いますが、今回は次のような手順でゾンビを動かすことにしました。
- ランダムな時間だけゾンビを待機させる。
- ゾンビをランダムに方向転換させる。
- ランダムな時間だけゾンビをまっすぐ移動させる。
- もしプレイヤーが一定速度以上でぶつかったらゾンビを倒れさせる。そうでなければ、1~3を繰り返す。
あとはこれをC#に落とし込むだけです。
ランダム移動するゾンビのプログラム
新しいC#スクリプト「ZombieController」を作成し、ゾンビにアタッチしたら次のプログラムを書いてください。
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 116 117 118 119 120 121 122 123 124 125 126 | using System.Collections; using System.Collections.Generic; using UnityEngine; public class ZombieController : MonoBehaviour { [SerializeField] float walkForce = 20.0f; [SerializeField] float minWalkTime = 3.0f; [SerializeField] float maxWalkTime = 5.0f; [SerializeField] float minIdleTime = 3.0f; [SerializeField] float maxIdleTime = 5.0f; [SerializeField] float minRotateAngle = -45.0f; [SerializeField] float maxRotateAngle = 45.0f; [SerializeField] float deadSpeed = 50.0f; [SerializeField] float walkAnimationSpeed = 1.0f; [SerializeField] float impulse = 300; bool isDead = false ; bool isWalk = false ; bool isCollision = false ; Animator animator; Rigidbody rigidBody; Rigidbody playerRigidBody; CapsuleCollider capsuleCollider; BoxCollider boxCollider; GameObject player; void Start() { //ゾンビ自身のコンポーネントを取得 animator = GetComponent<Animator>(); rigidBody = GetComponent<Rigidbody>(); capsuleCollider = GetComponent<CapsuleCollider>(); boxCollider = GetComponent<BoxCollider>(); //プレイヤーをタグで検索し、Rigidbodyを取得 player = GameObject.FindGameObjectWithTag( "Player" ); playerRigidBody = player.GetComponent<Rigidbody>(); StartCoroutine(ZombieBehavior()); } //歩く処理 void Walk() { //方向転換(Y軸ランダム回転) float angle = Random.Range(minRotateAngle, maxRotateAngle); transform.Rotate(0, angle, 0); animator.speed = walkAnimationSpeed; animator.SetBool( "IsWalk" , true ); } //待機処理 void Idle() { rigidBody.velocity = Vector3.zero; animator.speed = 1; animator.SetBool( "IsWalk" , false ); } //コルーチン IEnumerator ZombieBehavior() { while (isDead == false ) { float idleTime = Random.Range(minIdleTime, maxIdleTime); Idle(); yield return new WaitForSeconds(idleTime); float walkTime = Random.Range(minWalkTime, maxWalkTime); Walk(); isWalk = true ; yield return new WaitForSeconds(walkTime); isWalk = false ; } } void FixedUpdate() { if (isWalk) { rigidBody.AddForce(transform.forward * walkForce); } } //衝突判定 void OnCollisionEnter(Collision collision) { //一定速度以上の速度を出しているプレイヤーと接触したら倒れる //ただし、一度倒れたらこの処理はしない if (collision.gameObject.tag == "Player" && isCollision == false ) { float playerSpeed = playerRigidBody.velocity.magnitude; if (playerSpeed >= deadSpeed) { //倒れないようにしているのを解除 rigidBody.freezeRotation = false ; //吹っ飛ばす //※後で追加します //コライダーを切り替え capsuleCollider.enabled = false ; boxCollider.enabled = true ; //フラグON isDead = true ; isCollision = true ; animator.speed = 1; animator.SetBool( "IsDead" , true ); } } } } |
細かい処理の解説はプログラム中のコメントの通りです。下の画像のように動けば成功です。
次のページ→オブジェクトが吹っ飛ぶ処理の追加