【Unity(C#)】Oculus Integrationで手のマテリアル切り替え


デモ

さっそくデモです。

オブジェクトに触れると手のマテリアルが触れたオブジェクトと同じものに変わります。

この情報だけでコードが頭の中に完成してしまう人も少なくないかと思いますが、
なぜか、望んだ結果が得られず原因特定に時間を要したのでメモしておきます。

望んだ結果が得られなかったコード

触れられるオブジェクト側にアタッチ
public class HandTriggerEnterEvent : MonoBehaviour
{
    private MeshRenderer _meshRenderer;

    private void Start()
    { 
        _mshRenderer = this.gameObject.GetComponent<MeshRenderer>();
    }

    private void OnTriggerEnter(Collider other)
    {
        //この書き方については今回の本題ではないのでスルー安定
        SkinnedMeshRenderer skinnedMeshRenderer = other.gameObject.transform.parent.parent.parent.GetComponent<SkinnedMeshRenderer>();

        if (skinnedMeshRenderer != null)
        {
            Debug.Log("Get Color");

            //マテリアルを手に適用
            skinnedMeshRenderer.material = _meshRenderer.material;
        }
        else
        {
            Debug.Log("skinnedMeshRenderer is Null");
        }
    }
}

一見すると触れられたらマテリアルを手に反映させる
ただそれだけのコードで問題ないように思えますが、
マテリアルは変化しません。

原因

原因はOVRHandPrefabにアタッチされたOVRMeshRendererのUpdate内のコードにありました。

private void Update()
{
    IsDataValid = false;
    IsDataHighConfidence = false;
    ShouldUseSystemGestureMaterial = false;

    if (IsInitialized)
    {
        bool shouldRender = false;

        if (_dataProvider != null)
        {
            var data = _dataProvider.GetMeshRendererData();

            IsDataValid = data.IsDataValid;
            IsDataHighConfidence = data.IsDataHighConfidence;
            ShouldUseSystemGestureMaterial = data.ShouldUseSystemGestureMaterial;

            shouldRender = data.IsDataValid && data.IsDataHighConfidence;
        }

        if (_confidenceBehavior == ConfidenceBehavior.ToggleRenderer)
        {
            if (_skinnedMeshRenderer != null && _skinnedMeshRenderer.enabled != shouldRender)
            {
                _skinnedMeshRenderer.enabled = shouldRender;
            }
        }

        if (_systemGestureBehavior == SystemGestureBehavior.SwapMaterial)
        {
            if (_skinnedMeshRenderer != null)
            {
                if (ShouldUseSystemGestureMaterial && _systemGestureMaterial != null && _skinnedMeshRenderer.sharedMaterial != _systemGestureMaterial)
                {
                    _skinnedMeshRenderer.sharedMaterial = _systemGestureMaterial;
                }
                else if (!ShouldUseSystemGestureMaterial && _originalMaterial != null && _skinnedMeshRenderer.sharedMaterial != _originalMaterial)
                {
                    _skinnedMeshRenderer.sharedMaterial = _originalMaterial;
                }
            }
        }
    }
#if UNITY_EDITOR
    else
    {
        if (OVRInput.IsControllerConnected(OVRInput.Controller.Hands))
        {
            Initialize();
        }
    }
#endif
}

SystemGestureBehavior.SwapMaterialというステートに設定している場合、
マテリアルが変更されてもすぐに_originalMaterialに戻されてしまいます。

なので、Inspectorでデフォルト設定されているSystem Gesture BehaviorSwapMaterialNoneに変更すればOKでした。

余談ですが、Confidence Behavior
デフォルトでToggle Rendererに設定されているパラメーターで、
手のトラッキング状況に応じてRendererをオンオフして切り替えてくれます。

SystemGestureBehavior

System Gesture Behaviorは、OculusQuestのハンドトラッキング機能に
デフォルトで備わっている
"ホーム画面に戻るジェスチャーの予備動作(手のひらを自身に向ける)"に関わる機能です。

SwapMaterialを設定している場合、
"ホーム画面に戻るジェスチャーの予備動作(手のひらを自身に向ける)"を行うと
手のマテリアルがSystem Gesture Materialに設定したマテリアルに変更されます。

もし、このSystem Gesture BehaviorSwapMaterialも利用しつつ
他のマテリアルとの入れ替えも実装したいとなった場合、
下記のように少しコードを書き換える必要があります。

OVRMeshRendererのUpdate内のコード
if (_systemGestureBehavior == SystemGestureBehavior.SwapMaterial)
{
    if (_skinnedMeshRenderer != null)
    {
        if (ShouldUseSystemGestureMaterial && _systemGestureMaterial != null && _skinnedMeshRenderer.sharedMaterial != _systemGestureMaterial)
        {
            Debug.Log("Get Current Material");
            _originalMaterial = GetComponent<SkinnedMeshRenderer>().sharedMaterial;
            _skinnedMeshRenderer.sharedMaterial = _systemGestureMaterial;
        }
        else if (!ShouldUseSystemGestureMaterial && _originalMaterial != null && _skinnedMeshRenderer.sharedMaterial == _systemGestureMaterial)
        {
            _skinnedMeshRenderer.sharedMaterial = _originalMaterial;
        }
    }
}

参考リンク

SkinnedMeshRenderer material from OVRHand prefab is overwritten every frame