CharacterController

キャラの移動に便利なのがCharacterControllerだ。これを利用することで
地形の凸凹に沿って移動
壁にこするように沿って移動
登れる坂道の勾配を設定
などが簡単に実現することができる。今回はUnityちゃんのモデルをCharacterControllerを使って動かしてみよう。

新規プロジェクトの作成

1.新規にCharacterControllerLessonプロジェクトを作成する。
2.下のリンクからUnitychanパッケージをダウンロード、インポートする。(すでに持っている人はそれを利用すればよい)
http://unity-chan.com/download/download.php?id=UnityChan&v=1.2.1

ステージの作成

3.CreateEmptyからStageを原点に作成する。
4.Stageの子要素として、CubeからFloorを作成し、以下のようにtransformを設定する。

5.Stageの子要素として、CubeからSlopeを作成し、以下のようにtransformを設定する。

6.Slope複製からSlope(1)を作成し、以下のようにtransformを設定する。

7.Stageの子要素としてCubeからStepを作成する。以下のようにtransformを設定する。

8.Step複製からStep(1)を作成し、以下のようにtransformを設定する。

9.表面材質を適当に設定し、カメラを調整しよう。

Unityちゃんの配置

10.modelsフォルダに入っているunitychanをシーンに配置する。

11.transformを以下のように調整する。

カメラの設定

12.CameraController.csを以下のように作成し、カメラにアタッチする。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class CameraController : MonoBehaviour {
	public Transform player;

	void LateUpdate () {
		transform.position = player.position + (-player.forward * 3.0f) + (player.up * 1.0f);
		transform.LookAt (player.position+Vector3.up);
	}
}

13.インスペクターからplayerにUnitychanを登録する。

14.実行してみよう。以下のように表示されれば成功だ。

AnimatorController

12.Project->Create->AnimatorControllerからAnimatorControllerを作成し、名前をCharAnimとする。
13.Animatorタブを開き,createState->emptyからIdleステートを以下のように作成する。

14.Unitychanにアタッチして実行してみよう。普通の立ちポーズとなれば成功だ。この際、Apply Root Motionのチェックを外す。今回はUnityちゃんの位置の移動に関してはすべてスクリプトで行う。(モーションがもっている移動情報は使わない)。

15.アニメーターにWalkアクションを以下のように追加する。

16.パラメータを追加する。Parameters->+->Floatと選択する。

17.speedと入力する。これでspeedというパラメータを追加できた。

18.IdleからWalkの遷移の条件を以下の設定しよう。

19.同様にWalkからIdleの遷移を以下のように設定する。

20.次にRunのモーションを追加する。

21.WalkからRunの遷移を以下のように設定する。

22.RunからWalkへの遷移は以下。

23.さらにJumpを以下のように追加する。

24.jumpというparameterを追加する。今回はjumpに関してはtriggerを使う。

25.speedとjumpという2つのparameterがあることを確認する。

26.AnyStateから遷移を出すとどのタイミングでもモーションを実行できる。今回はjumpをAnyStateからの遷移にしよう。以下のように設定する。またすぐにジャンプアクションに遷移するようにTransitionDurationを小さく設定する。

27.ジャンプモーションを1回再生したら、Walkに繋げよう。下の図のようにHas Exit Timeにチェックをいれると再生を終えると自動的に遷移する。

CharacterControllerの付与

モーションの設定ができたのでいよいよキャラクターコントローラーを付与して実際にステージ上を移動できるようにしていこう。
18.AddComponent->PhysicsからCharacterControllerを付与する。
19.CharacterControllerを以下のように設定する(登れる最大傾斜45度、登れる段差30cm、コライダーはモデルに合わせる)

コントローラーの作成

20.あとは、モーションの遷移とキャラクターの動きを制御するCharMove.csを作成しよう。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class CharMove : MonoBehaviour {
	Animator animator;
	CharacterController cc;

	Vector3 dir = Vector3.zero;
	public float gravity = 20.0f;
	public float speed = 4.0f;
	public float rotSpeed = 300.0f;
	public float jumpPower = 8.0f;

	void Start () {
		animator = GetComponent<Animator> ();
		cc = GetComponent<CharacterController> ();
	}


	void Update () {
		//前進成分を取得(0~1),今回はバックはしない
		float acc = Mathf.Max (Input.GetAxis ("Vertical"), 0f);
		//接地していたら
		if (cc.isGrounded) {
			//左右キーで回転
			float rot = Input.GetAxis ("Horizontal");
			//前進、回転が入力されていた場合大きい方の値をspeedにセットする(転回のみをするときも動くモーションをする)
			animator.SetFloat ("speed", Mathf.Max (acc, Mathf.Abs (rot)));
			//回転は直接トランスフォームをいじる
			transform.Rotate (0, rot * rotSpeed * Time.deltaTime, 0);

			if (Input.GetButtonDown ("Jump")) {
				//ジャンプモーション開始
				animator.SetTrigger ("jump");
			}
		} 
		//下方向の重力成分
		dir.y -= gravity * Time.deltaTime;

		//CharacterControllerはMoveでキャラを移動させる。
		cc.Move ((transform.forward * acc * speed + dir) * Time.deltaTime);
		//移動した後着していたらy成分を0にする。
		if (cc.isGrounded) {
			dir.y = 0;
		}

	}
	
}



21.作成したスクリプトをUnityちゃんに付与し、実行してみよう。アニメーションに合わせて移動し、45度までの坂や、30cmまでの段差はそのまま進めることがわかる。

問題点

40cmの段差がジャンプしても登れないのは違和感がある。それを修正しよう。そもそも、なぜジャンプしているのに登れないかというとジャンプしているのは見た目だけで、実際の高さは変化していないからだ。

図を見てもわかるがジャンプしてもtransformの変わっていない。

修正しよう。ジャンプモーションに入ってすぐ高さを与えると、変な動きになる。なぜならば、ジャンプモーションの最初の部分で沈み込む動作をしているからだ。そこでモーションクリップを分析し地面から離れた時にイベントを発生させるという手法をとる。

22.Jumpをダブルクリックしてモーション編集画面に入り、Eventsを開く。

23.再生しながら地面から離れるタイミングを探し、そこにEventを挿入する。今回はOnJumpStartというイベントを発生させた。
(下部にあるApplyボタンを押すのを忘れない)

24.後は、CharMove.csにこのイベント発生時の処理を追記するだけだ。一番下にメソッドを追記する。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class CharMove : MonoBehaviour {
	Animator animator;
	CharacterController cc;

	Vector3 dir = Vector3.zero;
	public float gravity = 20.0f;
	public float speed = 4.0f;
	public float rotSpeed = 300.0f;
	public float jumpPower = 8.0f;

	void Start () {
		animator = GetComponent<Animator> ();
		cc = GetComponent<CharacterController> ();
	}


	void Update () {
		//前進成分を取得(0~1),今回はバックはしない
		float acc = Mathf.Max (Input.GetAxis ("Vertical"), 0f);
		//設置いていたら
		if (cc.isGrounded) {
			//左右キーで回転
			float rot = Input.GetAxis ("Horizontal");
			//前進、回転が入力されていた場合大きい方の値をspeedにセットする(転回のみをするときも動くモーションをする)
			animator.SetFloat ("speed", Mathf.Max (acc, Mathf.Abs (rot)));
			//回転は直接トランスフォームをいじる
			transform.Rotate (0, rot * rotSpeed * Time.deltaTime, 0);

			if (Input.GetButtonDown ("Jump")) {
				//ジャンプモーション開始
				animator.SetTrigger ("jump");
			}
		} 
		//下方向の重力成分
		dir.y -= gravity * Time.deltaTime;

		//CharacterControllerはMoveでキャラを移動させる。
		cc.Move ((transform.forward * acc * speed + dir) * Time.deltaTime);
		//移動した後着していたらy成分を0にする。
		if (cc.isGrounded) {
			dir.y = 0;
		}

	}
	//ジャンプモーションで地面から足が離れたときに呼ばれるイベント
	public void OnJumpStart () {
		//足が離れたらトランスフォームを上方に移動する。
		dir.y = jumpPower;
	}

}

確認

実行してみよう。地面から離れると同時に実際に高さを与えている。30cmの段差などはものともしない感じになった。

最後に

今回はChracterControllerによるキャラの移動をレッスンした。重力を自分で実装しないといけない点や、IsKinematic的な挙動で他者からの物理的影響は受けないなど癖も多いが、利用どころを間違えなければ結構使える。慣れていくといくと良いだろう。