今回は自身のサーチエリア内に敵がいて特定のボタンを押した時にその敵の方向を向き体の向きも変えてみようと思います。
例えば敵が自分の近くにいる時に剣や銃を構えるボタンを押した時にその敵の方向に体を回転させ敵の方向を向くようにします。
以前の記事でも同じような機能を作成しましたが、今回はより簡素にした機能になります。
以前の記事は
になります。
こちらは他の機能に追記してる形になるのでちょっとわかり辛いかもしれませんね・・・。
動かすキャラクターを作成する
まずはゲームステージを移動するキャラクターを作成します。
モデルの配置とコンポーネントの取り付け
モデルを配置しCharacterControllerとRotateMoveという名前のスクリプトを取りつけます。
↑のようにモデルを配置します。
モデルにはCharacterControllerを取りつけコライダのサイズを調整します。
↑がコライダのサイズを調整した時の画像です。
キャラクター操作スクリプトRotateMoveを作成する
新しくRotateMoveというスクリプトを作り取りつけます。
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 | using UnityEngine; using System.Collections; public class RotateMove : MonoBehaviour { private Animator animator; private CharacterController cCon; private Vector3 velocity; private Vector3 input; // 見るターゲット public Transform target; // 歩くスピード [SerializeField] private float walkSpeed = 2f; // キャラクターを回転させるスピード [SerializeField] private float rotateSpeed = 2f; void Start () { animator = GetComponent<Animator>(); cCon = GetComponent<CharacterController>(); velocity = Vector3.zero; } void Update () { // キャラクターコライダが接地、またはレイが地面に到達している場合 if(cCon.isGrounded) { velocity = Vector3.zero; input = new Vector3(Input.GetAxis("Horizontal"), 0f, Input.GetAxis("Vertical")); // 方向キーが多少押されている if(input.magnitude > 0f) { animator.SetFloat("Speed", input.magnitude); transform.LookAt(transform.position + input); velocity += input.normalized * walkSpeed; // キーの押しが小さすぎる場合は移動しない } else { animator.SetFloat("Speed", 0f); } // マウスの右ボタンを押したら向きを変える if(Input.GetMouseButton(1) && target != null) { Vector3 targetDir = new Vector3(target.position.x, transform.position.y, target.position.z) - transform.position; Vector3 newDir = Vector3.RotateTowards(transform.forward, targetDir, rotateSpeed * Time.deltaTime, 0f); transform.rotation = Quaternion.LookRotation(newDir); } } velocity.y += Physics.gravity.y * Time.deltaTime; cCon.Move(velocity * Time.deltaTime); } void OnAnimatorIK() { if(target != null) { animator.SetLookAtWeight (0.5f, 0.3f, 0.5f, 1f, 0.5f); animator.SetLookAtPosition(target.position); } } public void SetTarget(Transform target) { this.target = target; } } |
RotateMoveスクリプトはキャラクターを移動させるのとtargetがnullでなければそちらの方向にキャラクターを向ける処理です。
1 2 3 4 5 6 7 8 9 | // マウスの右ボタンを押したら向きを変える if(Input.GetMouseButton(1) && target != null) { Vector3 targetDir = new Vector3(target.position.x, transform.position.y, target.position.z) - transform.position; Vector3 newDir = Vector3.RotateTowards(transform.forward, targetDir, rotateSpeed * Time.deltaTime, 0f); transform.rotation = Quaternion.LookRotation(newDir); } |
マウスの右ボタンを押した時でかつターゲットが設定されている時に実行します。
ターゲットのY軸だけを自身のY軸の位置にして体全体が上を向いてしまわないようにしています。
targetDirはターゲットの方向を取得し、transform.forward(自身の前方の向き)をtargetDirに少しづつ回転させています。
その方向からQuaternion.LookRotationを使ってQuaternion値を取得しtransform.rotationに入れています。
その為少しづつキャラクターがターゲットの方向を向くようになります。
1 2 3 4 5 6 7 8 9 10 11 12 | void OnAnimatorIK() { if(target != null) { animator.SetLookAtWeight (0.5f, 0.3f, 0.5f, 1f, 0.5f); animator.SetLookAtPosition(target.position); } } public void SetTarget(Transform target) { this.target = target; } |
OnAnimatorIKメソッドはAnimatorのLayerでIK Passにチェックを入れると呼ばれるようになるメソッドです。
↑のようにAnimatorのBase Layerの歯車をクリックしIK Passにチェックを入れます。
SetTargetメソッドは敵をサーチするスクリプトから呼ばれて実行するメソッドで、受け取った引数をtargetに入れます。
敵をサーチするエリアとスクリプトを作成する
次は敵をサーチするエリアを作成します。
↑のように操作キャラクターの子要素に空オブジェクトを作成し名前をSearchAreaとします。
SearchAreaにはSphere Colliderを取りつけIs Triggerにチェックを入れます。
Spherer Colliderのサイズを変更しサーチするエリアを設定します。
↑のように円状のサーチエリアが完成しました。
次に敵の侵入を検知するスクリプトSearchTargetを作成します。
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 | using UnityEngine; using System.Collections; public class SearchTarget : MonoBehaviour { // RotateMoveスクリプトを入れる private RotateMove rotateMove; void Start() { // 自身の親要素からRotateMoveスクリプトを取得 rotateMove = GetComponentInParent<RotateMove>(); } // 敵が侵入したらターゲットにその敵を設定 void OnTriggerEnter(Collider col) { if(col.tag == "Enemy") { rotateMove.SetTarget(col.transform); } } // 敵が出ていったらターゲットをnullにする void OnTriggerExit(Collider col) { if(col.tag == "Enemy") { rotateMove.SetTarget(null); } } } |
OnTriggerEnterとOnTriggerExitを使い敵がサーチエリアに侵入したか?出て行ったか?をチェックしています。
OnTriggerEnterをOnTriggerStayにすると常時敵の侵入を監視出来ます。
実際にターゲットを指定するのはRotateMoveスクリプトのSetTargetメソッドなので引数を与えて呼び出しています。
これで操作キャラクターの機能は完成しました。
ターゲットである敵キャラクターを設置する
ターゲットである敵キャラクターを何体か設置しましょう。
今回はモデルを配置しCharacterControllerを取りつけコライダを調整しただけにします。
またSearchTargetスクリプトで敵を把握するのにタグのEnemyであるかどうかで判定していますので、敵キャラクターにはEnemyタグを設定しておきます。
↑のように設定しました。
敵キャラクターは動かさずそのままの位置に置いておきます。
↑のようにあらかじめ空中と地面に接地するぐらいの位置においておきます。
操作キャラクターを動かし敵キャラクターの方を向くか確認する
それでは機能が完成したので操作キャラクター(ゾンビ)で敵キャラクター(こちらもゾンビ)に近づけターゲットに敵キャラクターが設定されたら、
マウスの右ボタンを押して敵キャラクターの方を向くかどうか確認します。
敵キャラクターが近くにいる時にマウスの右ボタンを押すとそちらの方向に操作キャラクターが向いていますね。
ターゲットが設定されるとすぐにIKが働き体の向きが変わっていますがこれはマウスの右ボタンを押している時のみ作用するようにした方がいいかもしれませんね。
1 2 3 4 | // マウスの右ボタンを押しているかどうか private bool mouseDown = false; |
↑のようにマウスの右ボタンが押されているかどうかをbool型の変数で宣言します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | // マウスの右ボタンを押したら向きを変える if(Input.GetMouseButton(1)) { mouseDown = true; if(target != null) { Vector3 targetDir = new Vector3(target.position.x, transform.position.y, target.position.z) - transform.position; Vector3 newDir = Vector3.RotateTowards(transform.forward, targetDir, rotateSpeed * Time.deltaTime, 0f); transform.rotation = Quaternion.LookRotation(newDir); } } else { mouseDown = false; } |
1 2 3 4 5 6 7 8 | void OnAnimatorIK() { if(target != null && mouseDown) { animator.SetLookAtWeight (0.5f, 0.3f, 0.5f, 1f, 0.5f); animator.SetLookAtPosition(target.position); } } |
マウスの右ボタンが押されている時はmouseDownにtrue、そうでない時はfalseを入れます。
OnAnimatorIKの条件でmouseDownがtrueであるというのを追加すればマウスの右ボタンを押している時だけそちらの方向に体が向くようになります。
終わりに
今回のスクリプトをそのままゲームに使おうと思うと複数の敵がサーチエリアにいる時に動作がおかしくなります。
ここら辺の処理は最初に紹介した記事
の中で対処しているのでそちらも参考にしてください。
細かい部分では足りない所がありますが敵が近くにいる時に武器を構えるボタン(今回はマウスの右ボタンを押した時)を押すと敵の方向を向かせる事が出来ました。
以前の記事よりはわかりやすいかも!?