XR Interaction Toolkit 2.4.3 (Custom Hand Pose) OpenXR
VR/XR Interaction Toolkit 2023. 12. 29. 17:31
https://assetstore.unity.com/packages/3d/props/weapons/long-sword-212082
Long Sword | 3D 무기 | Unity Asset Store
Elevate your workflow with the Long Sword asset from VarcanRex. Find this & other 무기 options on the Unity Asset Store.
assetstore.unity.com
위 리소스를 다운받아 설치 하고
새 씬을 만들어 줍니다.
![](https://blog.kakaocdn.net/dn/cakY10/btsCMpB5smc/QsFAcVZTFlDW5HvLWFiSfk/img.png)
메인 카메라를 제거 하고 XR Origin을 생성 합니다.
![](https://blog.kakaocdn.net/dn/chEOSa/btsCLrGRFC8/Ljnxma8qEVzOCijYh6Aso1/img.png)
Tracking Origin Mode를 Floor로 설정 하고 XR Origin (XR Rig) 와 Camera Offset의 위치를 0으로 초기화 합니다.
![](https://blog.kakaocdn.net/dn/bMWAfe/btsCKSEHjAY/0dXDnYPjNbw1WjdsDxxht0/img.png)
Left / Right Controller를 선택하고
![](https://blog.kakaocdn.net/dn/bY5pak/btsCIEGHNos/3OwEDTkBwfHhkbwq51b14K/img.png)
XR Controller 컴포넌트를 제외한 나머지 컴포넌트들을 제거 합니다.
![](https://blog.kakaocdn.net/dn/s2JGi/btsCOHhVMpB/R5bVirsXZVoLJBaPvvpjpK/img.png)
다음 구조로 만들어 주고
![](https://blog.kakaocdn.net/dn/cpEj8n/btsCMxGOX6h/KWOaDTknyl4heQJpkA5kE1/img.png)
Left Controller를 선택하고
![](https://blog.kakaocdn.net/dn/bH469L/btsCKRshDDb/eaEIzFc76miHJtr8lAzmC1/img.png)
Model Prefab과 Model Parent를 넣어 줍니다.
![](https://blog.kakaocdn.net/dn/bWYNEf/btsCG4ZV9pZ/LIP9VmFqSjMIqNoaBvQXC0/img.png)
오른쪽도 동일하게 진행 합니다.
Left / Right Controller의 Hand Visual / Offset을 선택해 알맞게 회전 합니다.
![](https://blog.kakaocdn.net/dn/wixri/btsCKr8jtws/9tow3hx0w5ujJM2g0TKGuK/img.png)
![](https://blog.kakaocdn.net/dn/cOdDCa/btsCOEMhXQi/nxSC2JmOemKX6HkTCUdI70/img.png)
인터렉터 설정
프로젝트 창에서 Direct Interactor 프리팹을 찾아 Left Controller에 넣어줍니다.
![](https://blog.kakaocdn.net/dn/tUe6M/btsCKSEHYU0/drnqcikuWFt3Ht2soan301/img.png)
오른쪽도 동일하게 진행 합니다.
![](https://blog.kakaocdn.net/dn/b6vDtz/btsCOzKT3bE/yR4DDmt5GA3AKteoihKE81/img.png)
Direct Interactor를 선택해 배터리 절약을 위해 햅틱 이벤트를 꺼줍니다.
![](https://blog.kakaocdn.net/dn/cjDeH4/btsCRe0xut3/BYbxaUjcfMRnawA0NRvBMk/img.png)
롱소드를 씬으로 가져오고
![](https://blog.kakaocdn.net/dn/bK8zbM/btsCIDVmMxI/j6vqpaaTXRlMHLuKNgo0T0/img.png)
다음과 같은 구조로 만들어 줍니다.
![](https://blog.kakaocdn.net/dn/oH9K4/btsCG3mrwbu/bxnA5aJNFvYZbEpH4lg1K0/img.png)
Interactable LongSword 오브젝트를 선택하고 Rigidbody와 XR Grab Interactable 컴포넌트를 부착 합니다.
![](https://blog.kakaocdn.net/dn/bYfTsl/btsCIHQ2sAP/LnFfkp7vEAKhEJUD2mARR1/img.png)
LongSwordMesh에 이미 콜라이더가 있습니다.
![](https://blog.kakaocdn.net/dn/PeXM3/btsCMoQH9EK/AR64dgBz7L3nM2fZTzxw91/img.png)
그랩이 되는지 확인 합니다.
Interactable LongSword 오브젝트를 선택하고
![](https://blog.kakaocdn.net/dn/nCWR1/btsCG2nvqLZ/yKWxMQTPk79j0zdkSk41Xk/img.png)
Use Dynamic Attach를 체크 합니다.
![](https://blog.kakaocdn.net/dn/rgJzE/btsCJicBbTk/3w5x2WhYHJ2WY5e0VWF4WK/img.png)
잡은 위치와 회전이 잘 맞는지 확인 합니다.
- Use Dynamic Attach:
- 이 속성은 XR Grab Interactable 컴포넌트의 중요한 속성 중 하나입니다. 이것은 해당 상호작용 대상이 그랩될 때 동적으로 그랩 포인트(Grab Point)를 생성하고 이동시키는 데 사용됩니다.
- Use Dynamic Attach를 활성화하면 XR Grab Interactable가 그랩될 때 그랩 포인트가 자동으로 생성되고 설정된 조건에 따라 업데이트됩니다. 이로써 사용자의 손 또는 컨트롤러와 상호작용하는 물체에 더 자연스러운 물리적 연결을 만들 수 있습니다.
- 예를 들어, Use Dynamic Attach를 사용하면 물체가 그랩될 때 손 또는 컨트롤러와 물체 사이의 상대 위치와 회전이 고정되지 않고 동적으로 조정됩니다.
- Match Position (일치하는 위치):
- "Match Position" 프로퍼티는 XR Grab Interactable 컴포넌트의 일부로 설정할 수 있는 옵션 중 하나입니다.
- 이 프로퍼티를 활성화하면 물체가 그랩될 때 그랩 포인트(Grab Point)가 사용자의 손 또는 컨트롤러 위치와 일치하도록 설정됩니다. 즉, 그랩 포인트가 그랩될 때 손의 위치에 따라 동적으로 조정됩니다.
- 일반적으로 "Match Position"을 사용하면 물체가 그랩될 때 물체의 위치가 사용자의 손 또는 컨트롤러의 위치와 일치하여 더 자연스러운 상호작용을 가능하게 합니다.
- Match Rotation (일치하는 회전):
- "Match Rotation" 프로퍼티는 XR Grab Interactable 컴포넌트의 또 다른 중요한 설정입니다.
- 이 프로퍼티를 활성화하면 물체가 그랩될 때 그랩 포인트의 회전이 사용자의 손 또는 컨트롤러의 회전과 일치하도록 설정됩니다. 즉, 그랩 포인트의 회전이 동적으로 조정됩니다.
- "Match Rotation"을 사용하면 물체가 그랩될 때 회전이 맞추어져 물체가 사용자의 손 또는 컨트롤러와 일치하여 물체를 더 쉽게 조작할 수 있습니다.
- Snap To Collider Volume:
- 이 옵션은 XR Grab Interactable가 그랩될 때 물체를 자동으로 해당 콜라이더 볼륨에 맞게 조정하는 데 사용됩니다.
- Snap To Collider Volume을 활성화하면 XR Grab Interactable가 그랩될 때 물체가 그랩 포인트에 따라 자동으로 해당 콜라이더의 경계에 맞추어집니다. 이것은 물체가 손이나 컨트롤러와 자연스럽게 들어맞는 것을 도와줍니다.
- 이 옵션을 사용하면 사용자의 입체감과 상호작용 경험이 향상됩니다.
- Reinitialize Every Single Grab:
- 이 옵션은 XR Grab Interactable가 여러 번 그랩될 때마다 초기화를 다시 수행할지 여부를 결정합니다.
- Reinitialize Every Single Grab를 활성화하면 물체가 그랩될 때마다 XR Grab Interactable가 재설정되며 모든 상태와 속성이 초기 상태로 돌아갑니다. 이것은 그랩 해제 및 다시 그랩할 때마다 일종의 리셋을 수행하는 데 사용됩니다.
- 이 옵션을 비활성화하면 물체가 그랩될 때마다 초기화가 수행되지 않으며, 그랩 상태가 유지됩니다.
Interactable LongSword를 선택하고 빈오브젝트 (Hand Pose)를 생성 합니다.
![](https://blog.kakaocdn.net/dn/MSYnp/btsCIIbo2BD/QcFGUnJkkZK4wUSw18iok1/img.png)
Hand Pose 자식으로 Left Controller의 Model Prefab과 동일한 프리팹을 넣어줍니다.
이것은 핸드 포즈를 잡을 손 입니다.
![](https://blog.kakaocdn.net/dn/bc14Tf/btsCSbJnFvK/FMb2mv8jt7kmFwSko3a3OK/img.png)
Hand Data 스크립트를 만들고 다음과 같이 작성 합니다.
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
using UnityEngine.XR.Hands;
public class HandData : MonoBehaviour
{
public enum HandType
{
Left, Right
}
public HandType handType;
public Transform root;
public Animator anim;
public Transform[] bones;
}
Hand Data를 Hand Pose / OculusHand_L 에 부착 하고 각 프로퍼티에 아래와 같이 넣어 줍니다.
![](https://blog.kakaocdn.net/dn/TZ6KA/btsCKrNY1iy/wvIwtbukzWpg3QRwuauek0/img.png)
OculusHand_L 오브젝트를 선택하고
![](https://blog.kakaocdn.net/dn/IlY2o/btsCTwmcUsY/qu1BY0awx6ST0faIoNn9PK/img.png)
Animator와 Hand 스크립트를 제거 합니다.
![](https://blog.kakaocdn.net/dn/bOyT5w/btsCP5v75r2/MckcGjlKd5oXTGZ9YD32FK/img.png)
HandPose 스크립트를 생성하고 다음과 같이 작성 합니다.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Serialization;
using UnityEngine.XR.Interaction.Toolkit;
public class HandPose : MonoBehaviour
{
public HandData.HandType handType;
public HandData handDataRef;
public XRGrabInteractable grabInteractable;
private Vector3 startingHandPosition;
private Vector3 finalHandPosition;
private Quaternion startingHandRotation;
private Quaternion finalHandRotation;
private Quaternion[] startingFingerRotaions;
private Quaternion[] finalFingerRotations;
void Start()
{
grabInteractable.selectEntered.AddListener(this.SetupPose);
grabInteractable.selectExited.AddListener(this.UnSetPose);
}
private void UnSetPose(SelectExitEventArgs arg0)
{
if (arg0.interactorObject is XRDirectInteractor)
{
if (this.handDataRef.handType == this.handType && arg0.interactorObject.transform.name.Contains(this.handType.ToString()))
{
Debug.LogFormat("UnSetPose: <color=yellow>{0}</color>", arg0.interactorObject.transform.name);
HandData handData = arg0.interactorObject.transform.GetComponentInChildren<HandData>();
handData.anim.enabled = true;
//handData.gameObject.SetActive(false);
SetHandDataValues(handData, handDataRef);
SetHandData(handData, startingHandPosition, startingHandRotation, startingFingerRotaions);
handData.gameObject.transform.localPosition = Vector3.zero;
handData.gameObject.transform.localRotation = Quaternion.identity;
}
}
}
private void SetupPose(SelectEnterEventArgs arg0)
{
if (arg0.interactorObject is XRDirectInteractor)
{
if (this.handDataRef.handType == this.handType && arg0.interactorObject.transform.name.Contains(this.handType.ToString()))
{
Debug.LogFormat("SetupPose: <color=yellow>{0}</color>", arg0.interactorObject.transform.name);
HandData handData = arg0.interactorObject.transform.GetComponentInChildren<HandData>();
handData.anim.enabled = false;
//handData.gameObject.SetActive(false);
SetHandDataValues(handData, handDataRef);
SetHandData(handData, finalHandPosition, finalHandRotation, finalFingerRotations);
handData.gameObject.transform.localPosition = Vector3.zero;
handData.gameObject.transform.localRotation = Quaternion.identity;
}
}
}
public void SetHandDataValues(HandData h1, HandData h2)
{
// startingHandPosition = h1.root.localPosition;
// finalHandPosition = h2.root.localPosition;
startingHandPosition = new Vector3(h1.root.localPosition.x / h1.root.localScale.x,
h1.root.localPosition.y / h1.root.localScale.y, h1.root.localPosition.z / h1.root.localScale.z);
finalHandPosition = new Vector3(h2.root.localPosition.x / h2.root.localScale.x,
h2.root.localPosition.y / h2.root.localScale.y, h2.root.localPosition.z / h2.root.localScale.z);
startingHandRotation = h1.root.localRotation;
finalHandRotation = h2.root.rotation;
startingFingerRotaions = new Quaternion[h1.bones.Length];
finalFingerRotations = new Quaternion[h2.bones.Length];
for (int i = 0; i < h1.bones.Length; i++)
{
startingFingerRotaions[i] = h1.bones[i].localRotation;
finalFingerRotations[i] = h2.bones[i].localRotation;
}
}
public void SetHandData(HandData handData, Vector3 newPosition, Quaternion newRotation,
Quaternion[] newBonesRotation)
{
handData.root.localPosition = newPosition;
handData.root.localRotation = newRotation;
for (int i = 0; i < newBonesRotation.Length; i++)
{
handData.bones[i].localRotation = newBonesRotation[i];
}
}
}
Hand Pose를 선택하고
![](https://blog.kakaocdn.net/dn/DPeEU/btsCTuu9xo4/medRpt2WwqvRgyOKjvMPD1/img.png)
손의 모양을 잡아주고
![](https://blog.kakaocdn.net/dn/biMBw2/btsCMyeH2Rx/ZQWH5oFRKMhChZPdNOITck/img.png)
![](https://blog.kakaocdn.net/dn/bBkuXl/btsCQ4jregm/Yu2cMOcqkK78b1Iik2yXq0/img.png)
![](https://blog.kakaocdn.net/dn/bBPPce/btsCJjQa7Lx/SKNAgtuXUixrO2z4DKwFj1/img.png)
![](https://blog.kakaocdn.net/dn/QBsMF/btsCIHKjjcy/0QkRaRf9eoDOANCCZDkC3k/img.png)
HandPose 스크립트를 부착 합니다.
![](https://blog.kakaocdn.net/dn/bbHEbM/btsCTyqM5EM/us7vCk0MZdckkm5qAL4lO0/img.png)
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.XR.Interaction.Toolkit;
public class XRHandedGrabInteractable : XRGrabInteractable
{
[SerializeField]
private Transform LeftHandAttachTransform;
[SerializeField]
private Transform RightHandAttachTransform;
[SerializeField]
XRDirectInteractor LeftController;
[SerializeField]
XRDirectInteractor RightController;
protected override void OnSelectEntering(SelectEnterEventArgs args)
{
if (args.interactorObject == LeftController)
{
Debug.Log($"Left hand");
attachTransform.SetPositionAndRotation(LeftHandAttachTransform.position, LeftHandAttachTransform.rotation);
}
else if (args.interactorObject == RightController)
{
Debug.Log($"Right hand");
attachTransform.SetPositionAndRotation(RightHandAttachTransform.position, RightHandAttachTransform.rotation);
}
base.OnSelectEntering(args);
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Serialization;
using UnityEngine.XR.Interaction.Toolkit;
public class HandPose : MonoBehaviour
{
public HandData.HandType handType;
public HandData handDataRef;
public XRGrabInteractable grabInteractable;
public Transform wrist;
public Transform attachPoint;
[SerializeField] private HandData leftHandData;
[SerializeField] private HandData rightHandData;
[SerializeField]
XRDirectInteractor LeftController;
[SerializeField]
XRDirectInteractor RightController;
private Vector3 startingHandPosition;
private Vector3 finalHandPosition;
private Quaternion startingHandRotation;
private Quaternion finalHandRotation;
private Quaternion[] startingFingerRotaions;
private Quaternion[] finalFingerRotations;
void Start()
{
// this.attachPoint.localPosition = this.transform.InverseTransformPoint(this.wrist.position);
// this.attachPoint.localRotation = this.wrist.localRotation;
grabInteractable.selectEntered.AddListener(this.SetupPose);
grabInteractable.selectExited.AddListener(this.UnSetPose);
}
private void UnSetPose(SelectExitEventArgs arg0)
{
if (arg0.interactorObject == LeftController && this.handType == HandData.HandType.Left)
{
Debug.Log($"<color=yellow>Left hand</color>");
leftHandData.anim.enabled = true;
SetHandDataValues(leftHandData, handDataRef);
SetHandData(leftHandData, startingHandPosition, startingHandRotation, startingFingerRotaions);
leftHandData.gameObject.transform.localPosition = Vector3.zero;
leftHandData.gameObject.transform.localRotation = Quaternion.identity;
}
else if (arg0.interactorObject == RightController && this.handType == HandData.HandType.Right)
{
Debug.Log($"<color=yellow>Right hand</color>");
rightHandData.anim.enabled = true;
SetHandDataValues(rightHandData, handDataRef);
SetHandData(rightHandData, startingHandPosition, startingHandRotation, startingFingerRotaions);
rightHandData.gameObject.transform.localPosition = Vector3.zero;
rightHandData.gameObject.transform.localRotation = Quaternion.identity;
}
}
private void SetupPose(SelectEnterEventArgs arg0)
{
System.Action<HandData> setupPose = (handData) =>
{
handData.anim.enabled = false;
//handData.gameObject.SetActive(false);
SetHandDataValues(handData, handDataRef);
SetHandData(handData, finalHandPosition, finalHandRotation, finalFingerRotations);
handData.gameObject.transform.localPosition = Vector3.zero;
handData.gameObject.transform.localRotation = Quaternion.identity;
};
if (arg0.interactorObject == LeftController && this.handType == HandData.HandType.Left)
{
Debug.Log($"<color=yellow>Left hand</color>");
setupPose(leftHandData);
}
else if (arg0.interactorObject == RightController && this.handType == HandData.HandType.Right)
{
Debug.Log($"<color=yellow>Right hand</color>");
setupPose(rightHandData);
}
}
public void SetHandDataValues(HandData h1, HandData h2)
{
// startingHandPosition = h1.root.localPosition;
// finalHandPosition = h2.root.localPosition;
startingHandPosition = new Vector3(h1.root.localPosition.x / h1.root.localScale.x,
h1.root.localPosition.y / h1.root.localScale.y, h1.root.localPosition.z / h1.root.localScale.z);
finalHandPosition = new Vector3(h2.root.localPosition.x / h2.root.localScale.x,
h2.root.localPosition.y / h2.root.localScale.y, h2.root.localPosition.z / h2.root.localScale.z);
startingHandRotation = h1.root.localRotation;
finalHandRotation = h2.root.rotation;
startingFingerRotaions = new Quaternion[h1.bones.Length];
finalFingerRotations = new Quaternion[h2.bones.Length];
for (int i = 0; i < h1.bones.Length; i++)
{
startingFingerRotaions[i] = h1.bones[i].localRotation;
finalFingerRotations[i] = h2.bones[i].localRotation;
}
}
public void SetHandData(HandData handData, Vector3 newPosition, Quaternion newRotation,
Quaternion[] newBonesRotation)
{
handData.root.localPosition = newPosition;
handData.root.localRotation = newRotation;
for (int i = 0; i < newBonesRotation.Length; i++)
{
handData.bones[i].localRotation = newBonesRotation[i];
}
}
}
'VR > XR Interaction Toolkit' 카테고리의 다른 글
XR Interaction Toolkit 2.4.3 (Hand Pose) (0) | 2023.12.30 |
---|---|
XR Interaction Toolkit 2.4.3 (Grab Interactable) (0) | 2023.12.30 |
[OpenXR] Ray Interactor (XR Interaction Toolkit Movement 타입 차이점) (0) | 2023.12.28 |
[OpenXR] XR Interaction Toolkit 2.4.3 (Two Hand Grab) (0) | 2023.12.28 |
[XR Interaction Toolkit 2.4.3] Custom Hand Grab Pose (0) | 2023.12.27 |