[XR Interaction Toolkit 2.4.3] Custom Hand Grab Pose
VR/XR Interaction Toolkit 2023. 12. 27. 15:59핸드 모델 붙이기
https://youtube.com/playlist?list=PLTFRwWXfOIYBIPKhWi-ZO_ITXqtNuqj6j&si=XhyHS26tNDP6u7CA
새 씬을 만들고 메인 카메라를 지우고 XR Origin을 생성 합니다.
Left / Right Controller 오브젝트를 선택 하고 XR Controller를 제외한 나머지를 제거 합니다.
다음 구조로 만들어 주고
Left Controller Model을 넣어 줍니다.
오른손도 똑같이 해줍니다.
Offset설정을 해줍니다
오브젝트 잡기 만들기
총 프리팹을 가져와서 언팩 하고
Model을 선택해서 애니메이터를 제거 하고
다음 구조로 만들어 주고
Gun을 선택해서 Rigidbody와 XR Grab Interactable컴포넌트를 부착 하고 다음과 같이 설정 한다
Left / Right Controller 를 선택하고
XR Direct Interactor 컴포넌트를 부착 하고 Manager를 넣어준다
Gun을 선택 하고 Collider를 만들어주고 크기를 조절 한다
Is Trigger를 체크 한다
Left / Right Controller를 선택하고 Sphere Collider를 추가 한다 Radius의 값은 0.1로 설정 한다
Gun을 선택하고 XR Grab Interactable의 Colliders에 Collider를 넣어준다
Gun의 위치를 설정 하고
그랩이 잘 되는지 확인 한다
손의 Offset위치에 따라 잡는 위치가 달라지니 적당히 조절 한다
커스텀 핸드 포즈 만들기
Gun을 선택 하고
빈오브젝트를 만들고 (Hand Pose)
그 자식으로 핸드 프리팹을 넣어준다
Animator와 Hand컴포넌트는 제거 한다
쉽지 않겠지만 손의 포즈를 잡아 준다
스크립트 작성
HandData스크립트를 생성하고 다음과 같이 작성한다
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;
}
핸드 프리팹에 부착 한다
프로퍼티에 오브젝트를 넣어주고
자물쇠로 잠그고
다음 오브젝트들을 선택하고
Bones에 넣어준다
HandPose 스크립트를 생성하고 다음과 같이 작성한다
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.XR.Interaction.Toolkit;
public class HandPose : MonoBehaviour
{
public HandData leftHandData;
void Start()
{
XRGrabInteractable grabInteractable = GetComponent<XRGrabInteractable>();
grabInteractable.selectEntered.AddListener(this.SetupPose);
//leftHandData.gameObject.SetActive(false);
}
private void SetupPose(SelectEnterEventArgs arg0)
{
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);
}
}
Gun을 선택하고 HandPose 를 부착 한다
Hand Data에는 다음 오브젝트를 넣어준다
실행후 왼손으로 테스트 해본다
핸드의 값 적용하기
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.XR.Interaction.Toolkit;
public class HandPose : MonoBehaviour
{
public HandData leftHandData;
private Vector3 startingHandPosition;
private Vector3 finalHandPosition;
private Quaternion startingHandRotation;
private Quaternion finalHandRotation;
private Quaternion[] startingFingerRotaions;
private Quaternion[] finalFingerRotations;
void Start()
{
XRGrabInteractable grabInteractable = GetComponent<XRGrabInteractable>();
grabInteractable.selectEntered.AddListener(this.SetupPose);
//leftHandData.gameObject.SetActive(false);
}
private void SetupPose(SelectEnterEventArgs arg0)
{
Debug.LogFormat("SetupPose");
if (arg0.interactorObject is XRDirectInteractor)
{
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, leftHandData);
SetHandData(handData, finalHandPosition, finalHandRotation, finalFingerRotations);
}
}
public void SetHandDataValues(HandData h1, HandData h2)
{
startingHandPosition = h1.root.localPosition;
finalHandPosition = h2.root.localPosition;
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];
}
}
}
다음 코드로 수정 한다
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.XR.Interaction.Toolkit;
public class HandPose : MonoBehaviour
{
public HandData leftHandData;
private Vector3 initialHandPosition = Vector3.zero;
private Vector3 targetHandPosition = Vector3.zero;
private Quaternion initialHandRotation = Quaternion.identity;
private Quaternion targetHandRotation = Quaternion.identity;
private Quaternion[] initialFingerRotations;
private Quaternion[] targetFingerRotations;
private void Start()
{
XRGrabInteractable grabInteractable = GetComponent<XRGrabInteractable>();
grabInteractable.selectEntered.AddListener(SetupPose);
grabInteractable.selectExited.AddListener(UnSetPose);
// leftHandData.gameObject.SetActive(false);
}
private void UnSetPose(SelectExitEventArgs arg0)
{
if (arg0.interactorObject is XRDirectInteractor)
{
Debug.LogFormat("<color=lime>[UnSetPose]</color> : {0}", arg0.interactorObject.transform.name);
HandData handData = arg0.interactorObject.transform.GetComponentInChildren<HandData>();
EnableHandDataAnimation(handData);
SetHandData(handData, initialHandPosition, initialHandRotation, initialFingerRotations);
}
}
private void SetupPose(SelectEnterEventArgs arg0)
{
if (arg0.interactorObject is XRDirectInteractor)
{
Debug.LogFormat("<color=yellow>[SetupPose]</color> : {0}", arg0.interactorObject.transform.name);
HandData handData = arg0.interactorObject.transform.GetComponentInChildren<HandData>();
DisableHandDataAnimation(handData);
SetHandDataValues(handData, leftHandData);
SetHandData(handData, targetHandPosition, targetHandRotation, targetFingerRotations);
}
}
private Vector3 CorrectScale(Vector3 position, Vector3 scale)
{
return new Vector3(position.x / scale.x, position.y / scale.y, position.z / scale.z);
}
private void EnableHandDataAnimation(HandData handData)
{
handData.anim.enabled = true;
}
private void DisableHandDataAnimation(HandData handData)
{
handData.anim.enabled = false;
}
private void SetHandDataValues(HandData h1, HandData h2)
{
initialHandPosition = CorrectScale(h1.root.localPosition, h1.root.localScale);
targetHandPosition = CorrectScale(h2.root.localPosition, h2.root.localScale);
initialHandRotation = h1.root.localRotation;
targetHandRotation = h2.root.rotation;
initialFingerRotations = new Quaternion[h1.bones.Length];
targetFingerRotations = new Quaternion[h2.bones.Length];
for (int i = 0; i < h1.bones.Length; i++)
{
initialFingerRotations[i] = h1.bones[i].localRotation;
targetFingerRotations[i] = h2.bones[i].localRotation;
}
}
private 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];
}
}
}
손목 위치가 안맞는다
Offset을 변경해서 마춰준다
오른손도 해줍니다
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Serialization;
using UnityEngine.XR.Interaction.Toolkit;
public class HandPose : MonoBehaviour
{
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)
{
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, leftHandData);
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)
{
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];
}
}
}
Offset의 위치를 잘 맞춰주면 댄다
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.XR;
public class Hand : MonoBehaviour
{
public InputDeviceCharacteristics inputDeviceCharacteristics;
private InputDevice _targetDevice;
[SerializeField] private Animator _handAnimator;
private void Start()
{
InitializeHand();
}
private void InitializeHand()
{
List<InputDevice> devices = new List<InputDevice>();
InputDevices.GetDevicesWithCharacteristics(inputDeviceCharacteristics, devices);
if (devices.Count > 0)
{
_targetDevice = devices[0];
}
}
private void Update()
{
if (!_targetDevice.isValid)
{
InitializeHand();
}
else
{
UpdateHand();
}
}
private void UpdateHand()
{
SetAnimatorParameterValue(CommonUsages.grip, "Grip");
SetAnimatorParameterValue(CommonUsages.trigger, "Trigger");
}
private void SetAnimatorParameterValue(InputFeatureUsage<float> feature, string parameterName)
{
if (_targetDevice.TryGetFeatureValue(feature, out float value))
{
_handAnimator.SetFloat(parameterName, value);
}
else
{
_handAnimator.SetFloat(parameterName, 0);
}
}
}
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;
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Serialization;
using UnityEngine.XR.Interaction.Toolkit;
public class HandPose : MonoBehaviour
{
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)
{
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, leftHandData);
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)
{
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];
}
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//This will allow us to get InputDevice
using UnityEngine.XR;
public class InputReader : MonoBehaviour
{
//Creating a List of Input Devices to store our Input Devices in
List<InputDevice> inputDevices = new List<InputDevice>();
// Start is called before the first frame update
void Start()
{
//We will try to Initialize the InputReader here, but all components may not be loaded
InitializeInputReader();
}
//This will try to initialize the InputReader by getting all the devices and printing them to the debugger.
void InitializeInputReader()
{
InputDevices.GetDevices(inputDevices);
foreach (var inputDevice in inputDevices)
{
Debug.Log(inputDevice.name + " " + inputDevice.characteristics);
}
}
// Update is called once per frame
void Update()
{
//We should have a total of 3 Input Devices. If it’s less, then we try to initialize them again.
if (inputDevices.Count < 2)
{
InitializeInputReader();
}
}
}
'VR > XR Interaction Toolkit' 카테고리의 다른 글
[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 Animated Hands (Grip, Pinch) (0) | 2023.12.27 |
Access hand data from Unity components in the scene (0) | 2023.12.27 |
XR Interaction Toolkit 2.4.3 OpenXR Throw Object (0) | 2023.12.27 |