今まではキーボードの移動キーを押したらキャラクターを移動させていました。
市販のゲーム機であればコントローラを使ってゲームをするのは当たり前ですが、
パソコンのゲームやスマートフォンのゲームでは専用のコントローラーではなくマウスを使った移動や画面を触って移動させる場合もあるかと思います。
そんなわけで今回はマウスクリック時にクリックした位置にキャラクターを移動させる機能を作成したいと思います。
また、マウスクリック時ではなく常にマウスを移動した位置にキャラクターを移動させたい場合もあると思うので、切り替えモードを作ります。
今までの記事をご覧いただいている方はキャラクターに設定しているアニメーターコントローラをそのまま使います。
キャラクター操作スクリプトは新しく作りますので、キーボード操作で動かしていたキャラ操作スクリプトをオフにして、新しいスクリプトを作り設定してください。
もちろん元々のスクリプトを改造してマウスクリック操作に切り替えるのもいいと思います。
この記事からご覧頂いた方は、キャラクターにCharacterControllerとアニメーターコントローラが設定されている必要がありますので設定してください。
アニメーターコントローラーは
を参考にして作成し設定してください。
キャラクター操作スクリプトMouseMoveを作成する
キャラクターの設定が出来ていればあとはキャラクター操作スクリプトを作るだけです。
キャラクター操作スクリプトMouseMoveを作成します。
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 | using UnityEngine; using System.Collections; public class MouseMove : MonoBehaviour { // レイを飛ばす距離 private float rayRange = 100f; // 移動する位置 private Vector3 targetPosition; // 速度 private Vector3 velocity; // 移動スピード [SerializeField] private float moveSpeed = 1.5f; // マウスクリックで移動する位置を決定するかどうか [SerializeField] private bool isMouseDownMode = true; // スムースにキャラクターの向きを変更するかどうか [SerializeField] private bool smoothRotateMode = true; // 回転度合い [SerializeField] private float smoothRotateSpeed = 180f; private CharacterController characterController; private Animator animator; void Start () { characterController = GetComponent<CharacterController>(); animator = GetComponent<Animator>(); targetPosition = transform.position; velocity = Vector3.zero; } void Update () { if(characterController.isGrounded) { velocity = Vector3.zero; // マウスクリックまたはisMouseDownModeがOffの時マウスの位置を移動する位置にする if(Input.GetButton("Fire1") || !isMouseDownMode) { Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition); RaycastHit hit; if(Physics.Raycast(ray, out hit, rayRange, LayerMask.GetMask ("Field"))) { targetPosition = hit.point; } } // 移動の目的地と0.1mより距離がある時は速度を計算 if(Vector3.Distance(transform.position, targetPosition) > 0.1f) { var moveDirection = (targetPosition - transform.position).normalized; velocity = new Vector3(moveDirection.x * moveSpeed, velocity.y, moveDirection.z * moveSpeed); // スムースモードの時は徐々にキャラクターの向きを変更する if(smoothRotateMode) { transform.rotation = Quaternion.RotateTowards(transform.rotation, Quaternion.LookRotation(new Vector3(moveDirection.x, 0, moveDirection.z)), smoothRotateSpeed * Time.deltaTime); // スムースモードでなければ一気に目的地の方向を向かせる } else { transform.LookAt(transform.position + new Vector3(moveDirection.x, 0, moveDirection.z)); } // アニメーションパラメータの設定 animator.SetFloat("Speed", moveDirection.magnitude); // 目的地に近付いたら走るアニメーションをやめる } else { animator.SetFloat("Speed", 0f); } } velocity.y += Physics.gravity.y * Time.deltaTime; characterController.Move(velocity * Time.deltaTime); } } |
まずはフィールドの宣言部分から見ていきます。
1 2 3 4 5 6 7 8 9 10 11 | // マウスクリックで移動する位置を決定するかどうか [SerializeField] private bool isMouseDownMode = true; // スムースにキャラクターの向きを変更するかどうか [SerializeField] private bool smoothRotateMode = true; // 回転度合い [SerializeField] private float smoothRotateSpeed = 180f; |
isMouseDownModeはマウスクリックで移動の位置を決定するかどうかで、このモードがオフだとマウスクリックする必要はなく、マウスを動かした位置に移動します。
smoothRotateModeは移動先の方向にキャラクターを向ける時、徐々に角度を変えるかどうかです。
オフだと一気に移動先の方向を向きます。
smoothRotateSpeedは1秒間にどれだけの角度を変更するかの度合いです。
次に移動の条件部分を見ていきます。
1 2 3 | if(Input.GetButton("Fire1") || !isMouseDownMode) { |
ここではInput.GetButton(“Fire1”)を使用して攻撃ボタン(デフォルトはマウスの左クリック)が押された時にしていますが、マウスの左クリックと限定したい場合は
1 2 3 | Input.GetMouseButtonDown(0) |
とします。
1 2 3 4 5 6 7 8 9 10 | // マウスクリックまたはisMouseDownModeがOffの時マウスの位置を移動する位置にする if(Input.GetButton("Fire1") || !isMouseDownMode) { Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition); RaycastHit hit; if(Physics.Raycast(ray, out hit, rayRange, LayerMask.GetMask ("Field"))) { targetPosition = hit.point; } } |
移動する目的地を設定する部分です。
1 2 3 4 5 | if(Physics.Raycast(ray, out hit, rayRange, LayerMask.GetMask ("Field"))) { targetPosition = hit.point; } |
Physics.Raycastはある位置からある方向にレイ(線)を飛ばし何か他のゲームオブジェクトにぶつかったかどうかを調べます。
何かにぶつかった時は戻り値にtrueが返ってくるので、ぶつかった位置をtargetPositionに設定します。
Physics.Raycastの引数について説明します。
最初の引数rayはif文実行前に作成していて、このクラスで、ある位置とある方向を設定します。
レイのクラスの作成は
1 2 3 | Ray ray = new Ray(ある位置,ある方向); |
としてレイを飛ばし始める位置と飛ばす方向を指定し作成します。
ですが、Physics.Raycastの第1引数はRay型の物を指定すればいいので、そのやり方でなくても指定は出来ます。
今回の場合は
1 2 3 | Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition); |
を使っています。
1 2 3 | カメラ.ScreenPointToRay(飛ばす位置); |
は指定したカメラから引数で指定した飛ばす位置にレイを飛ばします。
戻り値がRay型で返ってくるので、これを使用します。
今回の場合はメインカメラ(Camera.mainで取得出来る)からマウスの位置(Input.mousePositionで取得出来る)のRayを作成しています。
hitはRaycastHit型の変数でPhysics.Raycastの引数に指定するとぶつかった相手の情報がhitに代入されます。
rayRangeはレイを飛ばす距離です。
今回は100mにしていますが、値を小さくすると近い場所しか反応しないような仕様にする事が出来ます。
Physics.Raycastはいくつかコンストラクタが用意されており設定する引数の型や個数も変わります。
Ray型の引数を作成せず、飛ばす位置と方向を別々に指定したり、特定のレイヤーに設定されたゲームオブジェクトのみレイの当たり判定をするという事も出来ます。
最後の引数でレイが当たったゲームオブジェクトのレイヤーがFieldである時だけ反応するようにしています。
Physics.Raycastに関しては
も参考にしてください。
位置から方向を計算する処理、位置?方向?わけわからん!
キャラクターの向きに関する処理を見てみます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | // 移動の目的地と0.1mより距離がある時は速度を計算 if(Vector3.Distance(transform.position, targetPosition) > 0.1f) { var moveDirection = (targetPosition - transform.position).normalized; velocity = new Vector3(moveDirection.x * moveSpeed, velocity.y, moveDirection.z * moveSpeed); // スムースモードの時は徐々にキャラクターの向きを変更する if(smoothRotateMode) { transform.rotation = Quaternion.RotateTowards(transform.rotation, Quaternion.LookRotation(new Vector3(moveDirection.x, 0, moveDirection.z)), smoothRotateSpeed * Time.deltaTime); // スムースモードでなければ一気に目的地の方向を向かせる } else { transform.LookAt(transform.position + new Vector3(moveDirection.x, 0, moveDirection.z)); } // アニメーションパラメータの設定 animator.SetFloat("Speed", moveDirection.magnitude); // 目的地に近付いたら走るアニメーションをやめる } else { animator.SetFloat("Speed", 0f); } |
1 2 3 4 | moveDirection = (targetPosition - transform.position).normalized; velocity = new Vector3(moveDirection.x * moveSpeed, velocity.y, moveDirection.z * moveSpeed); |
最初に移動先の位置から自分自身の位置を引いた位置のnormalizedプロパティをmoveDirectionに代入します。
移動先の位置-自分自身の位置
で移動先の方向が算出出来ます。
その方向のnormalizedプロパティを指定するとベクトルが正規化されて方向だけが算出出来ます。
velocityは向いている方向にmoveSpeedをかけた値を設定しますが、Y座標だけは重力のみの移動にする為velocity.yを指定します。
スムーズにキャラクターの向きを変える場合は、現在の向いている角度から移動先の角度に少しづつ回転させます。
Quaternion.RotateTowardsを使うとスムーズに角度を計算してくれるので、その値を自分自身の角度に代入します。
1 2 3 | new Vector3(moveDirection.x, 0, moveDirection.z) |
とY座標を0としているのは、Y座標をmoveDirectionの値で計算してしまうと、移動先の方向が高い位置にあった場合キャラクターが上の方を向いてしまうからです。
分かりづらい方は
1 2 3 | Quaternion.RotateTowards(transform.rotation, Quaternion.LookRotation(moveDirection)) |
を指定して高い所をマウスクリックして試してみるといいかもしれません。
スムーズに移動させない場合は
1 2 3 | transform.LookAt(transform.position + new Vector3(moveDirection.x, 0, moveDirection.z)); |
とキャラクターを即座に移動先の方向に向かせています。
ここで方向について考えたいと思います。
目的地をtargetというVector3型の変数で宣言されているとします。
主人公の位置はtransform.positionで取得出来ます。主人公から目的地の方向を調べたい時は
target – transform.position
で計算出来ます。
方向と位置は両方ともVector3で取得するので混乱しますが、「方向」もよく考えれば「そちら方の位置」を表しているのでそうなります。
相手の方向=相手の位置 – 自分の位置
と考えるとわかりやすくなります。
transform.LookAtは引数に向かせたい位置を指定します。例えば敵キャラをtekiという名前にしていたら
主人公を敵キャラの方に向かせたい場合
1 2 3 | transform.LookAt(teki.transform.position) |
と指定します。
角度をスムーズに変更する時に使用している
Quaternion.LookRotation
は引数に指定した位置の方向を計算します。(Z方向がUnityでは向いている方向になる)
今回であれば
1 2 3 | Quaternion.LookRotation(Vector3(moveDirection.x, 0, moveDirection.z)) |
としています。
引数にはY座標を0とした移動先の位置を指定しています。
移動先の位置を指定すれば移動先の向いている方向が計算されます。
位置を指定して向いている方向!?何言ってんだ!!となるかもしれませんが、UnityではZ方向が向いている方向になりますのでこれでmoveDirection(移動先の方向)の方向が計算出来ます。
分かりづらくなるのは引数にVector3で指定しているからだと思います。
元々はmoveDirectionに計算した移動先の向きが入っているはずだからと考えればわかるかと思います。
TransformのインスペクタではX、Y、Zとなっていますが、この値を直接操作するよりQuaternion値で操作するみたいです。
マウスクリックで移動させる機能が完成したので確認する
これで移動スクリプトが完成したので、キャラクターに追加してインスペクタに変数の値をセットします。
moveSpeedに1.5
isMouseDownModeにチェック
smoothRotateModeにチェック
smoothRotateSpeedを180(1秒間に180度回転する)
にしてUnityの実行ボタンを押します。
上のようにマウスをクリックした位置に移動し、キャラクターの向く方向がなめらかに変更されます。
上の画像はsmoothRotateModeをOffにした場合です。
キャラクターの向きが一気に変わっています。
isMouseDownModeをオフにするとマウスをクリック(厳密には押した時点)しなくてもマウスの位置にキャラクターが移動します。
このモードにするとキャラクターが止まる事はないので永遠に動き続けます。
またアニメーションもずっと走っている状態になります。
上の動画ではマウス操作で移動しているかは確認出来ないので、試してみてください(^^)/