Unityで三人称視点のキャラクターコントローラを作ってみよう(第6弾)

以前のチュートリアルはこちらです:

細かい移動の演出

以前のチュートリアルでアイドリング状態から走りアニメーションを切り替えるシステムを作りました。しかし、走り出した時や止まった時は動きが不自然でした。ここでは、ブレンドツリーという機能を使って移動する動きをより細かく設定します。具体的に移動速度によって以下のパターンに切り替えます:

  1. アイドリング(静止)
  2. 歩き
  3. 小走り
  4. 走り

追加のアニメーションが必要ですので、Mixamoから適切な「歩き」と「小走り」アニメーションを探します。ダウンロード手順は前回のチュートリアル同様ですので、説明を省きます。

「Walk」を検索して、よさそうな歩きアニメーションを見つけました。
小走りは「Jog」または「Jogging」を検索して見つかります。
いつも通りUnityのプロジェクトにアニメーションを追加します。

インポートしたアニメーションは前回同様に、リグ設定を変えてループ再生を有効にします。

ブレンドツリー

静止、歩き、小走りと走りの切り替えは前回のような遷移ではなく、よりパワフルなブレンドツリーを使います。ブレンドツリーは文字通りと複数のアニメーションをブレンドさせる方法です。つまり、歩き→小走りという明確なトランジションではなく、移動速度に合わせて演出を徐々に歩きから小走りにブレンドしていきます。

まず、アニメーターの「OnGround」サブステートマシンに「Locomotion」(移動)という新しいブレンドツリーを追加します。

インスペクターで「Blend Tree」と書かれているところをダブルクリックして、ブレンドツリーを編集します。

ブレンドツリーは1次元(1D)と2次元(2D)のツリーがあります。一次元のツリーは一つのパラメータだけで制御します。とりあえずこちらを使ってみます。ブレンドを制御するパラメータをMovementSpeedにします。そして、ブレンドするアニメーションクリップを追加します。

最終的にこうなります。モーションの順番が重要ですが、モーション名の左にあるハンドルで並べ替えられます。

ブレンドをプレビューしたいので、インスペクターの下で再生を開始します。

赤いカーソルでMovementSpeedの変化をシミュレーションします。

プレビューしてみると、歩きと小走り、小走りと走りがブレンドされている状態だとキャラクターの走り方がおかしいのが分かると思います。

原因はダウンロードした小走りのアニメーションです。他のアニメーションは1サイクル(左右一歩ずつ)しかありませんが、小走りは何歩かがあります。Mixamoからダウンロードしなおさなくても直せます。プロジェクトウィンドウで街頭のクリップを選択して、インスペクターで編集します。

「Animation」タブで終了のフレームを調整します。

下でプレビューで確認しながら、ぴったり1サイクル分だけを残します。
この辺で止めるといい感じです。

変更を適用して、ブレンドツリーの編集に戻ります。アイドリングから走りまでのブレンドが滑らかになったことが確認できます。

デフォルトで制御するパラメータの最高値が「1」になっていますが、MovementSpeedはキャラクターの最高速度まで上がります。

Characterスクリプトのこのパラメータと…
ブレンドツリーのこのパラメータを合わせます。

歩く速度と小走りの速度もThresholdパラメータで設定します。

青い三角のところをドラグして調整できます。

細かい微調整が残っていますが、プレーして動作確認をしながら行いましょう。

ブレンドツリーの調整が一旦終わったので、アニメーターで繋げます。最初は、「Locomotion」をレイヤーのデフォルトアニメーションにします。

せっかく作りましたが、既存の「Idle」と「Run」を削除します。「Locomotion」がすべてその役割を果たしてくれるからです。

「Locomotion」から「(Up) Base Layer」への遷移を追加します。

遷移の条件は以前と同様です。

動作確認します。

決して完璧じゃないですが、だいぶ良くなりました。

ここまで出来たら各モーションの再生速度やThresholdでさらに自然な動きを目指します。例えば、上の動画でアイドリングから歩きへの切り替えがまだイマイチですから、歩きの位置を調整しました。

2次元のブレンドツリー

移動をさらにリアルにしたいなら、曲がった時に曲がりながら走るアニメーションをブレンドツリーに追加しましょう。

Mixamoでよさそうな曲がるアニメーションがありました。
プロジェクトに追加しました。インポート設定はいつも通りです。

これが使える前にアニメーターに新しいパラメーターを追加します。

種類は「Float」です。

このパラメーターをスクリプトで設定するので、Characterスクリプトにプライベートプロパティを追加します。

bool isOnGround = false; // 地面に立っているかどうか
bool isJumping = false; // ジャンプしているかどうか
Vector3 groundVelocity = Vector3.zero; // 移動速度
float turnAngle = 0f; // 曲がる角度

ApplyMotionでアバターの向きを変えるコードに曲がりの角度計算を追加します。

// プレーヤーの向きを変える
if (avatar != null)
{
    Vector3 rotateTarget = new Vector3(movement.x, 0, movement.z);
    if (rotateTarget.magnitude > 0.1f)
    {
        Quaternion lookRotation = Quaternion.LookRotation(rotateTarget);
        avatar.transform.rotation = Quaternion.Lerp(lookRotation, avatar.transform.rotation, turnSmoothing);
        // 向かっている方向と向かおうとしている方向の角度を求める
        // 度で返される値をラジアンに変換する
        turnAngle  = (Mathf.PI / 180f)Quaternion.Angle(lookRotation, avatar.transform.rotation);
        // 左右の曲がりを識別したいので、左ならマイナス値にする
        if (movementInput.x < 0)
        {
            turnAngle *= -1f;
        }
    }
}

そして、Updateでこの結果をAnimatorに送ります。

void Update()
{
    if (animator != null)
    {
        animator.SetBool("OnGround", isOnGround);
        animator.SetFloat("MovementSpeed", groundVelocity.magnitude);
        animator.SetFloat("Turn", turnAngle);
    }
}

一応、Animatorウィンドウを見ながら動作確認をしましょう。左右曲がる時に、Turnパラメーターが変わります。

ここまで出来たら、ブレンドツリーを選択して、タイプを1Dから「2D Freeform Cartesian」に変えます。

パラメータをMovementSpeedとTurnにします。

ブレンドするモーションがすべて同じ位置に配置されてしまうので、切り離します。

x軸がMovementSpeedですので、以前の1Dのブレンドツリーを再現します。

y軸(曲がり)はすべて0にします。

ここでもう一度動作確認をします。以前と同じ動きになっていることを確認します。

追加した曲がりながら走るモーションを2度追加します。

同じモーションを2度追加します。

2つ目のアニメーションのミラー(左右反転)パラメータにチェックを入れます。これで右に曲がるモーションと左に曲がるモーションになります。

2つのモーションを走りと揃えて配置します。

曲がりの閾値を0.3ラジアンにしておきます。

動作確認してみると、曲がった時に確かに曲がるアニメーションが再生されますが、左右に移動方向を変えた時にアニメーションが急に切り替わってしまうのが気になります。滑らかにするためにコードを少し修正します。

Quaternion lookRotation = Quaternion.LookRotation(rotateTarget);
avatar.transform.rotation = Quaternion.Lerp(lookRotation, avatar.transform.rotation, turnSmoothing);
// 向かっている方向と向かおうとしている方向の角度を求める。
// 度で返される値をラジアンに変換する
float newAngle = (Mathf.PI / 180f) * Quaternion.Angle(lookRotation, avatar.transform.rotation);
// 左右の曲がりを識別したいので、左ならマイナス値にする
if (movementInput.x < 0)
{
    newAngle *= -1f;
}
turnAngle = Mathf.Lerp(turnAngle, newAngle, 0.1f); // Lerp(補完)で変化を滑らかにする

この修正で動作確認をしてみると…

曲がりアニメーションがちゃんと切り替わります。

Leave a Reply

Your email address will not be published. Required fields are marked *

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください