前回はキャラクターの移動と走る動作の作成まで完了しました。
今回はキャラクターの向きの変更と視点であるカメラの向きを上下に動かせるようにしたいと思います。
キャラクターの向きとカメラの視点の上下を変更するやり方を考える
まずはどのように向きを変更するかを考えます。
キャラクターの向きの変更や視点の上下は現状ではゲームパッドに対応していないのでマウスの移動のみで行うようにします。
マウスを左右に移動させた時はキャラクターのY軸の角度を変更してキャラクターが回転するようにします。
マウスを前後に移動させた時は視点(カメラの角度)のX軸の角度を変更してキャラクターが上を見たり下を見たりしているようにします。
キャラクターの回転の角度は制限を与えずずっと回転出来るようにします。
一方、視点の上限に関してはインスペクタで制限値を与え、決められた角度以上に上を向いたり下を向いたりしないようにします。
キャラクターの回転とカメラの回転機能を追加
キャラクターの回転とカメラの回転する機能を追加します。
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 UnityEngine; using System.Collections; using UnityEngine.UI; public class MyChara : MonoBehaviour { //キャラクターコントローラー private CharacterController cCon; // キャラクターの速度 private Vector3 velocity; // Animator private Animator animator; // 歩くスピード [SerializeField] private float walkSpeed = 1.5f; // 走るスピード [SerializeField] private float runSpeed = 2.5f; // 走っているかどうか private bool runFlag = false; // キャラクター視点のカメラ private Transform myCamera; // キャラクター視点のカメラで回転出来る限度 [SerializeField] private float cameraRotateLimit = 30f; // カメラの上下の移動方法。マウスを上で上を向く場合はtrue、マウスを上で下を向く場合はfalseを設定 [SerializeField] private bool cameraRotForward = true; // カメラの角度の初期値 private Quaternion initCameraRot; // キャラクター、カメラ(視点)の回転スピード [SerializeField] private float rotateSpeed = 2f; // カメラのX軸の角度変化値 private float xRotate; // キャラクターのY軸の角度変化値 private float yRotate; // マウス移動のスピード [SerializeField] private float mouseSpeed = 2f; // キャラクターのY軸の角度 private Quaternion charaRotate; // カメラのX軸の角度 private Quaternion cameraRotate; void Start () { //キャラクターコントローラの取得 cCon = GetComponent<CharacterController>(); animator = GetComponent <Animator> (); myCamera = GetComponentInChildren<Camera>().transform; // キャラクター視点のカメラの取得 initCameraRot = myCamera.localRotation; charaRotate = transform.localRotation; cameraRotate = myCamera.localRotation; } void Update () { // キャラクターの向きを変更する RotateChara(); // 視点の向きを変える RotateCamera(); // キャラクターコントローラのコライダが地面と接触してるかどうか if(cCon.isGrounded) { velocity = Vector3.zero; velocity = new Vector3 (Input.GetAxis ("Horizontal"), 0f, Input.GetAxis ("Vertical")); // 走るか歩くかでスピードを変更する float speed = 0f; if(Input.GetButton("Run")) { runFlag = true; speed = runSpeed; } else { runFlag = false; speed = walkSpeed; } velocity *= speed; if(velocity.magnitude > 0f) { if (runFlag) { animator.SetFloat ("Speed", 2.1f); } else { animator.SetFloat ("Speed", 1f); } } else { animator.SetFloat("Speed", 0f); } } velocity.y += Physics.gravity.y * Time.deltaTime; // 重力値を計算 cCon.Move(velocity * Time.deltaTime); // キャラクターコントローラのMoveを使ってキャラクターを移動させる } // キャラクターの角度を変更 void RotateChara() { // 横の回転値を計算 float yRotate = Input.GetAxis ("Mouse X") * mouseSpeed; charaRotate *= Quaternion.Euler(0f, yRotate, 0f); // キャラクターの回転を実行 transform.localRotation = Quaternion.Slerp(transform.localRotation, charaRotate, rotateSpeed * Time.deltaTime); } // カメラの角度を変更 void RotateCamera() { float xRotate = Input.GetAxis("Mouse Y") * mouseSpeed; // マウスを上に移動した時に上を向かせたいなら反対方向に角度を反転させる if(cameraRotForward) { xRotate *= -1; } // 一旦角度を計算する cameraRotate *= Quaternion.Euler(xRotate, 0f, 0f); // カメラのX軸の角度が限界角度を超えたら限界角度に設定 var resultYRot = Mathf.Clamp (Mathf.DeltaAngle (initCameraRot.eulerAngles.x, cameraRotate.eulerAngles.x), -cameraRotateLimit, cameraRotateLimit); // 角度を再構築 cameraRotate = Quaternion.Euler (resultYRot, cameraRotate.eulerAngles.y, cameraRotate.eulerAngles.z); // カメラの視点変更を実行 myCamera.localRotation = Quaternion.Slerp(myCamera.localRotation, cameraRotate, rotateSpeed * Time.deltaTime); } } |
キャラクターの回転とカメラの視点の上下を変更する方法を見ていく
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 | // キャラクター視点のカメラ private Transform myCamera; // キャラクター視点のカメラで回転出来る限度 [SerializeField] private float cameraRotateLimit = 30f; // カメラの上下の移動方法。マウスを上で上を向く場合はtrue、マウスを上で下を向く場合はfalseを設定 [SerializeField] private bool cameraRotForward = true; // カメラの角度の初期値 private Quaternion initCameraRot; // キャラクター、カメラ(視点)の回転スピード [SerializeField] private float rotateSpeed = 2f; // カメラのX軸の角度変化値 private float xRotate; // キャラクターのY軸の角度変化値 private float yRotate; // マウス移動のスピード [SerializeField] private float mouseSpeed = 2f; // キャラクターのY軸の角度 private Quaternion charaRotate; // カメラのX軸の角度 private Quaternion cameraRotate; |
でキャラクターと視点の角度で使用するフィールドを宣言しています。
cameraRotateLimitは視点が上下する時の最大の角度で、インスペクタで設定出来ます。無限に角度が変更出来ると人間ではない事になるので・・・・((+_+))
cameraRotForwardはマウスの動きに対して視点が同じ方向に移動するか逆方向に移動するかの設定です。
initCameraRotはマウス操作で角度を変更した時に元の角度からどのぐらい変わったか判定する時に初期のカメラ角度を保存しておくフィールドです。
rotateSpeedはキャラクターの角度や視点の角度の変更スピードでインスペクタで設定出来るようにします。
charaRotationはキャラクターの角度、cameraRotationはカメラの角度を入れます。
Updateメソッド内でRotateCharaとRotateCamera関数を呼びだし、キャラクターの角度とカメラの角度を変更します。
1 2 3 4 5 6 7 8 | void Update () { // キャラクターの向きを変更する RotateChara(); // 視点の向きを変える RotateCamera(); |
以前はCharacterControllerのisGroundedプロパティでキャラクターが地面と接地している時だけ回転するようにしてましたが、それだと回転中にisGroundedがfalseになる事があった為やめました。
1 2 3 4 5 6 7 8 9 10 11 12 | // キャラクターの角度を変更 void RotateChara() { // 横の回転値を計算 float yRotate = Input.GetAxis ("Mouse X") * mouseSpeed; charaRotate *= Quaternion.Euler(0f, yRotate, 0f); // キャラクターの回転を実行 transform.localRotation = Quaternion.Slerp(transform.localRotation, charaRotate, rotateSpeed * Time.deltaTime); } |
RotateCharaではマウスの横軸の移動値を計算し、元の角度にかける事で目的の角度を計算します。
目的の角度が計算出来たらQuaternion.Slerpを使って徐々に現在の角度から目的の角度に変更しています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | // カメラの角度を変更 void RotateCamera() { float xRotate = Input.GetAxis("Mouse Y") * mouseSpeed; // マウスを上に移動した時に上を向かせたいなら反対方向に角度を反転させる if(cameraRotForward) { xRotate *= -1; } // 一旦角度を計算する cameraRotate *= Quaternion.Euler(xRotate, 0f, 0f); // カメラのX軸の角度が限界角度を超えたら限界角度に設定 var resultYRot = Mathf.Clamp (Mathf.DeltaAngle (initCameraRot.eulerAngles.x, cameraRotate.eulerAngles.x), -cameraRotateLimit, cameraRotateLimit); // 角度を再構築 cameraRotate = Quaternion.Euler (resultYRot, cameraRotate.eulerAngles.y, cameraRotate.eulerAngles.z); // カメラの視点変更を実行 myCamera.localRotation = Quaternion.Slerp(myCamera.localRotation, cameraRotate, rotateSpeed * Time.deltaTime); } |
RotateCameraではマウスの縦軸の移動値を計算し、限界角度を越えていたらカメラの角度を限界角度に変更し直しています。
複雑な計算をしているように見えますが・・・、視点の変更を設定値内に収める計算をしているだけです。
これでキャラクターの向きと視点の上下の機能が出来たので、MyCharaのインスペクタにパラメータを設定します。
限界角度cameraRotateLimitを30にし、cameraRotForwardにチェックを入れてカメラの移動方向がマウスの移動方向と同じになるようにします。
キャラクターの移動機能の作りなおし・・・・(T_T)
回転の設定も出来たので、Unityの実行ボタンを押して動くか確認してみましょう。
おおーガッデム!(謎)
キャラクターの回転も出来、視点の上下も制限角度内でちゃんと動いているようですが、キャラクターを回転させた後にキャラクターを移動させると、
違う方向に移動してしまいます。
上の実行結果でもキャラクターを回転させた後に↓のキーを押すとキャラクターの左側に進んでしまっています。
あああああへたこいたああああ((+_+))、ドゥルルンドゥルルンドゥルルンドゥルルン、ウェーイ♪
(小島よしお好きだな・・・・・あ・・ご結婚おめでとうございます(^^)v)
この原因はもちろん移動の仕方です。
1 2 3 | velocity = new Vector3 (Input.GetAxis ("Horizontal"), 0f, Input.GetAxis ("Vertical")); |
現状は移動キーを押した方向をvelocityに設定しているので、キャラクターがどこを向いていようがいまいが、Unityの世界で言う正の方向と負の方向に移動してしまいます。
キャラクターの角度を変更する前はこれでよかったんですが、キャラクターの向きを変更したことによっておかしくなってしまいました。
本来であれば↑や↓のキーを押した時はキャラクターの向いている方向の前後、←や→を押した時はキャラクターの左右に移動。
という風にしなければいけません。
また、キャラクターが向きを変えている時は立っている状態で回転してしまっているので、足踏みをしながら回転するようにしてみます。
キャラクターの移動の修正と回転時のアニメーションの追加
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 | // キャラが回転中かどうか? private bool charaRotFlag = false; void Update () { // キャラクターの向きを変更する RotateChara(); // 視点の向きを変える RotateCamera(); // キャラクターコントローラのコライダが地面と接触してるかどうか if(cCon.isGrounded) { velocity = Vector3.zero; velocity = (transform.forward * Input.GetAxis ("Vertical") + transform.right * Input.GetAxis ("Horizontal")).normalized; // 走るか歩くかでスピードを変更する float speed = 0f; if(Input.GetButton("Run")) { runFlag = true; speed = runSpeed; } else { runFlag = false; speed = walkSpeed; } velocity *= speed; if(velocity.magnitude > 0f || charaRotFlag) { if (runFlag && !charaRotFlag) { animator.SetFloat ("Speed", 2.1f); } else { animator.SetFloat ("Speed", 1f); } } else { animator.SetFloat("Speed", 0f); } } velocity.y += Physics.gravity.y * Time.deltaTime; // 重力値を計算 cCon.Move(velocity * Time.deltaTime); // キャラクターコントローラのMoveを使ってキャラクターを移動させる } // キャラクターの角度を変更 void RotateChara() { // 横の回転値を計算 float yRotate = Input.GetAxis ("Mouse X") * mouseSpeed; charaRotate *= Quaternion.Euler(0f, yRotate, 0f); // キャラクターが回転しているかどうか? if(yRotate != 0f) { charaRotFlag = true; } else { charaRotFlag = false; } // キャラクターの回転を実行 transform.localRotation = Quaternion.Slerp(transform.localRotation, charaRotate, rotateSpeed * Time.deltaTime); } |
スクリプトを見ていきます。
1 2 3 4 | // キャラが回転中かどうか? private bool charaRotFlag = false; |
でキャラクターが方向を変えているかどうかを判別します。
RotateCharaに処理を加えます。
1 2 3 4 5 6 7 | if(yRotate != 0f) { charaRotFlag = true; } else { charaRotFlag = false; } |
マウスが左右に移動している場合はcharaRotFlagにtrueを入れ、それ以外はfalseを入れます。
また移動方法は
1 2 3 | velocity = (transform.forward * Input.GetAxis("Vertical") + transform.right * Input.GetAxis("Horizontal")).normalized; |
と変更します。
以前だと移動速度を正規化していなかった為、斜め方向の移動が大きくなっていましたので
全体をVector3のnormalizedプロパティで正規化した値をvelocityに入れるようにしました。
同じようなものでVector3のnormalize関数がありますが、こちらの場合元の値を書き換えてしまいます。
今回の場合は一時的な値として使うのでどちらでも問題はなさそうです。
transform.forwardはキャラクターの前方でそれにInput.GetAxis(“Vertical”)の値をかけ、
transform.rightはキャラクターの右方向でそれにInput.GetAxis(“Horizontal”)の値をかけて
キャラクターの向きに応じた移動方向と移動値を計算します。
1 2 3 4 5 6 7 8 9 10 11 | if(velocity.magnitude > 0f || charaRotFlag) { if (runFlag && !charaRotFlag) { animator.SetFloat ("Speed", 2.1f); } else { animator.SetFloat ("Speed", 1f); } } else { animator.SetFloat("Speed", 0f); } |
キャラクターが向きを変えている時は歩くアニメーションをさせたいので、移動している時以外にcharaRotFlagがOnの時も条件に入れます。
そのままだと走るボタンを押している時にキャラクターの向きを変えると走るアニメーションになってしまう(走るアニメーションでいいならいいですが)ので、
条件を加えてキャラクターが向きを変える時は歩くアニメーションにします。
これでキャラクターの回転とそのアニメーション、またキャラクターの向きによって移動方向を変える事が出来るようになりました。
キャラクターの回転と視点の角度変更の機能を作り終えて
キャラクターの回転と視点(カメラ)の上下の機能は簡単に実装出来そうで細かい部分で実は難しかったです。
キャラクターの回転自体はただキャラクターのY軸の角度を変更すればいいんですが、
カメラの角度の上下はただX軸の角度を変更出来るようにするだけだと、無制限に角度が変わってしまって人間ではない角度まで変わってしまいます・・・(^_^;)
そこで制限角度を設けて一定の角度以上にはカメラが向かないようにしました。
この機能がないとキャラクターの内部まで映し出してしまうほど回転して問題が出てきてしまいます。
現状角度を変更した時に限界角度の設定値によってはキャラクターの中身が見えてしまいますが、これは次回以降修正していきますので、ほうっておいてください。
実は視点の変更に関してはまだ問題を残しています。
のようにカメラをキャラクターのボーン内(頭のあたり)に設定し、ボーンの角度を変えてキャラクター自身の上半身の角度を変えるという方法がありますが、
今回はカメラの角度だけを変えキャラクターの上半身の角度を変えていません。
このままだと例えばオンライン対戦を実現しようと思った時に相手方のキャラが実際に攻撃をする方向とキャラクターが向いている方向が違うという不具合が発生します。
また、もし上半身の角度を変更出来たとして、
キャラクターの上半身の角度を変更する時にカメラをボーンの子要素に配置しておくとカメラが相対的に動くので簡単に視点の移動を実装出来そうですが、
親要素の位置や角度の相対値で動く為、キャラクターを歩かせるとカメラのブレが大きく視点が定まらなくなります。
今後、上半身の角度を変更する事や上半身の角度を変更することでカメラが歩く時に大きくブレてしまう時の対策やキャラクターの中身を表示してしまう問題の対処をしていきます。
これらはキャラクターのアニメーションを作成する記事以降に対処します。
次回はFPSゲーム用のジャンプ機能を作成していきます。