しっぽを追いかけて

ぐるぐるしながら考えています

Unity と猫の話題が中心   掲載内容は個人の私見であり、所属組織の見解ではありません

Unity の CharactorController で坂道を登らせる

今まで使っていたラッキービーストモデル・・・なんとPC 新調時に誤って消してしまいました!

せっかく作ったのにもったいない!

また作り直すのもちょっとつらいのでしかたなく下記のモデルを利用することにしました

assetstore.unity.com

どうせならにゃんこ!

Unity プロジェクトを新規作成し、上記ねこモデルをインポートして Prefab をシーン上に配置

これまでの手順をなぞりつつねこモデルの Prefab に動作用のスクリプトアタッチ、アニメーション設定付与

そして今回は新たにシーン上に地面となる Plane と坂道の Cube を配置しました

f:id:matatabi_ux:20180311190737p:plain

この状態でとりあえず実行し、ねこを歩かせてみると

f:id:matatabi_ux:20180311190830g:plain

坂道にめり込んでしまいました・・・優雅に坂道を登らせたい!

というわけで、坂道を登らせるために Unity の CharactorController を利用します

このコンポーネントは精度があまり高くはないですが、とりあえずといった場合には十分に利用可能かと思います

まずは Hierarchy パネル上のねこモデルの Prefab を選択し、Add Component ボタンから CharacterController を選んで追加

f:id:matatabi_ux:20180311191743p:plain

今回はこんな感じで設定

f:id:matatabi_ux:20180311193340p:plain

Center、Radius、Height によりカプセル型の衝突判定が緑色のワイヤーフレームで描画されます

f:id:matatabi_ux:20180311193917p:plain

実際の衝突判定範囲はこれに Skin Width を足した範囲になるらしい

ねこモデルにアタッチしたスクリプト(以前は Beast という名前でしたが CatWalker に変更しています)を次のように変更

using UnityEngine;

[RequireComponent(typeof(CharacterController))]
[RequireComponent(typeof(Animator))]
public class CatWalker : MonoBehaviour
{
    /// <summary>
    /// 顔の部分のボーン
    /// </summary>
    [SerializeField]
    private Transform headBone = null;

    /// <summary>
    /// キャッシュ用カメラ Transform
    /// </summary>
    private Transform camra;

    private Animator animator;
    private CharacterController controller;

    /// <summary>
    /// 状態の列挙型
    /// </summary>
    internal enum State
    {
        /// <summary>
        /// 待機状態
        /// </summary>
        Idle,

        /// <summary>
        /// 歩行中
        /// </summary>
        Walk,
    }

    /// <summary>
    /// 現在の状態
    /// </summary>
    private State state;

    /// <summary>
    /// 正面を向いた顔の角度
    /// </summary>
    private static readonly Quaternion DefaultCoreAngle = Quaternion.Euler(0f, 0f, 0f);

    /// <summary>
    /// 初期処理
    /// </summary>
    public void Start()
    {
        this.camra = Camera.main.transform;
        this.animator = this.GetComponent<Animator>();
        this.controller = this.GetComponent<CharacterController>();
        this.state = State.Idle;
    }

    /// <summary>
    /// 可変毎フレームごとの処理
    /// </summary>
    public void Update()
    {
        // 距離の判定に高低差を考慮しない
        var cameraPosition = this.camra.position;
        cameraPosition.y = this.transform.position.y;

        if (Vector3.Distance(cameraPosition, this.transform.position) > 2f)
        {
            this.animator.SetBool("IsWalking", true);
            this.state = State.Walk;
        }
        else
        {
            this.animator.SetBool("IsWalking", false);
            this.state = State.Idle;
        }
    }

    /// <summary>
    /// 固定毎フレームの処理
    /// </summary>
    public void FixedUpdate()
    {
        var cameraPosition = this.camra.position;

        switch (this.state)
        {
            case State.Idle:

                // 目の高さに合わせる
                cameraPosition.y -= 0.3f;

                // 顔だけカメラに向かせる(X軸の回転)
                this.headBone.LookAt(cameraPosition);
                var angle = this.headBone.rotation.eulerAngles.x;
                angle = angle > 180f ? angle - 360f : angle;
                this.headBone.localRotation
                    = Quaternion.Euler(0f + Mathf.Clamp(angle, -30f, 30f), 0f, 0f);

                // 身体全体をカメラに向ける(Y軸の回転)
                cameraPosition.y = this.transform.position.y;
                this.transform.LookAt(cameraPosition);

                break;

            case State.Walk:

                // 顔を正面に向ける
                this.headBone.localRotation = DefaultCoreAngle;

                // 身体全体をカメラに向ける(Y軸の回転)
                cameraPosition.y = this.transform.position.y;
                this.transform.LookAt(cameraPosition);

                // カメラに向かって移動
                var delta = Mathf.Clamp(Vector3.Distance(cameraPosition, this.transform.position) - 2f, 0f, 0.01f);
                this.controller.Move(Vector3.MoveTowards(this.transform.position, cameraPosition, delta) - this.transform.position + Physics.gravity);

                break;
        }
    }
}

CharacterController をプライベート変数に確保するようにしたのと、下記の部分が変更になっています

this.controller.Move(Vector3.MoveTowards(this.transform.position, cameraPosition, delta) - this.transform.position + Physics.gravity);

以前は直接 this.transform.position を更新していたところを、CharactorController の Move メソッドを利用して移動させるようにしました

また重力として Physics.gravity も加算しています

さてこの設定で同じように動かしてみると・・・

f:id:matatabi_ux:20180311194007g:plain

今度はちゃんと坂道を登ってくれました