指の動きを制限したい
今までViveしか触ったことがなかったため、
OculusTouchコントローラーで人差し指と親指が動かせると知った時には感動しました。
しかし、過去にViveで遊んできたコンテンツ、作成してきたコンテンツの中で
「ここで人差し指とか親指が動かせたらな~」と思ったことは さほどありませんでした。
実際にOculusQuestで開発を進めていると、
指が動くとかえって邪魔になるという場面にすら遭遇しました。
なので、開発に応じて簡単に指の動きの制限を切り替えられる機能は無いのか? と思い調べました。
着地点は下記実装です。
①コブシを握る
②握ったコブシを開く
③必要に応じて指を動かせるようにする
Hand
Hand
クラスの中に指のアニメーションのオンオフ切り替え機能がありました。
PrefabはOculus
-SampleFramework
-Core
-CustomHands
-CustomHandLeft(Right)
にあります。
Inspectorはこんな感じです。
この中で今回のアニメーションの切り替えに関係しているプロパティがあります。
Hand
の中のDefault Grab Pose
です。
初期状態ではPrefabの中のHandPoseDefaultPf
がアタッチされています。
HandPoseDefaultPf
のInspectorはこうです。
このAllow Pointing、Allow Thumbs Upが人差し指、親指の制御を担っています。
今回は使いませんが、Pose IDも便利な機能を持っていて、物に応じてつかんだ時のポーズを設定できます。
コード
少しHand
メソッドの中を見ていきます。
private void UpdateAnimStates()
{
bool grabbing = m_grabber.grabbedObject != null;
HandPose grabPose = m_defaultGrabPose;
if (grabbing)
{
HandPose customPose = m_grabber.grabbedObject.GetComponent<HandPose>();
if (customPose != null) grabPose = customPose;
}
// Pose
HandPoseId handPoseId = grabPose.PoseId;
m_animator.SetInteger(m_animParamIndexPose, (int)handPoseId);
// Flex
// blend between open hand and fully closed fist
float flex = OVRInput.Get(OVRInput.Axis1D.PrimaryHandTrigger, m_controller);
m_animator.SetFloat(m_animParamIndexFlex, flex);
// Point
bool canPoint = !grabbing || grabPose.AllowPointing;
float point = canPoint ? m_pointBlend : 0.0f;
m_animator.SetLayerWeight(m_animLayerIndexPoint, point);
// Thumbs up
bool canThumbsUp = !grabbing || grabPose.AllowThumbsUp;
float thumbsUp = canThumbsUp ? m_thumbsUpBlend : 0.0f;
m_animator.SetLayerWeight(m_animLayerIndexThumb, thumbsUp);
float pinch = OVRInput.Get(OVRInput.Axis1D.PrimaryIndexTrigger, m_controller);
m_animator.SetFloat("Pinch", pinch);
}
下記の箇所で掴んだ物に応じたアニメーション制御を可能にしています。
掴んだ物にHandPoseをアタッチしてPose Id
を設定することで可能です。
HandPose grabPose = m_defaultGrabPose;
if (grabbing)
{
HandPose customPose = m_grabber.grabbedObject.GetComponent<HandPose>();
if (customPose != null) grabPose = customPose
}
今回の目的は、
①コブシを握る
②握ったコブシを開く
③必要に応じて指を動かせるようにする
だけに制限することなので使いませんが非常に便利かと思います。
修正する箇所は下記のcanPointとcanThumbsUpです。
// Point
//bool canPoint = !grabbing || grabPose.AllowPointing;
bool canPoint = grabPose.AllowPointing && !grabbing;
float point = canPoint ? m_pointBlend : 0.0f;
m_animator.SetLayerWeight(m_animLayerIndexPoint, point);
// Thumbs up
//bool canThumbsUp = !grabbing || grabPose.AllowThumbsUp;
bool canThumbsUp = grabPose.AllowThumbsUp && !grabbing;
float thumbsUp = canThumbsUp ? m_thumbsUpBlend : 0.0f;
m_animator.SetLayerWeight(m_animLayerIndexThumb, thumbsUp);
短絡評価1を使用しているのでややこしくみえますが、
改良前のコード(コメントアウトしてある箇所)は、先にgrabbing(物をつかんでいるか)の評価を行っているので、
canPoint
とcanThumbsUp
のTrue、Falseに関わらず指が動いてしまいます。
つまり、改良前のコード(コメントアウトしてある箇所)は物をつかんだ時に指が動くかどうかのみを制限しているということです。
改良後のコードでは根本的に指を動かすことを制限するために、判定を逆にしています。
これにより、
①コブシを握る
②握ったコブシを開く
③必要に応じて指を動かせるようにする
という制限を与える事が可能になりました。
より厳密に
制限を加えることができましたが、まだ厳密には制限しきれていません。
画像のように、つまむ動作に関しては反映されてしまいます。
これはアニメーターの設定がゴリゴリになっていることが原因です。
右手がアニメーションを整理する前、左手が整理した後です。
見ても何も伝わらないと思いますのでアニメーターの設定変更と最後のコードのコピペをして試してみてほしいです。
最終的なコード
いらないと思ったものはガンガン削除しました。
一応、別のScriptとして作成しました。
using System.Linq;
using UnityEngine;
# if UNITY_EDITOR
using UnityEngine.SceneManagement;
# endif
namespace OVRTouchSample
{
[RequireComponent(typeof(OVRGrabber))]
[RequireComponent(typeof(HandPose))]
public class HandAction : MonoBehaviour
{
public const string ANIM_LAYER_NAME_POINT = "Point Layer";
public const string ANIM_LAYER_NAME_THUMB = "Thumb Layer";
public const string ANIM_PARAM_NAME_FLEX = "Flex";
public const float THRESH_COLLISION_FLEX = 0.9f;
public const float INPUT_RATE_CHANGE = 20.0f;
[SerializeField]
private OVRInput.Controller m_controller;
[SerializeField]
private Animator m_animator = null;
private Collider[] m_colliders = null;
private bool m_collisionEnabled = true;
private OVRGrabber m_grabber;
private HandPose grabPose;
private int m_animLayerIndexThumb = -1;
private int m_animLayerIndexPoint = -1;
private int m_animParamIndexFlex = -1;
private bool m_isPointing = false;
private bool m_isGivingThumbsUp = false;
private float m_pointBlend = 0.0f;
private float m_thumbsUpBlend = 0.0f;
private void Start()
{
m_grabber = GetComponent<OVRGrabber>();
grabPose = this.gameObject.GetComponent<HandPose>();
// Collision starts disabled. We'll enable it for certain cases such as making a fist.
m_colliders = this.GetComponentsInChildren<Collider>().Where(childCollider => !childCollider.isTrigger).ToArray();
// Get animator layer indices by name, for later use switching between hand visuals
m_animLayerIndexPoint = m_animator.GetLayerIndex(ANIM_LAYER_NAME_POINT);
m_animLayerIndexThumb = m_animator.GetLayerIndex(ANIM_LAYER_NAME_THUMB);
m_animParamIndexFlex = Animator.StringToHash(ANIM_PARAM_NAME_FLEX);
# if UNITY_EDITOR
OVRPlugin.SendEvent("custom_hand", (SceneManager.GetActiveScene().name == "CustomHands").ToString(), "sample_framework");
# endif
}
private void Update()
{
UpdateCapTouchStates();
m_pointBlend = InputValueRateChange(m_isPointing, m_pointBlend);
m_thumbsUpBlend = InputValueRateChange(m_isGivingThumbsUp, m_thumbsUpBlend);
float flex = OVRInput.Get(OVRInput.Axis1D.PrimaryHandTrigger, m_controller);
bool collisionEnabled = m_grabber.grabbedObject == null && flex >= THRESH_COLLISION_FLEX;
UpdateAnimStates();
}
// Just checking the state of the index and thumb cap touch sensors, but with a little bit of
// debouncing.
private void UpdateCapTouchStates()
{
m_isPointing = !OVRInput.Get(OVRInput.NearTouch.PrimaryIndexTrigger, m_controller);
m_isGivingThumbsUp = !OVRInput.Get(OVRInput.NearTouch.PrimaryThumbButtons, m_controller);
}
private float InputValueRateChange(bool isDown, float value)
{
float rateDelta = Time.deltaTime * INPUT_RATE_CHANGE;
float sign = isDown ? 1.0f : -1.0f;
return Mathf.Clamp01(value + rateDelta * sign);
}
private void UpdateAnimStates()
{
bool grabbing = m_grabber.grabbedObject != null;
// Flex
float flex = OVRInput.Get(OVRInput.Axis1D.PrimaryHandTrigger, m_controller);
m_animator.SetFloat(m_animParamIndexFlex, flex);
// Point
bool canPoint = grabPose.AllowPointing&&!grabbing;
float point = canPoint ? m_pointBlend : 0.0f;
m_animator.SetLayerWeight(m_animLayerIndexPoint, point);
// Thumbs up
bool canThumbsUp = grabPose.AllowThumbsUp && !grabbing;
float thumbsUp = canThumbsUp ? m_thumbsUpBlend : 0.0f;
m_animator.SetLayerWeight(m_animLayerIndexThumb, thumbsUp);
}
}
}
なんかOculusの権利関係ややこしそうなので、
コピーライティング書いときます。
Copyright © Facebook Technologies, LLC and
its affiliates. All rights reserved.