そういうのがいいブログ

アプリ個人開発 まるブログ

アプリ開発覚え書き

【Unity】VRM1.0 表情を動かすサンプル

要件

UniVRM:v0.113.0
Unity: 2021.3.27f1

はじめに

これまでVRMモデルを扱う際、
SkinnedMeshRendererを使用して表情変更ができていましたが、
VRM1.0になり仕様が変わったためメモです。

SkinnedMeshRendererを経由して操作できなくなったもの

2体しか確認していませんが、
BlendShapes名でいう以下のものがSkinnedMeshRendererから操作不可となっていました。
Fcl_ALL_Neutral
Fcl_ALL_Angry
Fcl_ALL_Fun
Fcl_ALL_Joy
Fcl_ALL_Sorrow
Fcl_ALL_Surprised

Fcl_EYE_Close
Fcl_EYE_Close_R
Fcl_EYE_Close_L

Fcl_MTH_A
Fcl_MTH_I
Fcl_MTH_U
Fcl_MTH_E
Fcl_MTH_O

happyの表情の値を変更

VRMInstanceコンポーネントが割りついているオブジェクトに
以下のスクリプトを付けることでhappyの表情変化をさせることができます。

using System.Collections;
using UnityEngine;
using UniVRM10;

public class Test : MonoBehaviour
{
    // VRMモデルのVrm10Instanceコンポーネントへの参照
    private Vrm10Instance vrmInstance;

    // VRMモデルのVrm10RuntimeExpressionコンポーネントへの参照
    private Vrm10RuntimeExpression vrm10RuntimeExpression;

    // Happy表情のExpressionKey
    private ExpressionKey happyExpressionKey;

    // 増減する間隔と速度
    public float interval = 0.1f;
    public float speed = 5.0f;

    // 増減中かどうかのフラグ
    private bool isIncreasing = true;

    private void Start()
    {
        vrmInstance = gameObject.GetComponent<Vrm10Instance>();

        // Vrm10InstanceからVrm10RuntimeExpressionコンポーネントを取得
        vrm10RuntimeExpression = vrmInstance.Runtime.Expression;

        // Happy表情のExpressionKeyを作成
        foreach (var key in vrm10RuntimeExpression.ExpressionKeys)
        {
            if (key.Preset == ExpressionPreset.happy)
            {
                happyExpressionKey = key;
                break;
            }
        }


        // コルーチンを開始
        StartCoroutine(ChangeExpression());
    }

    private IEnumerator ChangeExpression()
    {
        while (true)
        {
            // 値を増減
            if (isIncreasing)
            {
                // 表情の値を増加
                vrm10RuntimeExpression.SetWeight(happyExpressionKey, Mathf.Clamp01(vrm10RuntimeExpression.GetWeight(happyExpressionKey) + speed * Time.deltaTime));
            }
            else
            {
                // 表情の値を減少
                vrm10RuntimeExpression.SetWeight(happyExpressionKey, Mathf.Clamp01(vrm10RuntimeExpression.GetWeight(happyExpressionKey) - speed * Time.deltaTime));
            }

            // 値が範囲外になった場合、方向を反転
            if (vrm10RuntimeExpression.GetWeight(happyExpressionKey) >= 1.0f || vrm10RuntimeExpression.GetWeight(happyExpressionKey) <= 0.0f)
            {
                isIncreasing = !isIncreasing;
            }

            // 一定時間待機
            yield return new WaitForSeconds(interval);
        }
    }
}

違う表情

以下の部分をExpressionPreset.sadやら違うものに変更すれば良いです。

ExpressionPreset.happy


VRoid Studioで作成したモデルに対して
スクリプトから使える表情を確認してみると・・・

using UnityEngine;
using UniVRM10;

public class Test: MonoBehaviour
{

    public Vrm10Instance vrmInstance;

    private Vrm10RuntimeExpression vrm10RuntimeExpression;

    private void Start()
    {
        vrmInstance = gameObject.GetComponent<Vrm10Instance>();
        vrm10RuntimeExpression = vrmInstance.Runtime.Expression;

        // カスタムの表情が含まれているかをチェック
        bool hasCustomExpression = CheckForCustomExpression();

        Debug.Log("Has Custom Expression: " + hasCustomExpression);

        // 利用可能なすべての表情の一覧を表示
        ListAllExpressions();
    }

    private bool CheckForCustomExpression()
    {
        foreach (ExpressionKey expressionKey in vrm10RuntimeExpression.ExpressionKeys)
        {
            if (expressionKey.Preset == ExpressionPreset.custom)
            {
                return true;
            }
        }
        return false;
    }

    private void ListAllExpressions()
    {
        Debug.Log("Available Expressions:");
        foreach (ExpressionKey expressionKey in vrm10RuntimeExpression.ExpressionKeys)
        {
            Debug.Log(expressionKey.ToString());
        }
    }
}


実行結果

おわりに

VRM1.0対応がこれからの方は参考のスライドを見ると良いです。

参考

speakerdeck.com