[SKKU DT] 54일차 -유니티 VR 오큘러스 프로젝트 만들기(2) -VR 이동, 물체 상호작용, UI 뜨게 하기

2024. 1. 17. 18:25SKKU DT

728x90
반응형
 

[SKKU DT] 53일차 -유니티 VR 오큘러스 프로젝트 만들기

3D URP 프로젝트 생성 생성 후 [Window] - [Package Manager]에서 XR Interaction Toolkit을 검색하고, 최신 버전을 다운로드 XR Interaction Toolkit의 샘플 다운로드, Universal RP의 샘플 다운로드 Project Settings에서 XR Plug

lightbakery.tistory.com


 

 

이전 프로젝트에 이어서, 새로운 애니메이션을 넣어 다른 애니메이션도 구현하도록 한다.

Blend Tree에서 가장 아래 애니메이션에 l_hand_point 애니메이션을 넣는다. 오른손도 마찬가지로 넣는다.

 

 

처음에 컨트롤러가 손을 인식하지 않아서 아래의 세팅을 추가했다.

 

잘 된다 굿~

 

 


 

 

**안되는 기기 고친 방법

윈도우즈로 바꾸기

 

[Auto Graphics API for Windows] 체크 해제하고 아래에 Vulkan, OpenGLES3만 넣기

 

[Scripting Backend]를 [IL2CPP]로 설정

 

[OpenXR] 탭에서 윈도우즈에 프로파일 2개 넣기

 

 


 

 

아래의 경로에 들어가면 프리팹이 있다. Hierarchy 상에 Controller의 자식으로 넣는다.

 

 

Direct Interactor의 설정은 아래와 같다.

 

 

책상 에셋을 가져와서 배치한다.

 

 

Cup 에셋의 스크립트와 Rigidbody를 지우고 컴포넌트 추가 검색에서 xr grab interactable을 검색하면 Rigidbody도 같이 들어온다.

 

잘 잡히면 성공!

 

 

정확한 잡기를 가능하게 하려면 XR Grab Interactable 컴포넌트에서 [Use Dynamic Attach] 체크를 하면 된다.

 

 

XR Grab Interactable에서 [Movement Type]을 [Velocity Tracking]으로 설정하면 책상을 통과하지 않는다.

 

 

컵을 책상에 문지르는 등의 모서리에 상호작용이 불안정하다면 Rigidbody의 [Collision Detection]을 [Continuous]로 바꾼다.

 

 

키보드에 왼쪽은 [Movement Type]이 [Instantaneous], 오른쪽은 [Velocity Tracking]일 때의 차이이다.

 

 


 

 

이제 책상을 잠깐 치우고 WorkTable을 불러온다.

 

 

드라이버에 XR Grab Interactable 컴포넌트를 추가하고, 항상 일정한 부분에서 잡는 것을 구현하기 위해서, [Attach Transform] 부분에 빈 게임 오브젝트를 만들어서 위치 값을 넣는다.

 

 

다른 방법으로는, Colliders에 콜라이더를 넣어서 잡게하는 방법도 있다.

 

 


 

 

물건을 잡을 때 손이 안보이게 하기

Driver에 DisableGrabbingHandle 스크립트 만들기

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

public class DisableGrabbingHandle : MonoBehaviour
{
    public GameObject leftHandModel;
    public GameObject rightHandModel;

    private void Start()
    {
        XRGrabInteractable grabInteractable = GetComponent<XRGrabInteractable>();
        grabInteractable.selectEntered.AddListener(HideGrabbingHand);
        grabInteractable.selectExited.AddListener(ShowGrabbingHand);
    }
    /// <summary>
    /// 이벤트 가져오기
    /// </summary>
    /// <param name="args">이벤트</param>
    public void HideGrabbingHand(SelectEnterEventArgs args)
    {
        if (args.interactorObject.transform.tag == "Left Hand")
        {
            leftHandModel.SetActive(false);
        }
        else if (args.interactorObject.transform.tag == "Right Hand")
        {
            rightHandModel.SetActive(false);
        }
    }

    public void ShowGrabbingHand(SelectExitEventArgs args)
    {
        if (args.interactorObject.transform.tag == "Left Hand")
        {
            leftHandModel.SetActive(true);
        }
        else if (args.interactorObject.transform.tag == "Right Hand")
        {
            rightHandModel.SetActive(true);
        }
    }
}

 

 

태그 추가

 

 

스크립트에 Hand 프리팹 끌어다놓기

 

 

Direct Interactor에도 Left Hand 태그 넣기

 

 

손이 없어진다!

 

 

드라이버 위치가 잘 안맞는 부분은 Driver Transform을 Driver 프리팹의 자식으로 넣었고, Collider를 넣어서 해결했다.

 

 

 


 

 

Locomotion 이동하기

XR Origin에 Locomotion System 스크립트 컴포넌트 넣고 XR Origin 끌어다 놓기

 

 

Dynamic Move Provider 스크립트 컴포넌트 검색해서 추가, 아래 그림처럼 끌어다 놓기

 

 

밑에 Left Hand Move Action의 Use Reference 체크하고 LeftHand Locomotion/Move 검새해서 추가

 

 

밑에 카메라와 컨트롤러들 끌어다 놓기

 

왼손의 컨트롤러 조이스틱으로 이동이 가능해졌다.

 

 

여기에 Character Controller 컴포넌트를 추가해서 사물을 뚫고 지나가지 못하게 한다. 해당 컴포넌트의 설정은 아래처럼 했다.

 

 

Character Contorller Driver 스크립트 컴포넌트를 넣으면 머리와 몸통이 분리되는 현상이 일어나지 않는다.

 

 


 

 

회전하기

Continuous Turn Provider(Action-based) 스크립트 컴포넌트 추가, Right Hand Trun 체크하고 RightHand Locomotion/Turn 넣는다.

 

 

Snap Turn Provider(Action-based) 스크립트 컴포넌트 추가. 단, 일반 턴과 스탭 턴 둘 중 하나만 쓸 수 있다.

 

 

왼쪽이 Continuous Turn, 오른쪽이 Snap Turn이다.

 

 


 

 

텔레포트 이동 하기

Right Teleport Interactor에 Teleport Interactor 프리팹 넣기

 

 

[XR Ray Interactor] 컴포넌트에서 [Interaction Layer Mask]에서 레이어를 추가해서 31번째 레이어에 Teleport 추가한다.

 

 

Project Validation에서 떴던 31번째 레이어 Teleport도 해결할 수 있다.

 

 

텔레포트 공간 지정

빈 게임오브젝트 만들어서 TeleportArea라는 이름을 지어주었다. 여기에 [Teleportation Area] 스크립트 컴포넌트 추가, Colliders에 Box Collider가 들어있는 Floor 객체 넣기.

 

 

Teleport Interactor에 XR Controller 넣기

 

 

프리셋에서 Right Controller Default 값 넣기

 

 

맨 위에 4개는 체크 해제하기

 

 

Selection Action에 Right Hand Locomotion/Teleport Selet 넣기. 그 밑에 Selection Action Value에도 마찬가지로 같은 것 넣는다.

 

 

텔레포트 후 방향 설정

[Match Directional Input] 박스 체크

 

 

TeleportArea의 [Interaction Layer Mask]에도 Teleport 레이어 적용

 

 

 

XR Origin에 [Teleportation Provider] 스크립트 컴포넌트 추가하기, [System] 부분에 XR Origin 끌어다놓기

 

오른손 조이스틱을 움직이는대로 해당 부분에서 방향까지 설정돼서 텔레포트 된다.

 

 


 

 

 

볼트가 두 개 빠진 모터가 있다. 이걸 맞게 끼우는 UI와 붙을 수 있게하는 기능을 넣는다.

 

 

빈 오브젝트를 하나 만들어서 SocketInteractorBolt로 이름을 바꾼다. Motors 프리팹의 자식으로 넣기

 

 

XR Socket Interactor 스크립트 컴포넌트 추가, [Hover Mesh Material] 자리에 들어가는 Material은 프리뷰로 보여주는 역할이다.

 

 

Material 생성

 

 

[Hover Mesh Material]에 만든 메테리얼 넣기

 

 

씬 안에 볼트를 맞는 위치에 직접 넣어보고 SocketInteractorBolt 객체와 Transform 위치를 맞춘다.

 

 

Bolt에 XR Grab Interactable 스크립트 컴포넌트 추가

 

 

SocketInteractorBolt에 Sphere Collider 추가하고 Is Trigger 체크하기

 

 

해당 위치에 볼트를 움직이면 프리뷰가 보이면서 맞게 들어간다.

 

 


 

 

지정한 객체만 프리뷰가 뜨게 하기

SocketInteractorBolt에 XRSocketTagInteractor 스크립트 생성, 기존에 XR Socket Interactor 삭제

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

public class XRSocketTagInteractor : XRSocketInteractor
{
    public string targetTag;

    public override bool CanHover(IXRHoverInteractable interactable)
    {
        return base.CanHover(interactable) && interactable.transform.tag == targetTag;
    }

    public override bool CanSelect(IXRSelectInteractable interactable)
    {
        return base.CanSelect(interactable) && interactable.transform.tag == targetTag;
    }
}

 

 

Target Tag에 "Bolt" 추가해서 지정한 것만 프리뷰가 뜨게 한다.

 

 


 

 

UI 만들기

캔버스의 [Render Mode]를 [World Space]로 바꾼다. VR은 World Space를 쓴다.

Canvas는 Transform 리셋, Button은 Scale을 0.001로 설정한다.

 

 

버튼의 위치는 보기 좋은 위치에 놔둔다.

 

 

버튼이 뜨는 스크립트 만들기.

SocketInteractorBolt에 SelectEnterUIHandler 스크립트 생성.

 

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

public class SelectEnterUIHandler : MonoBehaviour
{
    public GameObject uiGameObject;
    private XRSocketInteractor socketInteractor;

    private void Awake()
    {
        socketInteractor = GetComponent<XRSocketInteractor>();
    }

    private void OnEnable()
    {
        if (socketInteractor != null && uiGameObject != null)
        {
            socketInteractor.selectEntered.AddListener(DisplayUI);
            socketInteractor.selectExited.AddListener(HideUI);
        }
    }

    private void OnDisable()
    {
        if(socketInteractor != null)
        {
            socketInteractor.selectEntered.RemoveListener(DisplayUI);
            socketInteractor.selectExited.RemoveListener(HideUI);
        }
    }

    void DisplayUI(SelectEnterEventArgs args)
    {
        uiGameObject.SetActive(true);
        Invoke("HideUI", 2f);
    }

    void HideUI()
    {
        if (uiGameObject.activeSelf)
        {
            uiGameObject.SetActive(false);
        }
    }

    void HideUI(SelectExitEventArgs args)
    {
        CancelInvoke("HideUI");
        HideUI();
    }
}

 

처음 버튼은 비활성화

 

UI가 잘 뜬다.

728x90
반응형