XR Poke Interactor (1월 2주차 방송 예정)

VR/XR Interaction Toolkit 2024. 1. 8. 10:08
반응형

 

찌르기 상호 작용은 다음과 같은 다양한 곳에서 사용 됩니다. 

 

특히 UI에서 많이 활용될수 있습니다.

 

이번 시간에는 Poke Interaction을 구현 하는 방법에 대해 알아보고 버튼을 누르는 예제를 만들어 보겠습니다. 


 

1단계 : 핸드 설정 

 

 

새 씬을 만들고 메인 카메라를 제거 하고 XR Origin을 생성후 Left, Right Controller를 선택해 XR Controller를 제외한 나머지 컴포넌트를 지워 줍니다.

 

컨트롤러로 테스트 해도 되지만 저는 손으로 해보겠습니다. 

Left / Right Controller를 선택후 다음과 같은 구조로 만들어 주고 

 

 

Offset에 알맞는 핸드 프리팹을 넣어 줍니다.

 

모델에 따라 위치와 회전의 조절이 필요 할수 있습니다.

각 손의 Offset을 선택후 정면을 바라보게 수정 합니다.

 

 

Left, Right Controller를 선택후 Hand Model을 none으로 설정 합니다.

 

이제 실행후 결과를 확인 합니다.

 

저는 다음과 같이 왼손 그립을 찌르기 애니메이션으로 변경 해보았습니다.

 

 


 

2단계 : Poke Interactor 설정 

 

Poke Interactor를 프로젝트 창에서 검색후 프리팹을 찾아 Left/Right Controller에 넣어 줍니다.

 

두 Poke Interactor를 선택해 Unpack 해주고 

 

 

실린더는 제거 합니다.

 

Poke Point를 선택하고 

 

빨강 메터리얼로 변경합니다. 

 

이렇게 하면 시각 적으로 포크 하는 위치를 확인 할수 있어 편리 합니다.

 

 

왼쪽 Poke Point를 l_index_finger_tip_makter (모델이 다르다면 손 끝)에 옮겨두고 

위치를 초기화 합니다.

 

 

 

오른손의 Poke Point는 손바닥쯤 위치 시킵니다.

실행후 Poke Point의 위치를 살펴 봅니다.

 

 

 


 

3단계 : 인터렉터블 오브젝트 만들기 

 

다음과 같이 빈 오브젝트 Cube / Visuals 자식으로 Cube 를 생성해 줍니다.

Mesh를 선택해 크기를 조절하고 

 

위치를 적당히 맞춰줍니다.

 

 

Cube오브젝트를 선택하고 XR Simple Interactable 컴포넌트를 부착 합니다.

 

이는 Interactable 객체의 가장 간단한 버전입니다. 이는 단순히 XRBaseInteractable의 구체적인 구현을 제공합니다. 기본 상호 작용 동작 없이 상호 작용 이벤트에 응답하는 방법으로 사용하기 위한 것입니다.

 

 

 

Interaction Affordance를 검색해 Cube자식으로 넣어줍니다.

 

Color Affordance를 선택하고 Material Property Block Helpter의 Renderer 속성에 Cube/Visuals/Mesh를 넣어 줍니다.

XR Interaction Toolkit 패키지는 사용자가 상호 작용 상태에 대한 시각적 및 청각적 피드백을 생성할 수 있는 어포던스 시스템을 제공합니다.

 

 

 

 

Left Controller 아래 있는 Poke Interactor를 선택하고 

Require Poke Filter를 체크 해제 합니다.

 

 

이제 실행하고 결과를 확인 합니다 

큐브는 왼손 포크 상호 작용에 대해 반응 하고 오른손은 반응 하지 않습니다.

 

 


 

4단계 : 찌르기 상호 작용 이벤트 

 


ControllerHand 스크립트를 생성후 

 

다음과 같이 작성 합니다.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.XR.Interaction.Toolkit;

public class ControllerHand : MonoBehaviour
{
    [SerializeField] private XRPokeInteractor _pokeInteractor;
    
    
    void Start()
    {
        _pokeInteractor.hoverEntered.AddListener((args) =>
        {
            Debug.LogFormat("hoverEntered: {0}", args.interactableObject);
        });
        
        _pokeInteractor.hoverExited.AddListener((args) =>
        {
            Debug.LogFormat("hoverExited: {0}", args.interactableObject);
        });
        
        _pokeInteractor.selectEntered.AddListener((args) =>
        {
            Debug.LogFormat("selectEntered: {0}", args.interactableObject);
        });
        
        _pokeInteractor.selectExited.AddListener((args) =>
        {
            Debug.LogFormat("selectExited: {0}", args.interactableObject);
        });
    }

    // Update is called once per frame
    void Update()
    {
        
    }
}

 

왼손과 오른손 컨트롤러를 선택해 부착 후 Poke Interactor를 프로퍼티에 넣어 줍니다.

오른손쪽도 동일하게

 

 

실행후 결과를 확인 합니다.

https://docs.unity3d.com/Packages/com.unity.xr.interaction.toolkit@2.4/manual/interactor-events.html

 

 

인터랙터 이벤트

 

이는 UI 버튼 누름에 반응하는 것과 같은 방식으로 에디터에 연결될 수 있는 이벤트입니다.

이는 Interactable과 상호작용할 수 있는 객체인 Interactor에 적용됩니다.

 

 

PropertyDescription

Hover Entered 이 Interactor가 Interactable 위에 마우스를 올리기 시작할 때 호출되는 이벤트입니다. 각 리스너에 전달된 HoverEnterEventArgs는 이벤트가 호출되는 동안에만 유효하며 이에 대한 참조를 보유하지 않습니다.
Hover Exited 이 Interactor가 Interactable 위에 마우스를 올리면 호출되는 이벤트입니다. 각 리스너에 전달된 HoverExitEventArgs는 이벤트가 호출되는 동안에만 유효하며 이에 대한 참조를 보유하지 않습니다.
Select Entered 이 Interactor가 Interactable을 선택하기 시작할 때 호출되는 이벤트입니다. 각 리스너에 전달된 SelectEnterEventArgs는 이벤트가 호출되는 동안에만 유효하며 이에 대한 참조를 보유하지 않습니다.
Select Exited 이 Interactor가 Interactable 선택을 종료할 때 호출되는 이벤트입니다. 각 리스너에 전달된 SelectEnterEventArgs는 이벤트가 호출되는 동안에만 유효하며 이에 대한 참조를 보유하지 않습니다.

 


 

버튼 누르기 인터렉션 

 

PushButton.unitypackage
0.04MB

 

 

 

 

Push Button 프리팹을 찾아 씬으로 가져온후 위치를 설정하고 실행해 봅니다.

 

 

 

푸시 버튼의 구조는 다음과 같습니다.

 

Push Button

┗ Visuals 

        ┗ Plate 

        ┗ StopOffset 

                ┗  Stop 

 

 

 

 

Push Button에 붙어 있는 컴포넌트 

 

 

Visuals와 StopOffset은 빈오브젝트 

 

나머지는 모두 메쉬입니다.

 

Plate 

 

 

Stop

 

 

 

 

Push Button을 선택 하고 Collider의 Center를 0으로 설정한후 

 

Poke Filter를 비활성화 > 활성화 해봅니다.

 

그러면 정확히 콜라이더의 중앙에서 콜라이더 상단까지 빨간색 라인이 그려지는것을 확인 할수 있습니다.

 

 

플레이를 해보면 포크 포인트가 닫는 위치부터 빨간색 레이의 아래쪽 끝부분까지 푸쉬가 되는것을 확인 할수 있습니다.

또한 콜라이더에서 나갔을 경우 원래 위치로 돌아갑니다.

 

 

 

XR Poke Follow Affordance 의 Return To Initial Position을 체크 해제 하면 원래 초기 위치로 돌아가지 않습니다.


 

 

 

Clamp To Max Distance 

Whethrer to keep the Poke Follow Transform from moving past a maximum distance from the poke target.

Poke Follow Transform이 찌르기 대상으로부터 최대 거리를 넘어서 이동하지 않도록 할지 여부입니다.

 

 

 

Max Distance 

The maximum distance from thie transform that the Poke Follow Transform can move.

Poke Follow Transform이 이동할 수 있는 변형으로부터의 최대 거리입니다.

 

 

 

 

m_MaxDistance는 일종의 거리 제한 또는 최대 거리를 나타내는 변수

게임 오브젝트가 어떤 대상에 따라 움직일 때, 이 대상과의 거리를 제한하기 위해 사용

m_MaxDistance 변수는 대상과의 거리를 제한하기 위해 사용되며, 이 거리가 설정된 최대 거리보다 크면 해당 거리로 제한

 

Vector3.ClampMagnitude는 주어진 3D 벡터를 특정 길이로 제한하는 데 사용되는 Unity 엔진의 함수입니다. 이 함수는 벡터의 크기(또는 길이)를 제한하고, 만약 제한된 크기보다 크면 해당 크기로 조절합니다.

함수 시그니처는 다음과 같습니다:

public static Vector3 ClampMagnitude(Vector3 vector, float maxLength);
  • vector: 크기를 제한하려는 3D 벡터입니다.
  • maxLength: 벡터의 최대 길이(크기)를 나타내는 값입니다. 만약 vector의 크기가 maxLength보다 크다면, vector는 maxLength로 스케일링(크기 조절)됩니다.

예를 들어, Vector3.ClampMagnitude를 사용하여 특정 벡터의 크기를 최대 1로 제한하려면 다음과 같이 호출할 수 있습니다:

 

Vector3 originalVector = new Vector3(2.0f, 0.0f, 0.0f); Vector3 clampedVector = Vector3.ClampMagnitude(originalVector, 1.0f);

 

이 경우, clampedVector(1.0f, 0.0f, 0.0f)이 될 것이며, 원래 벡터의 크기가 최대 길이보다 크기 때문에 길이가 1.0으로 제한됩니다.

 

 

 

 

 

 

 

 

 

 

이 둘을 같이 쓸때 의미가 있습니다.

 

이 코드 조각은 주어진 조건에 따라 targetPosition을 조정하고, 최대 거리(m_MaxDistance)를 초과하지 않도록 제한하는 역할을 합니다. 코드를 하나씩 설명하겠습니다.

  1. pokeTarget.InverseTransformPoint(data.axisAlignedPokeInteractionPoint):
    • pokeTarget의 로컬 좌표계로 변환된 data.axisAlignedPokeInteractionPoint를 targetPosition 변수에 저장합니다.
    • pokeTarget는 게임 오브젝트나 트랜스폼을 나타내며, data.axisAlignedPokeInteractionPoint는 다른 곳에서 계산된 위치 좌표를 의미합니다.
  2. if (m_ClampToMaxDistance && targetPosition.sqrMagnitude > m_MaxDistance * m_MaxDistance):
    • m_ClampToMaxDistance는 설정된 최대 거리를 사용할 것인지 여부를 나타내는 불리언 변수입니다.
    • targetPosition.sqrMagnitude는 targetPosition의 크기(거리)를 나타내는 값의 제곱입니다. 제곱을 사용하는 이유는 벡터의 크기를 비교할 때 제곱된 크기를 비교하면 계산이 빠르기 때문입니다.
    • m_MaxDistance * m_MaxDistance는 설정된 최대 거리의 제곱값입니다. 이 값과 targetPosition.sqrMagnitude을 비교합니다.
    • 조건문은 만약 m_ClampToMaxDistance가 참이고, targetPosition의 크기(거리)가 설정된 최대 거리의 제곱값보다 크다면 아래의 코드 블록을 실행합니다.
  3. targetPosition = Vector3.ClampMagnitude(targetPosition, m_MaxDistance):
    • 조건이 충족되면, Vector3.ClampMagnitude 함수를 사용하여 targetPosition을 m_MaxDistance로 제한합니다.
    • 이것은 targetPosition이 최대 거리를 초과하지 않도록 하는 역할을 합니다.
    • targetPosition의 크기(거리)가 m_MaxDistance보다 크면, 이 함수는 targetPosition을 길이가 m_MaxDistance인 벡터로 스케일링(크기를 조절)하여 제한된 크기로 만듭니다.

이 코드는 주로 게임 오브젝트나 트랜스폼을 대상으로 하는 상호 작용에서 사용되며, 해당 대상과의 거리가 설정된 최대 거리를 초과하지 않도록 보장합니다.

 

 

 

테스트 방법 

 

Push Button 자식으로 스피어를 하나 만들어 위치를 0, 0, 0 으로 설정 합니다. (콜라이더제거)

 

 

 

 EmergencyStopOffset을 선택하고 자식으로  스피어를 만들어 주고 위치를 0, 0, 0으로 합니다.

 

 

Push Button을 선택하고 자식으로 스피어를 하나 만들어 위치를 0, 0.045, 0 으로 설정 합니다.

 

 

 

플레이 해보면 콜라이더의 상단 마지막까지만 올라오는 모습을 확인 합니다.

 

 

 

즉, 콜라이더의 상단을 벗어날수는 없습니다.

 

 

 

 

 

이번에는 Max Distance의 값을 0.025로 변경하고 

Max Distance 스피어를 선택해 y 값을 0.025로 변경 합니다.

 

실행후 결과를 확인 합니다. 

포크 포인트가 콜라이더에 닿았을때 EmergencyStopOffset가 어디까지 움직이는지 확인해보면 됩니다.

 

 

정확히 0.025만큼 내려가는것을 확인할수 있습니다.

 

 

그래서 다음 과 같이 포크 포인트가 콜라이더에 닿았을때 버튼이 살짝 위로 올라가는 현상을 없애려면 

 

 

EmergencyStopOffset의 Y값 만큼 Max Distance가 설정 되어야 합니다.

 

EmergencyStopOffset오브젝트를 선택하고 Y 값을 복사 합니다.

 

 

 

Max Distance오브젝트를 선택하고 

복사한 위치로 설정 합니다.

 

 

Push Button을 선택해 XR Poke Follow Affordance컴포넌트의 Max Distance의 값을 0.03799997로 변경 합니다.

 

 

 

실행후 결과를 확인 합니다.

 

 


 

처음부터 만들어 보기 

 

이제 분석했으니 메쉬만 가지고 처음부터 구성 해보록 하겠습니다.

 

 

 

빈오브젝트 Push Button을 생성합니다

자식으로 Visuals 빈오브젝트를 생성 합니다.

자식으로 StopOffset과 Plate 빈오브젝트를 생성 합니다.

Stop Plate를 선택하고 Mesh Filter와 Mesh Renderer를 추가 합니다.

 

Mesh와 Material을 추가 합니다.

 

Stop Offset자식으로 빈오브젝트 Stop Mesh를 생성 합니다.

 

Mesh와 Materials를 넣어 줍니다.

 

 

 

 

Stop Offset의 피봇이 상단으로 오게 수정 합니다.

Stop Mesh를 선택하고 아래로 내려 줍니다.

 

Stop Offset이 0일경우가 완전히 눌러졌을때이므로 Plate를 선택해 아래로 내려 줍니다.

 

 

 

Push Button을 선택해 

Box Collider, XR Simple Interactable, XR Poke Filter, XR Poke Follow Affordance를 추가 합니다.

 

 

 

이때 Draw On Box Collider컴포넌트를 추가 해서 하면 편리 합니다.

 

using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEditor;
using UnityEngine;
 
[ExecuteAlways]
public class DrawOnBoxCollider : MonoBehaviour
{
    public enum DrawStates
    {
        DrawCube,
        DrawWireCube
    }
 
 
    public Transform boxColliderToDrawOn;
    [Range(0f, 1f)]
    public float colorTransparency;
    public bool draw = false;
    public DrawStates drawStates;
 
    private BoxCollider boxCollider = null;
 
    // Start is called before the first frame update
    void Start()
    {
        if (boxColliderToDrawOn != null)
        {
            boxCollider = boxColliderToDrawOn.GetComponent<BoxCollider>();
        }
 
        drawStates = DrawStates.DrawCube;
    }
 
    // Update is called once per frame
    void Update()
    {
       
    }
 
    private void DrawGizmosOnRunTime(Color color)
    {
        if (boxCollider != null && draw)
        {
            Gizmos.color = color;
            Matrix4x4 rotationMatrix = Matrix4x4.TRS(boxCollider.transform.position, boxCollider.transform.rotation, boxCollider.transform.lossyScale);
            Gizmos.matrix = rotationMatrix;
 
            switch (drawStates)
            {
                case DrawStates.DrawCube:
                    Gizmos.DrawCube(boxCollider.center, boxCollider.size);
                    break;
 
                case DrawStates.DrawWireCube:
                    Gizmos.DrawWireCube(boxCollider.center, boxCollider.size);
                    break;
            }
        }
    }
 
    private void OnDrawGizmos()
    {
        Color c = Color.red;
        c.a = colorTransparency;
        DrawGizmosOnRunTime(c);
    }
}

 

 

Stop Offset을 선택해 콜러이더 위쪽 상단으로 옮겨줍니다.

 

이것에 마춰 Plate도 이동 시킵니다.

 

 

Push Button 자식으로 디버깅용 Sphere를 만들어 콜라이더 상단으로 위치 시킵니다.

 

정확한 위치를 위해 Stop Offset의 Y값을 사용하십시오 

 

 

Push Button을 선택하고 XR Poke Filter의 Poke Configuration의 Poke Direction을 Negative Y로 수정 합니다.

 

 

XR Poke Follow Affordance를 선택해 Clamp To Max Distance를 체크 하고 

Max Distance 의 값을 0.0512로 설정 합니다. 

이 값은 Stop Offset의 Y값 입니다.

 

 

이제 완벽 하게 동작 하는 Push Button을 감상하며 영상을 마치도록 하겠습니다.

 

 

 

 

 


 

레퍼런스 

 

 

https://docs.unity3d.com/Packages/com.unity.xr.interaction.toolkit@2.4/manual/xr-poke-interactor.html

 

XR Poke Interactor | XR Interaction Toolkit | 2.4.3

XR Poke Interactor Interactor used for interacting with interactables through poking. Property Description Interaction Manager The XRInteractionManager that this Interactor will communicate with (will find one if None). Interaction Layer Mask Allows intera

docs.unity3d.com

https://docs.unity3d.com/Packages/com.unity.xr.interaction.toolkit@2.3/manual/xr-poke-filter.html

 

XR Poke Filter | XR Interaction Toolkit | 2.3.2

XR Poke Filter Filter component that allows for basic poke functionality and to define constraints for when the interactable will be selected. Property Description Poke Data  Use Asset Enable to reference an externally defined PokeThresholdData asset usi

docs.unity3d.com

https://docs.unity3d.com/Packages/com.unity.xr.interaction.toolkit@2.4/changelog/CHANGELOG.html

 

Changelog | XR Interaction Toolkit | 2.4.3

Changelog All notable changes to this package will be documented in this file. The format is based on Keep a Changelog and this project adheres to Semantic Versioning. [2.4.3] - 2023-07-21 Fixed Fixed Starter Asset validation check that would trigger a rac

docs.unity3d.com

https://docs.unity3d.com/Packages/com.unity.xr.interaction.toolkit@2.3/manual/affordance-system.html

 

Affordance system | XR Interaction Toolkit | 2.3.2

Affordance system The XR Interaction Toolkit package provides an affordance system which enables users to create visual and auditory feedback to interaction states. The general flow of information starts by adding an Affordance State Provider to the scene

docs.unity3d.com

 

반응형
: