しっぽを追いかけて

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

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

Unity でアニメーションさせながらスクリプトでモデルを動かす

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

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

ねこモデルで坂道を登らせることはできましたが、よく見るとラッキービーストのモデルでできていたことができていませんでした

f:id:matatabi_ux:20180311194007g:plain

なんと待機中の状態でカメラの方向に顔を向ける制御ができていない・・・

待機中もアニメーションさせていることで、スクリプトの制御が競合しているようです

これを直したい

といっても修正内容は比較的かんたんで、アタッチしているスクリプトを変更するだけ

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>
    public bool IsWalking
    {
        get { return this.animator.GetBool("IsWalking"); }
        set
        {
            // 変更があった場合のみ反映
            if (value != this.IsWalking)
            {
                this.animator.SetBool("IsWalking", value);
                this.state = value ? State.Walk : State.Idle;
            }
        }
    }

    /// <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;

        // 歩き出す距離と止まる距離に幅を持たせる
        var distance = Vector3.Distance(cameraPosition, this.transform.position);
        switch (this.state)
        {
            case State.Idle:
                this.IsWalking = distance > 2f;
                break;

            case State.Walk:
                this.IsWalking = distance > 1f;
                break;
        }
    }

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

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

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

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

        // 重力をかける
        this.controller.Move(Physics.gravity);
    }

    /// <summary>
    /// 遅延フレームの処理
    /// </summary>
    public void LateUpdate()
    {
        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);

                break;
        }
    }
}

これまで FixedUpdate() 内で行っていた制御を LateUpdate() 内で行うように変更しています

これにより、Update() 処理のアニメーション後、顔の部分の角度がスクリプトで変更を与えます

これで競合しないのでは?・・・ということでお試し

f:id:matatabi_ux:20180312233510g:plain

デキマシター!しっぽふりふりアニメをしつつ顔だけ傾いています