ã©ãããåéã§ãã
æ¨æ¥ãXR Handsã®ãµã³ãã«ã触ã£ã¦ããç¨åº¦æåãåãã£ãã®ã§ãã³ã³ãã³ããä½ããã¨æã£ãã®ã§ãããOculus Integrationã¨éã£ã¦ããã©ã«ãã§ããæãã«æã®ãã¼ãºåå¾ã§ãããã®ãç¾ç¶ãªãã®ã§ãèªåã§æ¸ãã¦ã¿ã¾ããã
XR Handsã®ãµã³ãã«è§¦ã£ããã¡ãªãããæ¸ããæ¨æ¥ã®è¨äºã¯ãã¡ã
åé¢ç¯ã®ãã¼ã¿ãåå¾ãã
XR Handsã®HandVisualizerã®ãµã³ãã«ã³ã¼ããè¦ã¦ã¿ãã¨ããç¨åº¦ã®ä½¿ãæ¹ã¯ãããã¨æãã¾ãããæ¡å¼µæ§çç¡ãªã®ã§èªåã§æ¸ãã¦ããã¾ãã
XR Handsã®è©³ããä»æ§ãæ¸ãã¦ããã¹ã¯ãªãããªãã¡ã¬ã³ã¹ã¯ãã¡ã
docs.unity3d.com
XRHandSubsystemã®åå¾
ãã³ããã©ããã³ã°ã®ãã¼ã¿ã«ã¢ã¯ã»ã¹ããã«ã¯XRHandSubsystemã使ãã¾ãã®ã§ãåå¾ããå¿ è¦ãããã¾ãã
subSystem = XRGeneralSettings.Instance?.Manager?.activeLoader?.GetLoadedSubsystem<XRHandSubsystem>();
ã§åå¾ã§ãã¾ãã
å·¦æå³æã®åå¾
å·¦å³ã®æã®ãã¼ã¿ãåã£ã¦ãã¾ãã
// å·¦æ hand = subSystem.leftHand // å³æ hand = subSystem.rightHand
ã§åå¾ã§ãã¾ãã
åé¢ç¯ã®ãã¼ãºãã¼ã¿ã®åå¾
çæã®ãã¼ã¿ãåå¾ããããæã®åé¢ç¯ã®ãã¼ãºãåå¾ãã¾ãã
hand.GetJoint(xrHandJointID).TryGetPose(out Pose pose);
ããã§åé¢ç¯ã®Pose(positionã¨rotation)ãåå¾ã§ãã¾ãã
ããã§XRHandJointIDã¨ãããã®ãåºã¦ãã¾ããããããã¯ã©ãã®é¢ç¯ãã示ãIDã§ãã
â
XRHandJointID
ãããæåã¡ãã£ã¨ãããããã¦ææ¡ããã®ã«æéããããã¾ããã
namespace UnityEngine.XR.Hands { public enum XRHandJointID { Invalid = 0, BeginMarker = 1, Wrist = 1, Palm = 2, ThumbMetacarpal = 3, ThumbProximal = 4, ThumbDistal = 5, ThumbTip = 6, IndexMetacarpal = 7, IndexProximal = 8, IndexIntermediate = 9, IndexDistal = 10, // 0x0000000A IndexTip = 11, // 0x0000000B MiddleMetacarpal = 12, // 0x0000000C MiddleProximal = 13, // 0x0000000D MiddleIntermediate = 14, // 0x0000000E MiddleDistal = 15, // 0x0000000F MiddleTip = 16, // 0x00000010 RingMetacarpal = 17, // 0x00000011 RingProximal = 18, // 0x00000012 RingIntermediate = 19, // 0x00000013 RingDistal = 20, // 0x00000014 RingTip = 21, // 0x00000015 LittleMetacarpal = 22, // 0x00000016 LittleProximal = 23, // 0x00000017 LittleIntermediate = 24, // 0x00000018 LittleDistal = 25, // 0x00000019 LittleTip = 26, // 0x0000001A EndMarker = 27, // 0x0000001B } }
ããã ãè¦ã¦ãã¨ãªãã®ãã£ã¡ãããããªãã®ã§ããã å³ã«ããã¨åãããããããªã¨æãã¾ãã
Enumã¨intã®å¤æ
XRHandJointIDUtilityã使ã£ã¦å¤æããªãã¨é åãä¸åããããããã®ã§æ³¨æãå¿ è¦ã§ãã(ããã«æ°ãã¤ããªãã¦çµæ§æé使ã£ãâ¦)
Enumããintã¸ã®å¤æ
XRHandJointID.IndexTip.ToIndex()
intããEnumã¸ã®å¤æ
XRHandJointIDUtility.FromIndex(jointID)
æã®ãã¼ãºãåå¾ãã
ããã¾ã§ã§åé¢ç¯ã®ãã¼ã¿ãåå¾ã§ããã®ã§ãæã®ãã¼ãºãæ¨å®ãã¦ããã¾ãã
åæã®éãå ·åãåå¾ãã
ãã®ã¡ã½ããã§ã¯äººå·®ãæã®éãå
·åã0ãã1ã®éã§åå¾ã§ãã¾ããå®è£
çã«ã¯åç´ã§ã人差ãæã®å
ã¨æ ¹æ¬ã®è·é¢ã§éãå
·åãè¨ç®ãã¦ãã ãã§ãã(ã ãã¶ç¡çããã§ããã¨ãããã)
ãã®ä»ã®æã«ãåããããªã¡ã½ãããä½ã£ã¦ããããã¨ã§æã®ãã¼ãºãã ãããææ¡ã§ãã¾ãã
public float RatioIndex(bool isLeft) { JointPoseData[] jointPoseData = isLeft ? _leftJointPoseData : _rightJointPoseData; return Mathf.InverseLerp(0.04f, 0.13f, Vector3.Distance( jointPoseData[XRHandJointID.IndexMetacarpal.ToIndex()].Pose.position, jointPoseData[XRHandJointID.IndexTip.ToIndex()].Pose.position)); }
â»JointPoseDataã¯é¢ç¯ãã¨ã®ãã¼ã¿ãä¿æãã¦ãèªä½ã®ã¯ã©ã¹ã
Mathf.InverseLerpã使ããã¨ã§æãéãã¦ãéã¨éãã¦ãéã®è·é¢ã0ãã1ã®éã«ä¸¸ãã¦ã¾ãã
åå¾ãããã¼ã¿ã表示
0ãã1ã§æã®éãå ·åãåå¾ãã¦ããã®ã§ããã®ã¾ã¾Sliderã«æµãè¾¼ãã§ãããã°ãããæãã«ãã¼ã¿ãå¯è¦åã§ãã¾ãã
å®æå½¢ã¯ãã¡ã
XR Handsã§ããããæã®åãããæãã«åå¾ã§ããããã«ãªã£ãï½ï½
— åé (@donadonadonabe) 2023å¹´3æ12æ¥
Oculus Integrationã¨éã£ã¦ããã©ã«ãã§ããæãã«æã®ãã¼ãºåå¾ã§ãããã¤ããªãã¦ã³ã¼ãã¨ã«ããã£ããã¦ããã¡ãã£ã¨æéããã£ã¦ãã¾ã£ãã
試ãã«æãã弾丸æ¾ã¤ã®ããã¹ã pic.twitter.com/IO46MP08Jh
ã¾ã¨ã
ä»å¾ãã£ã¨ä½¿ãããããã®ãå ¬å¼ããåºã¦ããããããã¾ããããã¨ããããããã§æã®ãã¼ãºãåå¾ã§ããããã«ãªãã¾ãããæã®åããå é度ãåå¾ã§ããã®ã§ãã£ã¨æ¡å¼µããã°è²ã éã¹ããã§ããã
以ä¸ã«ä¸é¨çç¥ãã¦ã¾ããä»åæ¸ããã¹ã¯ãªãããè²¼ã£ã¦ããã¾ãã ã¡ãã£ã¨éãªå®è£ ãªã®ã§ãæ¥ããããã§ããã大ã¾ããªä½¿ãæ¹ã¯åããã¨æãã¾ãã
ä»åæ¸ããã¹ã¯ãªãã
public class JointPoseData { public Pose Pose; public XRHandJointID XRHandJointID => handJointID; private XRHandSubsystem m_Subsystem; private XRHand hand; private XRHandJointID handJointID; public JointPoseData(bool isLeft, int jointID) { handJointID = XRHandJointIDUtility.FromIndex(jointID); m_Subsystem = XRGeneralSettings.Instance?.Manager?.activeLoader?.GetLoadedSubsystem<XRHandSubsystem>(); if (m_Subsystem != null) { hand = isLeft ? m_Subsystem.leftHand : m_Subsystem.rightHand; } } public void UpdatePose() { hand.GetJoint(handJointID).TryGetPose(out Pose pose); Pose = pose; } }
public class HandDataPresenter : MonoBehaviour { [SerializeField] private GameObject prefab; [SerializeField] private GameObject ball; private JointPoseData[] _leftJointPoseData = new JointPoseData[XRHandJointID.EndMarker.ToIndex()]; private JointPoseData[] _rightJointPoseData = new JointPoseData[XRHandJointID.EndMarker.ToIndex()]; private List<GameObject> leftJointObj = new List<GameObject>(); private List<GameObject> rightJointObj = new List<GameObject>(); private float timeElapsed; private void Start() { for (int i = 0; i < XRHandJointID.EndMarker.ToIndex(); i++) { _leftJointPoseData[i] = new JointPoseData(true, i); GameObject leftobj = Instantiate(prefab); leftobj.GetComponent<HandDataView>().HandJointID = _leftJointPoseData[i].XRHandJointID; leftobj.name = "Left" + _leftJointPoseData[i].XRHandJointID; leftJointObj.Add(leftobj); _rightJointPoseData[i] = new JointPoseData(false, i); GameObject rightobj = Instantiate(prefab); rightobj.GetComponent<HandDataView>().HandJointID = _rightJointPoseData[i].XRHandJointID; rightobj.name = "Right" + _rightJointPoseData[i].XRHandJointID; rightJointObj.Add(rightobj); } } private void Update() { for (int i = 0; i < XRHandJointID.EndMarker.ToIndex(); i++) { _leftJointPoseData[i].UpdatePose(); leftJointObj[i].transform.position = _leftJointPoseData[i].Pose.position; leftJointObj[i].transform.rotation = _leftJointPoseData[i].Pose.rotation; if (RatioIndex(true) > 0.95f && RatioMiddle(true) < 0.1f) { timeElapsed += Time.deltaTime; if (timeElapsed >= 30) { Instantiate(ball, _leftJointPoseData[XRHandJointID.IndexTip.ToIndex()].Pose.position, _leftJointPoseData[XRHandJointID.IndexTip.ToIndex()].Pose.rotation) .GetComponent<Rigidbody>() .AddRelativeForce(0, 0, 500); timeElapsed = 0.0f; } } _rightJointPoseData[i].UpdatePose(); rightJointObj[i].transform.position = _rightJointPoseData[i].Pose.position; rightJointObj[i].transform.rotation = _rightJointPoseData[i].Pose.rotation; } } public float RatioIndex(bool isLeft) { JointPoseData[] handPoseData = isLeft ? _leftJointPoseData : _rightJointPoseData; return Mathf.InverseLerp(0.04f, 0.13f, Vector3.Distance( handPoseData[XRHandJointID.IndexMetacarpal.ToIndex()].Pose.position, handPoseData[XRHandJointID.IndexTip.ToIndex()].Pose.position)); } public float RatioMiddle(bool isLeft) { JointPoseData[] handPoseData = isLeft ? _leftJointPoseData : _rightJointPoseData; return Mathf.InverseLerp(0.04f, 0.14f, Vector3.Distance( handPoseData[XRHandJointID.MiddleMetacarpal.ToIndex()].Pose.position, handPoseData[XRHandJointID.MiddleTip.ToIndex()].Pose.position)); } public float RatioRing(bool isLeft) { JointPoseData[] handPoseData = isLeft ? _leftJointPoseData : _rightJointPoseData; return Mathf.InverseLerp(0.035f, 0.13f, Vector3.Distance( handPoseData[XRHandJointID.RingMetacarpal.ToIndex()].Pose.position, handPoseData[XRHandJointID.RingTip.ToIndex()].Pose.position)); } public float RatioLittle(bool isLeft) { JointPoseData[] handPoseData = isLeft ? _leftJointPoseData : _rightJointPoseData; return Mathf.InverseLerp(0.032f, 0.11f, Vector3.Distance( handPoseData[XRHandJointID.LittleMetacarpal.ToIndex()].Pose.position, handPoseData[XRHandJointID.LittleTip.ToIndex()].Pose.position)); } public float RatioThumb(bool isLeft) { JointPoseData[] handPoseData = isLeft ? _leftJointPoseData : _rightJointPoseData; return Mathf.InverseLerp(0.075f, 0.083f, Vector3.Distance( handPoseData[XRHandJointID.ThumbMetacarpal.ToIndex()].Pose.position, handPoseData[XRHandJointID.ThumbTip.ToIndex()].Pose.position)); } }