「Animation Rigging」パッケージを使って、敵キャラに体ごと自然にプレイヤーの方を振り向かせてみました。
Animation Riggingはアニメーションリグやコンストレイントによって、動的にアニメーションを変更できます。Animation Riggingはパッケージマネージャーからインストールできます。
Rig BuilderとRigを設定
まず、キャラクターのルート等Animatorコンポーネントを持つゲームオブジェクトにRig Builderコンポーネントを追加します。
その子として空のゲームオブジェクトを作成して、Rigコンポーネントを付けます。
Rig Builderコンポーネントの「Rig Layers」にこのゲームオブジェクトをアタッチします。
顔をプレイヤーの方へ向かせる
Rigゲームオブジェクトの下にコンストレイントをまとめます。まず敵の顔をプレイヤーのカメラの方へ向かせるために「Multi-Aim Constraint」を設定してみます。
Rigゲームオブジェクトの下に空のゲームオブジェクトを作りMulti-Aim Constraintコンポーネントを追加します。
Multi-Aim Constraintコンポーネントの「Constrained Object」にキャラクターの頭のボーンを、Source Objectsにターゲットの空のゲームオブジェクトをアタッチします。
ターゲットはこのゲームオブジェクトの子にしています。
頭のボーンは Y が上で Z が前なので、Aim Axisを -Z にすると、顔をそむけます。
Constrained AxesをY軸のみにして、キャラの頭が真上を向かないようにしています。
Min LimitとMax Limitでは、頭の回転を制限しています。
ターゲットをプレイヤーのカメラの位置に置く
ターゲットのゲームオブジェクトに「Parent Constraint」コンポーネントを付けて、ターゲットをプレイヤーのカメラと同じ位置に置いています。
Sourcesにプレイヤーのカメラが置かれるトランスフォームをアタッチします。「Zero」ボタンを押すと、オフセットがなくなって、ターゲットがソースと同じ位置にきます。
これでキャラの顔だけがプレイヤーの方を向くようになりました。
頭につられて上体を回転させる
顔に合わせてキャラの上半身を少し回転させるために、Rigゲームオブジェクトの下に新しく空のゲームオブジェクトを追加して、「Twist Correction」コンポーネントを付けます。
Twist Correctionコンポーネントの「Twist Nodes」に首と2つの脊椎ボーンをアタッチして、Y軸に沿って回転させます。
ソースのゲームオブジェクトに合わせて各ボーンをどのくらい回転させるかを、右のスライダーで設定できます。プレイモードで確認しながら調節しました。
ソースはこのゲームオブジェクトの子にしています。ソースのゲームオブジェクトに「Parent Constraint」を付けて、「Sources」に頭のボーンをアタッチしています。
これでキャラの上体も回転するようになりました。
コンストレイントの順序を変えると挙動が変わりました。
スクリプトでRigのウェイトを変更
Rigのウェイトが0だとコンストレイントの影響を受けず、キャラが振り向かなくなります。
Rigのウェイトはアニメーションやスクリプトで変更できます。
using System.Collections;
using UnityEngine;
using UnityEngine.Animations.Rigging;
using UnityEngine.InputSystem;
public class RiggingTest : MonoBehaviour
{
[SerializeField] Rig rig;
[SerializeField] float turningSpeed = 2f;
bool isIdling;
Animator animator;
void Start()
{
animator = GetComponent<Animator>();
}
void Update()
{
if (Mouse.current.leftButton.wasPressedThisFrame)
{
isIdling = !isIdling;
if (isIdling)
{
EnablePlayerLook();
}
else
{
DisablePlayerLook();
}
}
}
void EnablePlayerLook()
{
animator.SetBool("IsIdling", true);
// rig.weight = 1f;
StopAllCoroutines();
StartCoroutine("IncreaseRigWeight");
}
IEnumerator IncreaseRigWeight()
{
var weight = rig.weight;
while(true)
{
weight += Time.deltaTime * turningSpeed;
rig.weight = weight;
if(weight >= 1f)
{
rig.weight = weight;
yield break;
}
yield return null;
}
}
void DisablePlayerLook()
{
animator.SetBool("IsIdling", false);
//rig.weight = 0f;
StopAllCoroutines();
StartCoroutine("DecreaseRigWeight");
}
IEnumerator DecreaseRigWeight()
{
var weight = rig.weight;
while (true)
{
weight -= Time.deltaTime * turningSpeed;
rig.weight = weight;
if (weight <= 0f)
{
rig.weight = 0f;
yield break;
}
yield return null;
}
}
}