MRが楽しい

MRやVRについて学習したことを書き残す

MRTKで球体オブジェクトが弾けて消えるアニメーションを作成する

本日は MRTK の小ネタ枠です。
MRTKで半透明の球体オブジェクトが弾けて消えるアニメーションを作成してみたので記事にします。
f:id:bluebirdofoz:20210709233738j:plain

MRTKStandardShader

MRTK の StandardShader を利用すると様々なアニメーションをシェーダのプロパティから実行することができます。
Samples パッケージの MaterialGallery で利用例を確認することができます。
f:id:bluebirdofoz:20210709233754j:plain

今回は Vertex Extrusion のサンプルを参考に球が弾けるアニメーションを作成しました。
f:id:bluebirdofoz:20210709233809j:plain

モデルの作成

MaterialGallery で使用されている Model_Bucky プレハブを取得し、シーンに配置します。
f:id:bluebirdofoz:20210709233821j:plain

Vertex Extrusion の効果を利用するためには、[Vertex Extrusion]にチェックを入れた MRTKStandardShader を利用します。
Model_Bucky プレハブにも以下のマテリアルが設定されています。
f:id:bluebirdofoz:20210709233835j:plain

今回は透過したオブジェクトで効果を実現したいので、マテリアルを編集します。
[RenderingMode]を[additive]に変更し、[Albedo]のカラーに透過色を設定します。
f:id:bluebirdofoz:20210709233902j:plain

この状態でシーンを再生し、アニメーションを確認します。
Vertex Extrusion のサンプルアニメーションは一定の変化量で広がって戻るを繰り返します。
f:id:bluebirdofoz:20210709233912j:plain

アニメーションの編集

今回はそのまま弾けて消えるアニメーションにしたいので anim ファイルを複製して編集します。
f:id:bluebirdofoz:20210709233924j:plain

元の形に戻るアニメーションキーを[右クリック-> Delete Key]で削除し、広がるアニメーションキーの値を大きめに設定します。
f:id:bluebirdofoz:20210709233936j:plain

またアニメーションをループさせないように、Inspector ビューから[Loop Time]のチェックを外しておきます。
f:id:bluebirdofoz:20210709233948j:plain

アニメーションを確認して形状が元に戻らないことを確認します。
アニメーションコントローラを複製した場合は参照の差し替えを行っておきます。
f:id:bluebirdofoz:20210709234001j:plain

ただし[Vertex Extrusion]は頂点位置を押し出すだけのものなので、値をどれだけ大きくしても破片が残ります。
f:id:bluebirdofoz:20210709234014j:plain

プロパティの追加

最終的にモデルを消失させるため Transform の Scale 値を変化させることでモデルを消失させます。
アニメーションのプロパティを追加するには anim ファイルを再び開き、編集対象の Platonic オブジェクトを選択状態にします。
f:id:bluebirdofoz:20210709234024j:plain

この状態で[Add Property]ボタンをクリックすると対象のプロパティを選択できます。
[Transform -> Scale]の[+]ボタンをクリックします。
f:id:bluebirdofoz:20210709234035j:plain

アニメーションの終わりで[Scale]が 0 になるようにキーを設定します。
f:id:bluebirdofoz:20210709234046j:plain

再びシーンを再生して確認します。
球体オブジェクトが弾けて消えるアニメーションが再生されれば成功です。
f:id:bluebirdofoz:20210709234057j:plain

指定のトランスフォームを中心にオブジェクトを回転させながら追従させる その3

本日は Unity の小ネタ枠です。
指定のトランスフォームを中心にオブジェクトを回転させながら追従させる方法を記事にします。
今回は指定のオブジェクトに徐々に近づけることで科学館でよく見る万有引力の動きをそれっぽく再現してみます。
f:id:bluebirdofoz:20210708233216j:plain

前提条件

前回記事の続きです。
bluebirdofoz.hatenablog.com
bluebirdofoz.hatenablog.com

Vector3.Lerp

直線上にある 2 つのベクトル間を補間します。
本記事では徐々にターゲットオブジェクト方向に近づけるために利用します。
docs.unity3d.com

public static Vector3 Lerp (Vector3 a, Vector3 b, float t);
引数 説明
a 開始位置
b 到達位置
t 移動割合(0.0~1.0)

サンプルシーン

以下の通り、サンプルスクリプトを修正しました。
ターゲットオブジェクトの周囲を中心に回転しながら、徐々にターゲットオブジェクトに近づいていきます。
そして距離が近づくほど、回転の速度が上昇していきます。
・RotateAroundTargetGravitation.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class RotateAroundTargetGravitation : MonoBehaviour
{
    [SerializeField, Tooltip("開始位置オブジェクト")]
    private GameObject StartObject;

    [SerializeField, Tooltip("ターゲットオブジェクト")]
    private GameObject TargetObject;

    [SerializeField, Tooltip("開始時の回転速度係数")]
    private float SpeedFactor = 0.1f;

    [SerializeField, Tooltip("Lerp係数")]
    private float LerpFactor = 0.01f;

    [Tooltip("開始時の2点間の距離")]
    private float RadiusDistance;

    [Tooltip("回転軸")]
    // 回転軸はY軸(縦軸)に固定する
    private Vector3 RotateAxis = Vector3.up;

    private void Start()
    {
        // 各オブジェクト位置を取得する
        Vector3 startPosition = StartObject.transform.position;
        Vector3 targetPosition = TargetObject.transform.position;

        // 初期位置はスタートオブジェクトと同じ位置に設定する
        this.transform.position = startPosition;

        // 開始時の距離を取得する
        RadiusDistance = Vector3.Distance(startPosition, targetPosition);
    }

    // Update is called once per frame
    void Update()
    {
        if (StartObject == null || TargetObject == null) return;

        // 各オブジェクト位置を取得する
        Vector3 targetPosition = TargetObject.transform.position;
        Vector3 selfPosition = this.transform.position;

        // 現在の距離を取得する
        float currentRadiusDistance = Vector3.Distance(selfPosition, targetPosition);

        if (currentRadiusDistance < 0.1f)
        {
            // 一定以上に距離が近づいたら計算を終了する
            this.transform.position = TargetObject.transform.position;
            this.transform.rotation = TargetObject.transform.rotation;
            return;
        }

        // 距離が近づくほど回転速度を早くする
        float currentSpeedFactor = SpeedFactor * (RadiusDistance / currentRadiusDistance);

        // Leap関数を使って徐々にターゲットに近づいていく
        this.transform.position = Vector3.Lerp(selfPosition, targetPosition, LerpFactor);

        // 指定オブジェクトを中心に回転する
        this.transform.RotateAround(
            targetPosition,
            RotateAxis,
            360.0f / (1.0f / currentSpeedFactor) * Time.deltaTime
            );
    }
}

f:id:bluebirdofoz:20210708233330j:plain

シーンに Sphere オブジェクトを作成し、本スクリプトを追加します。
f:id:bluebirdofoz:20210708233344j:plain

追従させるターゲットとして Cube オブジェクトを配置し手指定します。
開始位置のターゲットには[MainCamera]を指定します。
f:id:bluebirdofoz:20210708233358j:plain

動作確認

シーンを再生して動作を確認します。
Sphere オブジェクトがカメラ位置から cube オブジェクトに向かって回転しながら落下していきます。
f:id:bluebirdofoz:20210708233427j:plain

距離が近づくほど回転速度が速くなり、ターゲットオブジェクトの位置に到達します。
f:id:bluebirdofoz:20210708233438j:plain

指定のトランスフォームを中心にオブジェクトを回転させながら追従させる その2

本日は Unity の小ネタ枠です。
指定のトランスフォームを中心にオブジェクトを回転させながら追従させる方法を記事にします。
今回は2つの異なるトランスフォームを参照して動的に回転半径と回転軸を変動させてみます。
f:id:bluebirdofoz:20210707233550j:plain

前提条件

前回記事の続きです。
bluebirdofoz.hatenablog.com

上記の記事では回転の半径と回転軸をInspectorで指定していました。
今回は更に別のトランスフォームを参照し、その動きに合わせて動的に回転の半径と回転軸を変動させてみます。

Vector3.Dot

2つのベクトルの内積を算出します。
本記事では2オブジェクト間のベクトルとY軸の平行を検出するために利用します。
docs.unity3d.com

public static float Dot (Vector3 lhs, Vector3 rhs);
引数 説明
lhs ベクトル1
rhs ベクトル2

Mathf.Abs

数値の絶対値を求めます。
本記事では2オブジェクト間のベクトルとY軸の平行を検出するために利用します。
docs.unity3d.com

public static float Abs (float f);
引数 説明
f 数値

サンプルシーン

以下の通り、サンプルスクリプトを修正しました。
開始位置となるスタートオブジェクトと、回転の中心となるターゲットオブジェクトを指定します。
実行中は2つのオブジェクト間の距離を回転半径、オブジェクト間のベクトルのY軸方向の直交ベクトルを回転軸とします。
・RotateAroundTargetFromStart.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class RotateAroundTargetFromStart : MonoBehaviour
{
    [SerializeField, Tooltip("開始位置オブジェクト")]
    private GameObject StartObject;

    [SerializeField, Tooltip("ターゲットオブジェクト")]
    private GameObject TargetObject;

    [SerializeField, Tooltip("速度係数")]
    private float SpeedFactor = 0.1f;

    [Tooltip("回転軸")]
    private Vector3 RotateAxis = Vector3.up;

    [Tooltip("半径距離")]
    private float RadiusDistance = 1.0f;

    private void Start()
    {
        // 各オブジェクト位置を取得する
        Vector3 startPosition = StartObject.transform.position;
        Vector3 targetPosition = TargetObject.transform.position;

        // 初期位置はスタートオブジェクトと同じ位置に設定する
        this.transform.position = startPosition;

        // 2オブジェクト間の距離を半径距離とする
        RadiusDistance = CalcRadiusDistance(startPosition, targetPosition);

        // 2オブジェクトを結ぶベクトルのY軸方向の直交ベクトルを回転軸とする
        RotateAxis = CalcRotateAxis(startPosition, targetPosition);
    }

    // Update is called once per frame
    void Update()
    {
        if (StartObject == null || TargetObject == null) return;

        // 各オブジェクト位置を取得する
        Vector3 startPosition = StartObject.transform.position;
        Vector3 targetPosition = TargetObject.transform.position;
        Vector3 selfPosition = this.transform.position;

        // ターゲットオブジェクトの軸方向の移動量はそのまま追従する
        Vector3 diffVector = selfPosition - targetPosition;
        float diffMagnitude = diffVector.magnitude;
        float dot = Vector3.Dot(diffVector, RotateAxis);

        // 回転軸との内積から回転軸方向への移動量を求める
        // 離れすぎるとこの演算でエラーが出るので要改良
        selfPosition -= RotateAxis.normalized * (diffMagnitude * dot);

        // 2オブジェクト間の距離を半径距離とする
        RadiusDistance = CalcRadiusDistance(startPosition, targetPosition);

        // 現在の距離と半径距離の差分を取得する
        float diffDistance = Vector3.Distance(selfPosition, targetPosition) - RadiusDistance;

        // 指定半径の距離だけ近づく(or離れる)
        this.transform.position = Vector3.MoveTowards(selfPosition, targetPosition, diffDistance);

        // 2オブジェクトを結ぶベクトルのY軸方向の直交ベクトルを回転軸とする
        RotateAxis = CalcRotateAxis(startPosition, targetPosition);

        // 指定オブジェクトを中心に回転する
        this.transform.RotateAround(
            targetPosition,
            RotateAxis,
            360.0f / (1.0f / SpeedFactor) * Time.deltaTime
            );
    }

    /// <summary>
    /// 回転半径を計算する
    /// </summary>
    /// <param name="a_StartPos">開始オブジェクト位置</param>
    /// <param name="a_TargetPos">ターゲットオブジェクト位置</param>
    /// <returns>回転半径</returns>
    private float CalcRadiusDistance(Vector3 a_StartPos, Vector3 a_TargetPos)
    {
        // 2点間の距離を回転半径とする
        return Vector3.Distance(a_StartPos, a_TargetPos);
    }

    /// <summary>
    /// 回転軸ベクトルを計算する
    /// </summary>
    /// <param name="a_StartPos">開始オブジェクト位置</param>
    /// <param name="a_TargetPos">ターゲットオブジェクト位置</param>
    /// <returns>回転軸ベクトル</returns>
    private Vector3 CalcRotateAxis(Vector3 a_StartPos, Vector3 a_TargetPos)
    {
        // 2オブジェクトを結ぶベクトルのY軸方向の直交ベクトルを回転軸とする
        Vector3 diffVector = (a_StartPos - a_TargetPos).normalized;

        // 2点間のベクトルとY軸が平行な場合は回転軸をZ軸で指定する
        if (Mathf.Abs(Vector3.Dot(diffVector, Vector3.up)) == 1.0f)
        {
            return Vector3.forward;
        }

        // 側面方向のベクトルを取得するため、2点間のベクトルとY軸との直交ベクトルを求める
        Vector3 sideVector = Vector3.Cross(diffVector, Vector3.up);

        // 2点間のベクトルと側面方向のベクトルの直交ベクトルを回転軸とする
        return Vector3.Cross(diffVector, sideVector);
    }
}

f:id:bluebirdofoz:20210707233642j:plain

シーンに Sphere オブジェクトを作成し、本スクリプトを追加します。
f:id:bluebirdofoz:20210707233653j:plain

追従させるターゲットとして Cube オブジェクトを配置し手指定します。
開始位置のターゲットには[MainCamera]を指定します。
f:id:bluebirdofoz:20210707233703j:plain

動作確認

シーンを再生して動作を確認します。
Sphere オブジェクトがカメラ位置から cube オブジェクトを中心にして回転します。
f:id:bluebirdofoz:20210707233714j:plain

常にカメラと cube オブジェクト間のベクトルの直交ベクトルを回転軸にするため、Sphere はカメラが動いても、カメラと cube の直線上を回り込むように回転します。
f:id:bluebirdofoz:20210707233724j:plain

HoloLens2のファイルエクスプローラから自作アプリケーションをインストールする

本日は HoloLens2 の小ネタ枠です。
HoloLens2のファイルエクスプローラから自作アプリケーションをインストールする手順を記事にします。
f:id:bluebirdofoz:20210706212434j:plain

アプリインストーラ

HoloLens2では 20H2 以降のOSバージョンであればアプリインストーラの機能を使ってファイルエクスプローラ内の appxbundle ファイルから直接アプリインストールを行うことができます。
docs.microsoft.com

今回はこちらの機能を使って自作アプリをインストールします。

PC側の作業

開発 PC で自作アプリの appxbundle ファイルを作成し、HoloLens2 にコピーします。

appxbundleの作成

自作アプリの appxbundle ファイルを作成する手順は以下の記事を参照ください。
bluebirdofoz.hatenablog.com

出力されたファイルのうち、appxbundle ファイルのみ利用します。
f:id:bluebirdofoz:20210706212513j:plain

appxbundleをHoloLens2のフォルダにコピーする

作成した appxbundle を HoloLens2 のフォルダにコピーします。
HoloLens2 を PC にUSB接続するとファイルエクスプローラからストレージにアクセスできます。
f:id:bluebirdofoz:20210706212527j:plain

HoloLens2 の内部ストレージから[Donwloads]フォルダを開き、appxbundle ファイルをコピーします。
f:id:bluebirdofoz:20210706212543j:plain

HoloLens2 側の作業

コピーした appxbundle からインストールを行います。

ファイルエクスプローラの起動

ホームメニューのアプリ一覧から[File Explorer(ファイルエクスプローラ)]を選択して起動します。
f:id:bluebirdofoz:20210706212558j:plain

バイス内のファイル一覧を開き、appxbundle ファイルをコピーした[Downloads]フォルダを開きます。
f:id:bluebirdofoz:20210706212615j:plain

フォルダ内の appxbundle ファイルをタップ操作で選択します。
f:id:bluebirdofoz:20210706212630j:plain

アプリインストーラの起動と実行

appxbundle をタップするとアプリインストーラが起動します。
[Install(インストール)]ボタンをタップします。
f:id:bluebirdofoz:20210706212645j:plain

ユーザ認証の確認ダイアログが表示されます。
現在のユーザ名を確認し、[OK]ボタンをタップします。
f:id:bluebirdofoz:20210706212702j:plain

これでアプリのインストールが開始されます。
f:id:bluebirdofoz:20210706212716j:plain

インストールが完了すると、アプリが自動で起動します。
f:id:bluebirdofoz:20210706212731j:plain

インストールしたアプリはアプリ一覧からも起動できます。
f:id:bluebirdofoz:20210706212743j:plain

指定のトランスフォームを中心にオブジェクトを回転させながら追従させる

本日は Unity の小ネタ枠です。
指定のトランスフォームを中心にオブジェクトを回転させながら追従させる方法を記事にします。
f:id:bluebirdofoz:20210705040024j:plain

前提条件

前回記事の続きです。
bluebirdofoz.hatenablog.com

上記の記事の方法では回転の半径をチェックしていないため、指定のトランスフォームが大きく移動すると回転半径が変わってしまう問題が発生します。
Vector3.Distance と Vector3.MoveTowards を利用することで半径を一定に保ちながら、指定のトランスフォームを追従することができます。

Vector3.Distance

Vector3.Distance は2点間の距離を返します。
docs.unity3d.com

public static float Distance (Vector3 a, Vector3 b);
引数 説明
a 座標1
b 座標2

Vector3.MoveTowards

Vector3.Distance は移動元の座標から移動先の座標まで指定した距離を移動します。
移動先までの距離が指定した距離よりも短い場合、返却される座標は移動先の座標と等値になります。
docs.unity3d.com

public static Vector3 MoveTowards (Vector3 current, Vector3 target, float maxDistanceDelta);
引数 説明
current 移動元の座標
target 移動先の座標
maxDistanceDelta 移動距離

Vector3.Cross

2つのベクトルと直交するベクトルを算出します。
本記事では回転軸に直交するベクトルを算出するために利用します。
docs.unity3d.com

public static Vector3 Cross (Vector3 lhs, Vector3 rhs);
引数 説明
lhs ベクトル1
rhs ベクトル2

サンプルシーン

以下の通り、サンプルスクリプトを修正しました。
・RotateAroundTarget.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class RotateAroundTarget : MonoBehaviour
{
    [SerializeField, Tooltip("ターゲットオブジェクト")]
    private GameObject TargetObject;

    [SerializeField, Tooltip("回転軸")]
    private Vector3 RotateAxis = Vector3.up;

    [SerializeField, Tooltip("速度係数")]
    private float SpeedFactor = 0.1f;

    [SerializeField, Tooltip("半径距離")]
    private float RadiusDistance = 1.0f;

    // Update is called once per frame
    void Update()
    {
        if (TargetObject == null) return;

        // 指定オブジェクトと自身の現在位置を取得する
        Vector3 selfPosition = this.transform.position;
        Vector3 targetPosition = TargetObject.transform.position;

        // 座標が重なっていた場合は回転軸の直交方向に離れた場所を初期位置とする
        if (selfPosition.Equals(targetPosition))
        {
            // 直交ベクトルを求めるため、回転軸に平行でないダミーベクトルを作成する
            Vector3 rotateAxisNormal = RotateAxis.normalized;
            Vector3 dummyDirectVector = Vector3.forward;
            if (Mathf.Abs(rotateAxisNormal.y) < 0.5f) dummyDirectVector = Vector3.up;

            // 回転軸とダミーベクトルから直交ベクトルを算出し、初期位置を設定する
            Vector3 directVector = Vector3.Cross(RotateAxis, dummyDirectVector).normalized;
            selfPosition = directVector * RadiusDistance;
        }

        // 軸方向の移動量は追従する
        Vector3 diffVector = selfPosition - targetPosition;
        float diffMagnitude = diffVector.magnitude;
        float dot = Vector3.Dot(diffVector, RotateAxis);
        // 回転軸との内積から回転軸方向への移動量を求める
        selfPosition -= RotateAxis.normalized * (diffMagnitude * dot);

        // 現在の距離と半径距離の差分を取得する
        float diffDistance = Vector3.Distance(selfPosition, targetPosition) - RadiusDistance;

        // 指定半径の距離になるよう近づく(or離れる)
        this.transform.position = Vector3.MoveTowards(selfPosition, targetPosition, diffDistance);

        // 指定オブジェクトを中心に回転する
        this.transform.RotateAround(
            targetPosition,
            RotateAxis,
            360.0f / (1.0f / SpeedFactor) * Time.deltaTime
            );
    }
}

f:id:bluebirdofoz:20210705040550j:plain

シーンに Sphere オブジェクトを作成し、本スクリプトを追加します。
f:id:bluebirdofoz:20210705040601j:plain

今回はプレイヤーに追従させるため、[TargetObject]に[MainCamera]を指定します。
f:id:bluebirdofoz:20210705040611j:plain

動作確認

シーンを再生して動作を確認します。
プレイヤーの周囲を Sphere が指定半径で回転します。
f:id:bluebirdofoz:20210705040621j:plain

プレイヤーが移動しても一定距離を保ちながら追跡します。
f:id:bluebirdofoz:20210705040631j:plain

参考ページ

techblog.kayac.com

指定のトランスフォームを中心にオブジェクトを回転させる

本日は Unity の小ネタ枠です。
指定のトランスフォームを中心にオブジェクトを回転させる方法を記事にします。
f:id:bluebirdofoz:20210704235304j:plain

Transform.RotateAround

指定の座標を中心にオブジェクトを回転させるには Transform.RotateAround 関数を利用します。
docs.unity3d.com

public void RotateAround (Vector3 point, Vector3 axis, float angle);
引数 説明
point 中心となるワールド座標
axis 回転軸
angle 回転角度(度数)

サンプルシーン

以下のサンプルスクリプトを作成しました。
・RotateAroundTarget.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class RotateAroundTarget : MonoBehaviour
{
    [SerializeField, Tooltip("ターゲットオブジェクト")]
    private GameObject TargetObject;

    [SerializeField, Tooltip("回転軸")]
    private Vector3 RotateAxis = Vector3.up;

    [SerializeField, Tooltip("速度係数")]
    private float SpeedFactor = 0.1f;

    void Update()
    {
        if (TargetObject == null) return;

        // 指定オブジェクトを中心に回転する
        this.transform.RotateAround(
            TargetObject.transform.position,
            RotateAxis,
            360.0f / (1.0f / SpeedFactor) * Time.deltaTime
            );
    }
}

f:id:bluebirdofoz:20210704235332j:plain

シーンに Sphere オブジェクトを作成し、本スクリプトを追加します。
f:id:bluebirdofoz:20210704235352j:plain

中心となるオブジェクトを配置して[TargetObject]に指定します。
f:id:bluebirdofoz:20210704235404j:plain

動作確認

シーンを再生して動作を確認します。
指定オブジェクトの周囲を Sphere が回転します。
f:id:bluebirdofoz:20210704235414j:plain

ただし、この方法では半径距離をチェックしていないため、ターゲットオブジェクトが移動すると回転半径が変わってしまいます。
回転の半径距離を一定に保ちつつ、オブジェクトを回転させる方法は以下の記事を参照ください。
bluebirdofoz.hatenablog.com

HoloLens2でホロモンアプリを作る その44(ホロモンが一緒に頷いたり首を振る)

本日はアプリ作成枠です。
HoloLens2でホロモンアプリを作る進捗を書き留めていきます。
f:id:bluebirdofoz:20210703235245j:plain

今回はホロモンが一緒に頷いたり首を振るメモです。

ホロモンが一緒に頷いたり首を振る

以下の記事で作成した頭部ジェスチャーの検出ロジックをホロモンアプリにも組み込みました。
bluebirdofoz.hatenablog.com

首傾げの検出処理のスクリプトジェスチャー判定スクリプトへの参照を追加しています。
これにより、ホロモンがプレイヤーに注視しているときに頷きや首振りを検知します。
bluebirdofoz.hatenablog.com
・HeadObjectFeaturesSetter.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

using System;

using UniRx;
using UniRx.Triggers;

using HMProject.HoloMon;

namespace HMProject.Player
{
    /// <summary>
    /// 顔の状態識別
    /// </summary>
    [Serializable]
    public enum HeadStatus
    {
        /// <summary>
        /// 無し(デフォルト)
        /// </summary>
        Nothing = 0,
        /// <summary>
        /// 頷く
        /// </summary>
        Nod,
        /// <summary>
        /// 首振り
        /// </summary>
        Shake,
        /// <summary>
        /// 首傾げ
        /// </summary>
        Tilt,
    }

    [RequireComponent(typeof(ObjectFeatures))]
    public class HeadObjectFeaturesSetter : MonoBehaviour
    {
        [SerializeField, Tooltip("通常状態のオブジェクト名")]
        private HoloMonKnownObjectFeatures p_NormalHeadName;

        [SerializeField, Tooltip("頷いた状態のオブジェクト名")]
        private HoloMonKnownObjectFeatures p_NodHeadName;

        [SerializeField, Tooltip("首を振る状態のオブジェクト名")]
        private HoloMonKnownObjectFeatures p_ShakeHeadName;

        [SerializeField, Tooltip("首を傾げた状態のオブジェクト名")]
        private HoloMonKnownObjectFeatures p_TiltHeadName;


        [SerializeField, Tooltip("現在の顔の状態")]
        private HeadStatus p_CurrentHeadStatus;


        [SerializeField, Tooltip("顔の状態判定ロジックの参照")]
        private HeadGestureRecognizer p_HeadGestureRecognizer;


        // オブジェクトの特徴の設定参照
        private ObjectFeatures p_ObjectFeatures;


        /// <summary>
        /// 起動処理
        /// </summary>
        void Start()
        {
            // オブジェクトの特徴の参照を取得する
            p_ObjectFeatures = this.gameObject.GetComponent<ObjectFeatures>();

            // 初期状態は通常状態
            SettingNormal();

            // 判定イベントを登録する
            p_HeadGestureRecognizer.EventNothing += SettingNormal;
            p_HeadGestureRecognizer.EventNod += SettingNod;
            p_HeadGestureRecognizer.EventShake += SettingShake;
            p_HeadGestureRecognizer.EventTilt += SettingTilt;
        }


        /// <summary>
        /// 状態を通常状態に設定する
        /// </summary>
        private void SettingNormal()
        {
            p_ObjectFeatures.Features = p_NormalHeadName;
            p_CurrentHeadStatus = HeadStatus.Nothing;
        }

        /// <summary>
        /// 状態を頷き状態に設定する
        /// </summary>
        private void SettingNod()
        {
            p_ObjectFeatures.Features = p_NodHeadName;
            p_CurrentHeadStatus = HeadStatus.Nod;
        }

        /// <summary>
        /// 状態を首振り状態に設定する
        /// </summary>
        private void SettingShake()
        {
            p_ObjectFeatures.Features = p_ShakeHeadName;
            p_CurrentHeadStatus = HeadStatus.Shake;
        }

        /// <summary>
        /// 状態を首傾げ状態に設定する
        /// </summary>
        private void SettingTilt()
        {
            p_ObjectFeatures.Features = p_TiltHeadName;
            p_CurrentHeadStatus = HeadStatus.Tilt;
        }
    }
}

プレイヤーを注視しているので HeadLookController の[Override Animation]にチェックを入れます。
これで注視による頭の移動とアニメーションの頷き動作がミックスされたアニメーションが再生されます。
f:id:bluebirdofoz:20210703235318j:plain
bluebirdofoz.hatenablog.com

動作確認

シーンを再生して動作を確認します。
ホロモンがこちらを見ているときに、頷く動作をするとホロモンも頷いてくれます。
f:id:bluebirdofoz:20210703235329j:plain

また、首を振る動作をするとホロモンも首を振ってくれました。
f:id:bluebirdofoz:20210703235345j:plain