本日は機能に引き続きHoloLens MeetupVol.21でのLT登壇内容に関する記事です。
〇HoloLensMeetupとは?
〇ハンドトラッキングで握手をする。
HoloLens2のHandTrackingは手の位置だけでなく間接ごとの動きでポーズや手のひらの向きを検出することができます。
今回取り組んでいるキャラクターをよりデータではなくキャラクターとして扱うための表現の実験の一つとしてキャラクターと握手をできる仕組みに取り組みました。
〇キャラクターの手を動かす。
VRMのキャラクターモデルはUnityHumanoidBoneを持っているためHumanoidのAnimationを使用して動かすことができます。
今回はAnimationではなくIKを使用して動かします。 IKとは[Inverse Kinematics]の意味で、キャラクターのボーン構造を使用してアニメーションさせる手法ですが、最初に手先、頭、腰、足などの最終的な位置を決めてそれに対応するようにほかのボーンを逆算していく方法です。
今回の場合キャラクターと握手をするためにはユーザーの手の位置があり、デジタルなキャラクターの手とユーザーの手が重なるようにすることで握手が可能です。
この手の位置から逆算して動かすために今回は[FinalIK]のアセットを使用しました。
FinalIKは90$ほどで販売されていますが、頻繁にセールが行われ筆者は以前45$の際に購入しました。
FinalIKの中に[VR IK]というコンポーネントがあります。 このコンポーネントはアタッチされたオブジェクトの子オブジェクトからボーンの構造を取得します。
[Solver]には[Target]と呼ばれる『対応する四肢や頭をどのオブジェクトの位置に移動させるか?』を指定することができます。
・キャラクターのゲームオブジェクトにVRIKコンポーネントをアタッチします。
・空のゲームオブジェクトを作成し[ShakeHands]と名付け、VRIKの[Solver]の[Right Hand]の[Target]に指定します。
・Unityで実行し[ShakeHands]の[Transform]を動かすとそれに合わせてキャラクターの右手の位置が変わることが確認できます。
〇スクリプト
次に[HandTarget]の位置とユーザーの手の位置を同期させます。
次のようなスクリプトを書きました。
using System.Collections; using System.Collections.Generic; using UnityEngine; using Microsoft.MixedReality.Toolkit.Input; using Microsoft.MixedReality.Toolkit.Utilities; public class HandTarget : MonoBehaviour { [SerializeField] bool isShakeHands; GameObject _rightHand; Vector3 _righthandpos; Quaternion _rigthandRot; // Update is called once per frame void Update() { if (isShakeHands) { if (HandJointUtils.TryGetJointPose(TrackedHandJoint.IndexTip, Handedness.Right, out MixedRealityPose pose)) { _rightHand = GameObject.Find("Wrist Proxy Transform"); _righthandpos = _rightHand.transform.position; _righthandpos.z += 0.12f; _righthandpos.x -= 0.1f; _righthandpos.y += 0.02f; this.transform.position = _righthandpos; } else { _rightHand = null; Debug.Log("hand not find"); } } } }
if (HandJointUtils.TryGetJointPose(TrackedHandJoint.IndexTip, Handedness.Right, out MixedRealityPose pose)) { ... } else { ... }
HandJointUtils.TryGetJointPose()で右手の検出を行っています。これは以前調査したHoloLens 2でのHandTrackingの検出を応用しています。
if (HandJointUtils.TryGetJointPose(TrackedHandJoint.IndexTip, Handedness.Right, out MixedRealityPose pose)) { _rightHand = GameObject.Find("Wrist Proxy Transform"); _righthandpos = _rightHand.transform.position; //手の位置とキャラクターの手の重なりのずれを補正 _righthandpos.z += 0.12f; _righthandpos.x -= 0.1f; _righthandpos.y += 0.02f; this.transform.position = _righthandpos; } else { _rightHand = null; Debug.Log("hand not find"); }
右手が検出された場合_rightHandのゲームオブジェクトに[Wrist Proxy Transform]のオブジェクトを取得して代入しています。
この[Wrist Proxy Transform]は手首のジョイントを指します。
今回の場合指ではなく手のルートの位置情報が欲しいので手首のジョイントを指定しました。
このスクリプトを[Shake Hand]のオブジェクトにアタッチします。
これで右手が検出された場合キャラクターの手をユーザーの右手に重ねます。
以上でキャラクターと握手する機能ができました。
〇実機で確認
実機で確認すると次のようになります。