본문 바로가기
Unity XR

[Unity XR] Measuring Tape XR

by 나만두 2024. 8. 21.

 

 

 

 

 

 

 

 

XR 기본 세팅 후, 

 

Building Block- Camera Rig, Interaction, Passthrough 추가

 

 

 

Learn XR Core 패키지 추가:Dilmer Valecillos

https://github.com/dilmerv/com.learnxr.core.git

 

GitHub - dilmerv/com.learnxr.core: A set of scripts or utilities we use for most of the videos taught on YouTube.

A set of scripts or utilities we use for most of the videos taught on YouTube. - dilmerv/com.learnxr.core

github.com

 

 

 

 

 

 

길이 측정하기 위한 TapeArea를 만들고, Controller Anchor에 붙인다. 

 

 

 

 

 

 

MeasuringTapeFeature 오브젝트를 생성하고, 스크립트를 만든다.

 

 

 

 

 

전체 코드

 

using System.Collections.Generic;
using System.Linq;
using LearnXR.Core.Utilities;
using TMPro;
using UnityEngine;

public class MeasureTapeFeature : MonoBehaviour
{
    [Range(0.005f,0.05f)] //Tape폭의 최소, 최대값 설정
    [SerializeField]
    private float tapeWidth =0.01f; // Tape폭 설정
    [SerializeField] private OVRInput.Button tapeActionButton; //Tape를 작동시킬 OVRInput.Button설정
    [SerializeField] private Material tapeMaterial; //Tape Material 설정

    [SerializeField] private GameObject measurementInfoPrefab; //측정정보를 나타내는 게임 오브젝트
    [SerializeField] private Vector3 measurementInfoControllerOffset= new (0,0.045f,0); //측정정보오브젝트의 오프셋값
    [SerializeField] private string measurementInfoFormat= "<mark=#0000005A padding=\"20, 20, 10, 10\"><color=white>{0}</color></mark>"; //포맷
    [SerializeField] private float measurementInfoLength =0.01f; //측정정보 시각화에 필요한 길이 

    [SerializeField] private Transform leftControllerTapeArea; //왼쪽 컨트롤러 Tape Area
    [SerializeField] private Transform rightControllerTapeArea;// 오른쪽 컨트롤러 Tape Area

    private List<MeasuringTape> savedTapeLines= new(); //측정한 값을 저장할 리스트 생성
    private TextMeshPro lastMeasurementInfo; //측정 정보의 텍스트값 
    private LineRenderer lastTapeLineRenderer;//LineRenderer
    private OVRInput.Controller? currentController; //현재 사용중인 컨트롤러 
    private OVRCameraRig cameraRig; //CameraRig

    private void Awake() => cameraRig=FindObjectOfType<OVRCameraRig>();// =>를 사용한 Awake 람다 표현식이다. OVRCameraRig를 찾아 cameraRig에 할당 

    void Update()
    {
        HandleControllerActions(OVRInput.Controller.LTouch, leftControllerTapeArea);// 왼쪽 컨트롤러의 동작 처리
        HandleControllerActions(OVRInput.Controller.RTouch, rightControllerTapeArea);// 오른쪽 컨트롤러의 동작 처리 
        
    }

    private void HandleControllerActions(OVRInput.Controller controller, Transform tapeArea) //컨트롤러 버튼 동작에 따른 조건 메서드
    {
        if(currentController!=controller&& currentController !=null) return;// 만약 현재 컨트롤러가 controller가 아니고 null값이 아니라면 반환

        if(OVRInput.GetDown(tapeActionButton, controller))// 버튼을 눌렀을 때, 실행
        {
            currentController= controller;//현재 컨트롤러 설정
            HandleDownAction(tapeArea);//HandleDownAction 호출 
        }
        if(OVRInput.Get(tapeActionButton, controller))//버튼을 누른 상태일때, 실행
        {
            HandleHoldAction(tapeArea);//HandleHoldAction 호출
        }
        if(OVRInput.GetUp(tapeActionButton, controller))// 버튼을 떼었을 때, 실행
        {
            currentController=null; //현재 컨트롤러= null
            HandleUpAction(tapeArea);//HandleUpAction 호출 
        }

    }

    private void HandleDownAction (Transform tapeArea)// 버튼을 눌렀을 때 실행될 메서드
    {
        CreateNewTapeLine(tapeArea.position); //CreateNewTapeLine 호출 
        AttachAndDetachMeasurementInfo(tapeArea);//AttachAndDetachMeasurementInfo 호출
    }

    private void HandleHoldAction (Transform tapeArea)// 버튼을 누른 상태일 때 실행될 메서드
    {
        lastTapeLineRenderer.SetPosition(1, tapeArea.position);
        // LineRenderer.SetPosition:라인렌더러의 시작과 끝 지점을 설정해주는 함수 
        CalculateMeasurements();// 길이 계산 메서드 호출 
        AttachAndDetachMeasurementInfo(tapeArea);//AttachAndDetachMeasurementInfo 호출
    }

    private void HandleUpAction (Transform tapeArea)// 버튼을 뗐을 때 실행될 메서드
    {
        AttachAndDetachMeasurementInfo(tapeArea,false);//AttachAndDetachMeasurementInfo 호출, attachToController를 false로
    }


    private void CreateNewTapeLine(Vector3 initialPosition) //TapeLine 드로우 메서드
    {
        var newTapeLine= new GameObject(name:$"TapeLine_{savedTapeLines.Count}",typeof(LineRenderer));
        // 오브젝트를 생성한다. ex) TapeLine_1, TapeLine_2, TapeLine_3 ... 이 오브젝트는 LineRenderer 컴포넌트가 자동으로 추가된다. 
        lastTapeLineRenderer = newTapeLine.GetComponent<LineRenderer>();// lastTapeLineRenderer를 생성된 오브젝트의 LineRenderer로 할당
        lastTapeLineRenderer.startWidth=tapeWidth;// 시작 TapeWidth
        lastTapeLineRenderer.endWidth=tapeWidth;// 끝 TapeWidth 
        lastTapeLineRenderer.material= tapeMaterial;//Material
        lastTapeLineRenderer.SetPosition(0,initialPosition); 
        // LineRenderer.SetPosition:라인렌더러의 시작과 끝 지점을 설정해주는 함수 
        //시작 지점을 설정

        lastMeasurementInfo= Instantiate(measurementInfoPrefab, Vector3.zero, Quaternion.identity).GetComponent<TextMeshPro>();
        // 측정인포오브젝트를 포지션값은 (0,0,0), Rotation는 (0,0,0)-회전이 없는 상태로 인스턴스화. 오브젝트의 TextMeshPro를 lastMeasurementInfo에 할당 
        lastMeasurementInfo.GetComponent<BillboardAlignment>().AttachTo(cameraRig.centerEyeAnchor);
        //lastMeasurementInfo BillboardAlignment 컴포넌트의 AttachTo를 통해 게임 오브젝트가 항상 카메라 시선에 정렬되도록 설정 
        lastMeasurementInfo.gameObject.SetActive(false); //게임 오브젝트 비활성화

        savedTapeLines.Add(new MeasuringTape 
        {
            TapeLine= newTapeLine,
            TapeInfo= lastMeasurementInfo
        });
        // MeasuringTape 객체를 생성, TapeLine,TapeInfo을 할당해 savedTapeLines리스트에 추가해 나중에 참조할 수 있도록 한다.
    } 

    private void AttachAndDetachMeasurementInfo(Transform tapeArea, bool attachToController= true) //측정정보를 두 지점 사이에 배치하는 메서드 
    {
        if(attachToController) //측정중일 때
        {
            lastMeasurementInfo.gameObject.SetActive(true);//측정인포 오브젝트 활성화 
            lastMeasurementInfo.transform.SetParent(tapeArea.transform.parent);// 측정인포의 부모 오브젝트를 tapeArea의 부모 오브젝트로 설정
            lastMeasurementInfo.transform.localPosition=measurementInfoControllerOffset;//측정인포 오브젝트 포지션값 설정
        }
        
        else//측정이 끝났을 때 = attachToController false
        {
            lastMeasurementInfo.transform.SetParent(lastTapeLineRenderer.transform);//측정인포가 테이프 라인의 위치를 기반으로 위치
            var lineDirection= lastTapeLineRenderer.GetPosition(0) - lastTapeLineRenderer.GetPosition(1); //lineDirection을 lastTapeLineRenderer의 끝점과 시작점의 차로 방향벡터 계산 
            
            Vector3 lineCrossProduct= Vector3.Cross(lineDirection,Vector3.up); // Vector3.Cross 메서드를 사용하여lineDirection와 위쪽 방향 벡터(Vector3.up)의 외적을 계산

            Vector3 lineMidPoint =(lastTapeLineRenderer.GetPosition(0)+lastTapeLineRenderer.GetPosition(1))/2.0f;// 중앙값 계산해 lineMidPoint에 할당 
            lastMeasurementInfo.transform.position=lineMidPoint+(lineCrossProduct.normalized * measurementInfoLength) ;// lineMidPoint에서 정규화된 수직방향(lineCrossProduct)으로 일정거리(measurementInfoLength)만큼 떨어진 거리에 배치
            
        }
    }

    private void CalculateMeasurements()// 거리 측정 메서드
    {
        var distance = Vector3.Distance(lastTapeLineRenderer.GetPosition(0),lastTapeLineRenderer.GetPosition(1)); //시작,끝 지점의 거리를 계산해 distance에 할당
        var inches =MeasuringTape.MetersToInches(distance);//Meter->Inches
        var centimeters=MeasuringTape.MetersToCentimeters(distance);//Meter-.Centimeters
        var lastLine =savedTapeLines.Last(); //저장된 savedTapeLines의 마지막 라인을 lastLine에 할당 
        lastLine.TapeInfo.text=string.Format(measurementInfoFormat, $"{inches:F2}inches<i> {centimeters:F2}cm</i>");  // 포맷
    }

    private void OnDestroy() //savedTapeLines에 저장된 리스트값 파괴 메서드 
    {
        foreach(var tapeLine in savedTapeLines) //savedTapeLines에 저장된 수 만큼 순회하며 반복
        {
            Destroy(tapeLine.TapeLine);// 제거
        }
        savedTapeLines.Clear();// 리스트 모든 요소 제거 

        // 리스트를 초기화하여 메모리 참조 오류를 방지
    }
}

 

 

MeasuringTape스크립트

 

 

 

TapeLine과 Info값을 받아 Meters--> Inches, Centimeters값으로 반환해 준다.

 

 

 

 

LearnXR 패키지 내  BillboardAlignment 스크립트

 

 

 

 

 

 

참고 영상:https://www.youtube.com/watch?v=es9d1y1hYfM&t=456s

 

'Unity XR' 카테고리의 다른 글

[Unity XR] OVRInput-Shoot Ball XR  (0) 2024.08.25
[Unity XR] Mixed Reality Utility Kit - Scene Effect Mesh  (0) 2024.08.24
[Unity XR] Grab Interaction XR  (0) 2024.08.23
[Unity XR] Meta XR CameraRig  (0) 2024.08.09
Unity XR OVR Setting  (0) 2024.08.02