'전체 글'에 해당되는 글 1801건

  1. 2023.12.28 [OpenXR] XR Interaction Toolkit 2.4.3 (Two Hand Grab)
  2. 2023.12.27 [XR Interaction Toolkit 2.4.3] Custom Hand Grab Pose
  3. 2023.12.27 XR Interaction Toolkit Animated Hands (Grip, Pinch)
  4. 2023.12.27 Access hand data from Unity components in the scene
  5. 2023.12.27 XR Interaction Toolkit 2.4.3 OpenXR Throw Object
  6. 2023.12.26 XR Interaction Toolkit 2.4.3 (OpenXR) Animated Hand Model

[OpenXR] XR Interaction Toolkit 2.4.3 (Two Hand Grab)

VR/XR Interaction Toolkit 2023. 12. 28. 11:25
반응형

 

https://youtube.com/playlist?list=PLTFRwWXfOIYBIPKhWi-ZO_ITXqtNuqj6j&si=9t2YSxJcsw7fRNMR

 

XR Interaction Toolkit 2.4.3

XR Interaction Toolkit 2.4.3 OpenXR Oculus

www.youtube.com


https://youtu.be/LZFjfT_2TaQ?list=PLTFRwWXfOIYBIPKhWi-ZO_ITXqtNuqj6j

리소스 

https://assetstore.unity.com/packages/3d/props/guns/stylized-m4-assault-rifle-with-scope-complete-kit-with-gunshot-v-178197

 
 
컨트롤러를 사용한 한손잡기를 만들어 준다 
 
새 씬을 만들고 
메인 카메라를 제거 후 XR Origin을 생성 한다 

 
XR Origin오브젝트를 선택후 Tracking Origin Mode를 Floor로 변경 

 
Left/Right Controller를 선택하고 

 
XR Controller를 제외한 나머지 컴포넌트는 제거 한다 

 
다음과 같은 구조를 만들어주고 

 
Left Controller를 선택후 Model Prefab과 Model Parent를 각각 넣어준다 

 
왼손 Offset의 위치와 회전을 설정한다 

왼손 설정된 모습

 
오른손도 동일하게 작업 한다 
 
Right Controller를 선택하고 

 
Model Prefab/Parent를 넣어주고 

 
 
Offset의 위치와 회전을 맞춰준다 

 
 

완성된 왼손 컨트롤러 핸드

 
 

 
 


 
리소스를 다운 받고 

 
프리팹을 선택한다 

 
다음과 같이 핑크색으로 보이면 

 
 
 
핑크색 메터리얼들을 선택 하고 

 

 
Edit > Rendering > Materials > Convert Selected Bulit-In Materials to URP를 선택한다 

 

 
 
이제 프리팹을 씬으로 가져온후 위치를 맞추고 실행후 정면에 잘 보이는지 확인 한다 

 
 
실행후 확인 
 

 
 
 
이제 씬에서 총을 선택하고 언팩 하고 다음과 같은 구조로 만들어 준다 

 
기존에 붙어 있던 스크립트는 제거 한다 

 
 
Rifle오브젝트를 선택하고 위치와 크기를 조절 한다 

 
 
손의 크기에 맞게 크기를 조절하고 위치도 잡을수 있게 수정 한다 

 
다시 실행후 크기와 위치가 맞는지 확인한다 
 

 
 


 
Left Controller를 선택하고 
 

 
XR Direct Interactor컴포넌트를 부착하고 

 
후 Interaction Manager 프로퍼티에 XR Interaction Manager오브젝트를 넣어준다 

 
이어서 Sphere Collider를 추가 하고 Radius를 0.1로 설정한다 

 
 
이제 Rifle오브젝트를 선택하고 Rigidbody, XR Grab Interactable 컴포넌트를 부착한뒤 
다음과 같이 설정 한다 

 
Rifle을 선택하고 자식으로 빈오브젝트 (Collider)를 만들어 주고 
 

 
 
Box Collider컴포넌트를 부착 하고 Is Trigger를 체크 한다  

 
크기와 위치 회전을 조절해 핸들 부붙에 가져다 놓는다 

 
 
Rifle 오브젝트를 선택하고 Colliders에 넣어준다 

 
 
Assault_Rifle오브젝트를 선택하고 

Mesh Collider를 제거 한다 

 
 
 
위치와 크기가 맞지 않는다면Assault_Rifle_M4를 선택해 

위치와 회전을 다음과 같이 설정하고 

 
Rifle을 선택해 정면을 바라보게 만들어 준다 

 
실행후 결과를 확인 한다 

 
 
손의 위치가 맞지 않으면 

 
 
Offset의 위치를 설정 한다 

 
 
 

왼손 오프셋

 

오른손 오프셋

 

라이플 위치와 회전 크기

 
실행후 결과를 확인 하자 

 


 
Rifle을 선택한뒤 XR Grab Interactable 컴포넌트의 Select Mode를 Multiple로 변경 한다 

 
다음과 같이 간단히 두손 잡기를 구현 할수 있다 
 

 
 
콜라이더를 하나 더 만들어 핸드가드 (총열덮개) 부분에 위치 시킨다 

 
Rifle 오브젝트를 선택하고 XR Grab Interactable 컴포넌트의 Colliders에 추가 한다 

 

 


라이플을 선택후 

 
 
XR Grab Interactable컴포넌트의 Use Dynamic Attack 프로퍼티를 체크 해본다 

 
다음과 같이 잡은 위치와 회전을 기반으로 잡기가 동작 한다 
 

https://docs.unity3d.com/Packages/com.unity.xr.interaction.toolkit@2.4/api/UnityEngine.XR.Interaction.Toolkit.XRGrabInteractable.html

 
 
완성된 결과물 


 
도전 
이전시간에 했던 Hand Grab Pose 를 이용해 손동작을 적용해보자

 

 

 

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");
        
        // Animator 컴포넌트에 해당 트리거 파라미터가 존재하는지 확인
        if (HasParameterOfType(this._handAnimator, "Trigger", AnimatorControllerParameterType.Trigger))
        {
            // 트리거 파라미터가 존재하면 실행
            SetAnimatorParameterValue(CommonUsages.trigger, "Trigger");
        }
        else
        {
            // 트리거 파라미터가 존재하지 않으면 실행하지 않음
            Debug.LogWarning("Trigger parameter does not exist");
        }
        
    }
    
    public bool HasParameterOfType(Animator animator, string paramName, AnimatorControllerParameterType paramType)
    {
        AnimatorControllerParameter[] parameters = animator.parameters;
        foreach (AnimatorControllerParameter parameter in parameters)
        {
            if (parameter.name == paramName && parameter.type == paramType)
            {
                return true;
            }
        }
        return false;
    }

    private void SetAnimatorParameterValue(InputFeatureUsage<float> feature, string parameterName)
    {
        if (_targetDevice.TryGetFeatureValue(feature, out float value))
        {
            _handAnimator.SetFloat(parameterName, value);
        }
        else
        {
            _handAnimator.SetFloat(parameterName, 0);
        }
    }
}

 
 
Use Dynamic Attach 를 사용하지 않았을 경우 
 

 
 
Use Dynamic Attach를 사용했을 경우 
 

 

반응형
:

[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 Interaction Toolkit 2.4.3

XR Interaction Toolkit 2.4.3 OpenXR Oculus

www.youtube.com



새 씬을 만들고 메인 카메라를 지우고 XR Origin을 생성 합니다.

 
Left / Right Controller 오브젝트를 선택 하고 XR Controller를 제외한 나머지를 제거 합니다.

 
다음 구조로 만들어 주고 

Left Controller Model을 넣어 줍니다.

오른손도 똑같이 해줍니다.
 
 
 
Offset설정을 해줍니다 
 

왼손 Offset
오른손 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();
        }
    }
}
반응형
:

XR Interaction Toolkit Animated Hands (Grip, Pinch)

VR/XR Interaction Toolkit 2023. 12. 27. 12:12
반응형

https://youtube.com/playlist?list=PLTFRwWXfOIYBIPKhWi-ZO_ITXqtNuqj6j&si=XhyHS26tNDP6u7CA

XR Interaction Toolkit 2.4.3

XR Interaction Toolkit 2.4.3 OpenXR Oculus

www.youtube.com


새 씬을 만들어 주고 

메인 카메라를 제거 하고 XR Origin을 생성 한다 

 
 
Left / Right Controller 오브젝트를 선택하고 XR Controller 컴포넌트를 제외하고 모두 제거 한다 

 
 
Left Controller를 선택하고 다음과 같은 구조로 빈오브젝트를 생성한다 

 
Left Controller를 선택하고 Model Prefab과 Model Parent를 넣어준다 

 
오른쪽도 동일하게 진행한다 
 
Offset을 설정해 손의 위치와 회전을 설정 한다 
 

왼손 Offset
오른손 Offset
실행 결과

 


 

애니메이션 잡기 

 
왼손 프리팹모드로 들어와 

 
 
Animation을 선택한다 

Create버튼을 누르고 

 
애니메이션을 저장 한다 
Hand_L_Grip

레코드 버튼을 누르고 손을 접어 준다

 
 
이제 레코드를 해제 한다 
 

Pinch 애니메이션 만들기 

새로운 클립을 생성하고 
 

저장한다 

 
다시 레코드 버튼을 누르고 손을 접어준다 

 
이제 레코드를 해제 한다 
 
 

완전히 편 손 애니메이션 만들기 

새로운 클립을 만들고 

저장한다 

 
기본이 펴진 상태이기 때문에 키는 안잡아도 된다 

 
 
애니메이터 컨트롤러를 더블클릭하고 

 
안에 있는 State를 제거 한다 

 
블랜드 트리를 생성하고 

 
Float 형식으로 Grip, Trigger 파라미터를 생성하고 기존에 있던 것은 제거 한다 
팝업이 뜨면 그냥 지우겠다고 하면된다 

 
 
 
블랜드 트리를 선택 하고 

 
블랜드 트리를 선택하고 인스펙터에서 Blend Type을 2D Freeform Cartesian을 선택 한다 

 
Add Motion Field를 4개 만든다 

 
Pose X, Y 를 다음과 같이 설정 하고 

 
다음과 같이 모션을 넣어 준다 

 
손을 넣어주고 

 
 
빨간 점을 이용해 모션이 동작 하는지 확인 한다 

 
 
 
스크립트를 만들어 다음과 같이 작성하고 프리팹에 부착 한다 

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);
        }
    }
}

 
이때 Input Device Characteristics 는 Controller, Left를 선택 한다 

 
실행후 결과를 확인해보자 
 
인덱스 트리거를 눌렀을때 Pich가 
핸드 트리거를 눌렀을때 Grip 애니메이션이 실행되는지 확인한다 
 

 
 
오른손도 동일하게 작업 한다 
프리팹을 선택하고 

 

 
애니메이션 창을 열고 

 
저장하고 

 
 
레코드 버튼을 누르고 
손 모양을 잡아준다 

 
 
새 클립을 만들고 

다시 저장 하고 

 
레코드 버튼을 누르고 손 모양( Pinch)를 잡아 준다 

 
다시 새 클립을 만들고 

Hand_R_Open으로 저장한다 

 
기본형이 펴진 상태기 때문에 키는 안잡아도 된다 

 
 
핸드 프리팹을 선택하고 

 
 
컨트롤러를 더블클릭하고 

 
안에 있는 State들을 제거 한다 

 
Float 형식으로 2개의 파라미터 (Grip, Trigger)를 만들어 주고

 
 
블랜드 트리를 생성하고 더블 클릭해서 들어간다 

 
 
인스펙터에서 2D Freefrom Cartesian을 선택하고 

다음과 같이 파라미터를 설정 한다 

 
+ 버튼을 눌러 Add motion field 를 4개 생성하고 
다음과 같이 설정 한다 

 

 
이제 블랜드 트리의 슬라이더를 이용해 애니메이션 동작을 확인 한다 
 

 
 
핸드 프리팹을 선택하고 Hand 스크립트를 부착 한다
 
이때 Input Device Characteristics는 Controller, Right를 선택하고 
Hand Animator는 위에 있는 Animator 컴포넌트를 넣어주면 된다 

 
플레이 해보고 두 손 모두 애니메이션이 잘 나오는지 확인 하자 
 

 

반응형
:

Access hand data from Unity components in the scene

VR/XR Interaction Toolkit 2023. 12. 27. 11:03
반응형

https://docs.unity3d.com/Packages/com.unity.xr.hands@1.3/manual/hand-data/xr-hand-access-data.html

Access hand data from Unity components in the scene | XR Hands | 1.3.0

Access hand data from Unity components in the scene Access hand-tracking data from Unity components in the scene. The XRHandTrackingEvents component can be added to a GameObject in the scene to subscribe to hand-tracking events for a particular hand chosen

docs.unity3d.com

반응형
:

XR Interaction Toolkit 2.4.3 OpenXR Throw Object

VR/XR Interaction Toolkit 2023. 12. 27. 10:13
반응형

https://youtube.com/playlist?list=PLTFRwWXfOIYBIPKhWi-ZO_ITXqtNuqj6j&si=9t2YSxJcsw7fRNMR

XR Interaction Toolkit 2.4.3

XR Interaction Toolkit 2.4.3 OpenXR Oculus

www.youtube.com


새 씬을 만들고 

메인 카메라를 지우고 XR Origin을 추가 한다 

 
XR Origin 오브젝트를 선택한뒤 Tracking Origin Mode를 Floor 로 변경한다 

Left/Right Controller를 선택한뒤 
 

 
XR Controller를 제외한 나머지 컴포넌트를 제거 한다 

 
실행한뒤 컨트롤러가 잘 나오는지 확인 하자 
 

 
 
빈오브젝트 (Cube)를 만들고 자식으로 빈오브젝트 (Visuals)를 만들고 그 자식으로 큐브를 만들어 준다 

Mesh는 큐브다 

 
Cube 오브젝트를 선택하고 Rigidbody와 XR Grab Interactable 컴포넌트를 부착 하고 
Use Gravity는 체크 해제 한뒤 Interaction Manager에 XR Interaction Manager게임오브젝트를 넣어준다 

 
Left Controller를 선택하고 XR Direct Interactor 컴포넌트를 붙여 준다 

 
Interaction Manager에 XR Interaction Manager오브젝트를 넣어주자 

 
이어서 Sphere Collider를 부착 하고 Is Trigger를 체크 하고 Radius를 0.1로 설정 한다 

 
Cube 오브젝트를 선택하고 위치를 적당히 보이게 설정한다 

실행후 잡히는지 테스트 해본다 
 

 
이렇게 잡고 놨을때 날라가버리면 
Cube오브젝트를 선택하고 Is Kinematic 을 체크 한다 

다시 실행해보자 
 

 
이렇게 왼손 잡기가 잘 되면 오른손도 똑같이 해주자 

 
플레이를 해보고 왼손 오른손 잡기가 모두 동작 하는지 확인 한다 
 

 


 

Throw an Object 

 
Cube 오브젝트를 선택하고 Use Gravity를 체크 하고 Is Kinematic을 체크 해제 한다 

 
Cube를 하나 만들어 Desk를 만들어 주고 

 
 

 


 

애니메이션 손을 적용하기 

 
Left Controller 자식으로 빈 오브젝트 Hand Visual을 만들고 
그 자식으로 Offset 빈 오브젝트를 생성한다 

 
 

 
오른쪽도 동일하게 진행하고 플레이후 테스트 한다 
 
손의 위치와 회전이 맞지 않는다면 런타임에서 마추고 Transform컴포넌트를 복사해 적용한다 

왼손 Offset

 
 

오른손 Offset

 
실행후 테스트 해본다 
잘 동작한다 

 
 
 


 
스카이박스 

Skybox_Gradient.unitypackage
0.00MB

 
 
 
오큘러스 핸드 

OculusHand_L_R.unitypackage
0.43MB

 
 
 
오큘러스 핸드 메터리얼 

OculusHand_Mat.unitypackage
0.53MB

 
 
 
오큘러스 Animated Hands

Oculus_Animated_Hands.unitypackage
0.99MB

 
 
 
URP 프로토타입 메터리얼 
https://assetstore.unity.com/packages/2d/textures-materials/minimalist-scalable-grid-prototype-materials-214264

Minimalist Scalable Grid Prototype Materials | 2D 텍스처 및 소재 | Unity Asset Store

Elevate your workflow with the Minimalist Scalable Grid Prototype Materials asset from MLAgent. Find this & more 텍스처 및 소재 on the Unity Asset Store.

assetstore.unity.com

 

반응형
:

XR Interaction Toolkit 2.4.3 (OpenXR) Animated Hand Model

VR/XR Interaction Toolkit 2023. 12. 26. 16:07
반응형

 https://youtube.com/playlist?list=PLTFRwWXfOIYBIPKhWi-ZO_ITXqtNuqj6j&si=9t2YSxJcsw7fRNMR


새 씬을 만들고 메인 카메라를 제거 한다 

 
오큘러스 핸드 모델을 넣어주고 

OculusHand_L_R.unitypackage
0.43MB

 
 
 

물론 XRHands에 있는걸 써도 된다 

 
 
 
XR Origin을 만들어 준다 

 
 
Origin Mode를 설정 하고 

 
 
Left Controller를 선택해 매니저를 넣어주자 

 
오른쪽도 똑같이 해준다 

 
 
Left Controller를 선택하고 자식으로 빈 오브젝트(Hand Visual)를 만든다 

 
그 자식으로 빈오브젝트(Offset)을 또 만들어 주고 

 
Left Controller 오브젝트를 선택하고 XR Controller 컴포넌트의 Model 프로퍼티의 Model Prefab에는 왼손 모델을 
Model Parent에는 Offset 오브젝트를 넣어준다  

 
오른손도 똑같이 한다 

 
 
Skybox를 간지나게 하고 싶다면 다음패키지를 설치 하고 

skybox_gradient.unitypackage
0.00MB
스카이 박스 메터리얼을 변경해준다


 
 
이제 플레이를 해보고 결과를 확인 하자 
 

 
손은 잘 나오는데 회전을 좀 해야 할거 같다 
다음과 같이 Offset오브젝트의 Rotation X , Y 를 90으로 설정 한다 

 
오른손은 다음과 같이 설정한다 

 
 
다시 실행후 결과를 확인 하자 
 

위치도 조금 내려주자 

왼손
오른손

 
다시 실행해보고 확인해보자 

 
 
이제 왼손모델을 가져와서 언팩하고 프리팹으로 만들어 준다 

오른손도 똑같이 해준다 

Left / Right Controller의 모델 프리팹을 다시 연결해주자 

 
 
 
 
애니메이션을 만들어 주고 

 
블랜드 트리를 생성하고 

 
테스트 
 

 
 
스크립트를 하나 만들고 

 

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");
    }

    private void SetAnimatorParameterValue(InputFeatureUsage<float> feature, string parameterName)
    {
        if (_targetDevice.TryGetFeatureValue(feature, out float value))
        {
            _handAnimator.SetFloat(parameterName, value);
        }
        else
        {
            _handAnimator.SetFloat(parameterName, 0);
        }
    }
}

 
오른손도 똑같이 해준다 

 
 

 
 
 
 


OculusHand_Mat.unitypackage
0.53MB

 
 
참고 

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();
        }
    }
}

https://fistfullofshrimp.com/unity-vr-inputs-and-hand-animation/
 
 

반응형
: