しっぽを追いかけて

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

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

Unity でキャラクターを歩きながらカメラに近づけてみる

※ これは 2018/01/08 Unity 2017.3.0f3 時点の情報です

最新版では動作が異なる可能性がありますのでご注意ください

歩きモーションのアニメができたので、今回は歩きながらカメラに近づいてくる動きを実装してみます

f:id:matatabi_ux:20180113230615g:plain

少し前に書いた顔を向けるスクリプトを次のように変更

using UnityEngine;

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

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

    private Animator animator;

    /// <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(90f, 0f, 0f);

    /// <summary>
    /// 初期処理
    /// </summary>
    public void Start()
    {
        this.camra = Camera.main.transform;
        this.animator = this.GetComponent<Animator>();
        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.transform.LookAt(cameraPosition);
                var angle = this.transform.rotation.eulerAngles.x;
                angle = angle > 180f ? angle - 360f : angle;
                this.coreBone.localRotation
                    = Quaternion.Euler(90f + Mathf.Clamp(angle, -30f, 30f), 0f, 0f);

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

                break;

            case State.Walk:

                // 顔を正面に向ける
                this.coreBone.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.transform.position = Vector3.MoveTowards(this.transform.position, cameraPosition, delta);

                break;
        }
    }
}

列挙型で Idle と Walk の2つの状態を定義しました

カメラとの距離が 2m より離れたら Walk 状態に切り替え、歩きモーションの再生開始と Vector3.MoveTowards() メソッドで移動する処理を追加

カメラとの距離が 2m 以内の場合は Idle 状態に切り替え、歩きモーションを停止し以前の顔を向ける処理だけを行うようにしました

状態とモーションの切替は即座に行われるように Update() メソッド内で実行しますが、移動や運動系の変化は固定フレームで実行される FixedUpdate() メソッド内で実行します

これを UnityEditor 上で試してみると

f:id:matatabi_ux:20180114100110g:plain

だいたい思い通り