2015年03月10日

FPS作成--(3)プレイヤーの向き変更




ゲーム「FPS(ファーストパーソン・シューティング)」をUnityで作成致します。
今回はプレイヤーの向きの制御を行います。


プレイヤーの向き


まずはプレイヤーの正面が分かるように細工致します。

左上メニューから、
  GameObject → 3D Object → Sphere
を選択。

Sphereの名前を「 Nose 」(鼻)に変更。

「 Nose 」を「 Player 」の中に放り込み、親子関係にする。
※これにより、NoseオブジェクトはPlayerの一部として扱われるようになる。


「 Nose 」のTransformを以下に変更。
  ・Postion ( x , y , z ) = ( 0 , 0 , 0.5)
  ・Scale ( x , y , z ) = ( 0.2 , 0.2 , 0.2)

これで、プレイヤーに鼻が付きました







では、プレイヤーの向きの制御に入ります。
実際のプログラムは説明の後で一括で書きます。


まず、プレイヤーを向かせたい方向の取得ですが、
実は既にとある行でその方向が手に入っています。
  move = new Vector3(Input.GetAxis("Horizontal") , 0.0f , Input.GetAxis("Vertical"));
という行です。

何故なら、ここで取得している値は、
プレイヤーがこれから向かおうとしている方向でもあるのです。

なので、方向を取得したい場合は、この時のMoveの値を利用してあげれば問題ありません。

新しく変数を作って、この時の値を取得しておきましょう。
  Vector3 playerDir = move;

ここで間違ってはならないのが、重力方向の移動であるyの数値が0である事です。
方向ベクトルは、視点から終点までの向きなので、
もしyの値も方向ベクトルの取得に入っていると、プレイヤーは下方向にも回転しようとしてしまいます。




プレイヤーの向きたい方向が取得できたので、
今度はその値を変換してプレイヤーの向きに代入してやります。

ここで、ただ単に代入しただけでは、プレイヤーの向きは一瞬で変わってしまいます。
なので『現在の角度 → 目的の角度』へと、じわ〜っと変わるように細工を致します。

プログラムに直すと、以下の用になります。
  Quaternion q = Quaternion.LookRotation(playerDir);
  transform.rotation = Quaternion.RotateTowards(transform.rotation , q , 360.0f * Time.deltaTime);

上記1行目では、向きたい方角をQuaternionn型に直しています。

上記2行目では、プレイヤーの角度transform.rotationに、
Quaternion.RotateTowards()で向きを変えています。
第一引数が、変更前の角度。(transform.rotation)
第二引数が、目的の角度。(q)
第三引数が、変化量です。(ここではTime.deltaTime に360度を乗算しています)

上記の場合、第三引数は1秒間で360度回転する早さで向きが変わると考えてOKです。



尚、Quaternion.RotateTowards()ではなくQuaternion.Lerp()でも同様の事が行えます。
但し、Quaternion.Lerp()の場合は第三引数は0〜1の間となり、
第一引数を0、第二引数を1とした場合、第三引数で指定した値へ変化させるという関数です。
なので、1秒間に?度回転させる早さ……という考え方ではないため注意してください。





さて、プレイヤーの向きはこれで変更する事が出来ました。
但しこの状態でゲームを実行すると、Unityから少し注意を受けてしまいます。
理由は、
  Quaternion q = Quaternion.LookRotation(playerDir);
において、playerDirがゼロなので、
LookRotation……つまり、見る方角がありませんよという指摘です。

これを解決するためには、playerDirの値が一定値以上の場合にのみ、
プレイヤーの方向転換処理を行うようにしてやればOKです。


playerDirは方角を示す値として使用していますが、
当然ながらそれは現在地からの距離という値にも変換する事が出来ます。
その距離を取得したい場合は、
  playerDir.magnitude
と書きます。

Vector3型の値は、後ろに .magnitude と書くことで純粋な距離(float型)に変換する事が出来ます。
あとはこの値をif文で「0.1f以上の時には〜〜せよ」みたいに書けばOKです。



以上を踏まえて、プログラムを書きます。

using UnityEngine;
using System.Collections;

public class C01_Player : MonoBehaviour {
private CharacterController charaCon; // キャラクターコンポーネント用の変数
private Vector3 move = Vector3.zero; // キャラ移動量.
private float speed = 5.0f; // 移動速度
private float jumpPower = 10.0f; // 跳躍力.
private const float GRAVITY = 9.8f; // 重力
private float rotationSpeed = 180.0f; // プレイヤーの回転速度

void Start(){
charaCon = GetComponent< CharacterController >();
}

void Update () {
// ▼▼▼移動量の取得▼▼▼
float y = move.y;
move = new Vector3(Input.GetAxis("Horizontal") , 0.0f , Input.GetAxis("Vertical")); // 左右上下のキー入力を取得し、移動量に代入.
Vector3 playerDir = move; // 移動方向を取得.
move *= speed; // 移動速度を乗算.

// ▼▼▼重力/ジャンプ処理▼▼▼
move.y += y;
if(charaCon.isGrounded){ // 地面に設置していたら
if(Input.GetKeyDown(KeyCode.Space)){ // ジャンプ処理.
move.y = jumpPower;
}
}
move.y -= GRAVITY * Time.deltaTime; // 重力を代入.

// ▼▼▼プレイヤーの向き変更▼▼▼
if(playerDir.magnitude > 0.1f){
Quaternion q = Quaternion.LookRotation(playerDir); // 向きたい方角をQuaternionn型に直す .
transform.rotation = Quaternion.RotateTowards(transform.rotation , q , rotationSpeed * Time.deltaTime); // 向きを q に向けてじわ〜っと変化させる.
}

// ▼▼▼移動処理▼▼▼
charaCon.Move(move * Time.deltaTime); // プレイヤー移動.
}
}

最初に、プレイヤーの回転速度であるrotationSpeedを追加しています。

次に、方向キーでmoveの値を取得した際に、 plyerDirに格納しています。

プレイヤーの向き変更をすぐその場で書いても良いのですが、
ここは処理を分けるために、重力処理の後に持ってきています。
こうしておけば、後で関数化して処理を分かりやすくする事が出来ます。

また、ジャンプ中は向きを変えないようにしたい場合には、
  plyerDir.magnitude > 0.1f && charaCon.isGrounded
という条件式に変えてやればOKです。
移動中であり、着地中である場合のみプレイヤーは方向転換するようになります。


では、ゲームを実行してみましょう。



プレイヤーの向きが移動方向に合わせて変わるのが分かると思います。







今回の説明に関しては以上です。




前回 : FPS作成--(2)着地とジャンプ
次回 : 番外編--サイコロ作成

Unity初心者の為の記事一覧へ
Unityで製作したゲーム一覧へ
ブログTOPへ














posted by 漆之黒褐 at 10:07| Comment(4) | TrackBack(0) | Unity | このブログの読者になる | 更新情報をチェックする
この記事へのコメント
Unityを始めたばかりの初心者です。
よくキャラクターの操作をベクトルで扱いtransformで移動させたりする解説記事は多いのですが、なかなか進行方向にキャラを回転させることについて解説が見つからなくて…(ggrのが下手なだけでしょうか…)

当サイトで無事解決しました。

ありがとうございました!
Posted by at 2015年07月02日 00:25
Unityの回転は、画面上(Inspector)では rotation と表示されてVector3型の角度で考えられるのに、実際にプログラムしようと思うとrotation変数はQuaternion型(4次元)となっていててアレ?と思う方は多いです。私もそうでしたし。

単純に、現在の角度から指定分だけ変化させたい場合、
transform.localEulerAngles
という変数が使えます。
(transform.positionみたいに使えます)

transform.localEulerAngles += new Vector3(60.0f , 0 , 0);
と書けば、Xの角度を+60度します。

変数ではなく、transform.Rotate()もあります。こちらは
transform.Rotate(new Vector3(60.0f , 0 , 0));
ですね。


上記はただ向きを変えたい時に使えます。

ブログ本文の中で使われている方法の場合、向きたい方向を取得しているので、例えば敵キャラのAIに組み込むと、向く方向をプレイヤーの位置として渡してやる事で、敵はプレイヤーのいる方向まで回転して止まるという事が出来ます。

回転関係の処理は色々悩む事もあると思いますが、これからも頑張って下さい。


Posted by 漆之黒褐 at 2015年07月02日 18:13
こちらのGetComponent();には何を入れればいいのでしょう?
Posted by at 2015年08月02日 04:43
すみません、修正がちょっとまにあってません……。

基本的に、変数を宣言した時の型を入れれば問題有りません。
Posted by 漆之黒褐 at 2015年08月02日 07:01
コメントを書く
お名前:

メールアドレス:

ホームページアドレス:

コメント:


この記事へのトラックバック