홀로렌즈 UI 변경

기존 홀로렌즈 AR Controller의 직관성 및 조작의 편리성 향상위해 UI를 변경하는 작업을 진행하였다.


image


이를 통해 메인매니저의 OnGazeEnter, OnGazeLeave, Update 함수등을 변경하였다.

결과

01

일정 시간 물체를 바라보면 Controller의 조종 대상이 변경된다.

조종 대상 중 Vuforia Image Target을 통해 그 자리에 “고정”된 물체는 인식시 이동버튼이 비활성화 되도록 변경하였다.

04

리모콘으로 조종하는 물체만 영향을 받는다. 위 시진에서, 왼쪽에 위치한 물체만 분해가 되는 모습이다.

05

위 영상은 두 물체를 조종하는 모습이다. 조종 할 물체를 바라보고 있으면, 컨트롤러의 대상이 바뀌고 그 대상을 조작 할 수 있다.

06

같은 방법으로 하나의 리모콘으로 두 물체를 모두 제거하는 모습이다.

구현

아래는 아예 기존의 방식을 참조하여 새로 만든 AR Controller UI 코드이다.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;

//================================================================================
//  ENUM 선언 | AR Remote Contoller Btn 클래스에서 버튼 구분을 위해 이용
//================================================================================
public enum EnumARReomoteContolUIBtnType {
    EnumARContolUIBtnType_MOVE = 1,
    EnumARContolUIBtnType_SHOW = 1 << 1,
    EnumARContolUIBtnType_ASSEMBLE = 1 << 2,
    EnumARContolUIBtnType_ZOOM = 1 << 3,
    EnumARContolUIBtnType_ROTATE = 1 << 4,
    EnumARContolUIBtnType_RESET = 1 << 5,
    EnumARContolUIBtnType_ANIMATION = 1 << 6,
    EnumARContolUIBtnType_REMOVE = 1 << 7,
    EnumARContolUIBtnType_HIDDEN = 1 << 8,
}

//================================================================================
//  AR Remote Controller | AR Control UI Handler 참고해서 구현
//================================================================================
public class ARRemoteController : MonoBehaviour {
    //============================================================================
    //  Public, Private 필드 선언
    //============================================================================
    public GameObject targetObject;                     // 바라본 물체
    public Text objectNameTitle;                        // 바라본 물체의 이름을
                                                        // 적을 텍스트 필드
    public GameObject[] buttonArr = new GameObject[7];  // 버튼 GameObject
                                                        // 저장하는 배열
    public Sprite[] activeSprite = new Sprite[7];       // 활성화된 버튼 Sprite
    public Sprite[] inactiveSprite = new Sprite[7];     // 비활성화된 버튼 Sprite

    private bool isPartModel = false;                   // 파트모델인지 여부
    private bool isHyundaiModel = false;                // 현대 모델인지 여부
    private bool isShowing = true;                      // 물체를 보여줄지 여부
    private ARRemoteControllerBtn[] buttonScriptArr
                        = new ARRemoteControllerBtn[7];
                                                        // 버튼들의 Dictionary
    private GestureAction gestureAction;                // 바라본 물체의
                                                        // 제스처 액션
    private class TransformValue {                      // 원복시 사용할
                                                        // transform 클래스
        public Vector3 localPosition;
        public Vector3 localScale;
        public Quaternion rotation;
    }
    private TransformValue originalTransformValue = new TransformValue();
                                                        // 초기 transform 상태
                                                        // 저장용 변수

    //============================================================================
    //  Start 메소드
    //============================================================================
    public void Start() {
        // GameObject인 buttonArr에서 AR Remote Controller Btn
        // 클래스만 꺼내서 따로 저장
        for (int i = 0; i < 7; i++)
            buttonScriptArr[i] =
                 buttonArr[i].GetComponent<ARRemoteControllerBtn>();

        // 타겟의 분류에 따라, 버튼 활성도 다르게 처리
        foreach (ARRemoteControllerBtn button in buttonScriptArr) {
        switch (button.buttonType) {
            case EnumARReomoteContolUIBtnType.EnumARContolUIBtnType_MOVE:
                button.SetOnClickARControlButton(OnClick_Move);
                break;
            case EnumARReomoteContolUIBtnType.EnumARContolUIBtnType_SHOW:
                button.SetOnClickARControlButton(OnClick_Show);
                break;
            case EnumARReomoteContolUIBtnType.EnumARContolUIBtnType_ASSEMBLE:
                button.SetOnClickARControlButton(OnClick_Assemble);
                break;
            case EnumARReomoteContolUIBtnType.EnumARContolUIBtnType_RESET:
                button.SetOnClickARControlButton(OnClick_Reset);
                break;
            case EnumARReomoteContolUIBtnType.EnumARContolUIBtnType_ANIMATION:
                button.SetOnClickARControlButton(OnClick_Animation);
                break;
            case EnumARReomoteContolUIBtnType.EnumARContolUIBtnType_ROTATE:
                button.SetOnClickARControlButton(OnClick_Rotate);
                break;
            case EnumARReomoteContolUIBtnType.EnumARContolUIBtnType_REMOVE:
                button.SetOnClickARControlButton(OnClick_Remove);
                break;
        }
        }
    }

    //============================================================================
    //  물체를 바라봤을 경우, 리모컨이 제어할 대상 변경해주는 메소드
    //============================================================================
    public void ChangeObject (GameObject target) {

        //========================================================================
        //  새로 바라볼 경우, 관련된 필드들 초기화
        //========================================================================
        this.targetObject = target;
        this.isShowing = true;
        this.objectNameTitle.text = "3D MODEL CONTROLLER : " + target.name;
        this.originalTransformValue.localScale =
                                            targetObject.transform.localScale;
        this.originalTransformValue.localPosition =
                                        targetObject.transform.localPosition;
        this.originalTransformValue.rotation = targetObject.transform.rotation;
        // 제스처 액션 컴포넌트 초기화 (없으면 만듦)
        if (!target.GetComponent<GestureAction>())
            targetObject.AddComponent<GestureAction>();
        this.gestureAction = targetObject.GetComponent<GestureAction>();

        /*
         *
         * @kimYC1223
         * TO-DO : Tag에 따른 변수 설정 (isPartModel,isHyundaiModel)
         *
         */

        // 타겟의 분류에 따라, 버튼 활성도 다르게 처리
         if (target.tag == "PartModel_ImageTarget") {
             buttonScriptArr[0].SetOnClickARControlButton(null);
             buttonArr[0].GetComponent<SpriteRenderer>().sprite =
                                                         inactiveSprite[0];
             buttonArr[0].GetComponent<BoxCollider>().enabled = false;
         } else {
            buttonScriptArr[0].SetOnClickARControlButton(OnClick_Move);
            buttonArr[0].GetComponent<SpriteRenderer>().sprite =
                                                        activeSprite[0];
             buttonArr[0].GetComponent<BoxCollider>().enabled = true;
         }
    }

    //============================================================================
    //  물체를 이동시키는 버튼
    //============================================================================
    public void OnClick_Move() {
        if (targetObject == null) return;
        Debug.Log("MOVE BUTTON CLICK");
        // MOVE 버튼의 경우, Placeable 스크립트의 On Placement Start 실행
        if (targetObject.GetComponent<Placeable>())
            targetObject.GetComponent<Placeable>().OnPlacementStart();
    }

    //============================================================================
    //  렌더러를 토글시키는 버튼
    //============================================================================
    public void OnClick_Show() {
        if (targetObject == null) return;
        Debug.Log("SHOW BUTTON CLICK");
        isShowing = !isShowing;
        // Show 버튼의 경우, 바라보고 있는 오브젝트의 Mesh Renderer를 토글
        foreach (MeshRenderer childMeshRenderer in
                        targetObject.GetComponentsInChildren<MeshRenderer>()) {
            childMeshRenderer.enabled = isShowing;
        }
    }

    //============================================================================
    //  물체를 파츠로 분해 / 파츠를 물체로 재조립 하는 버튼
    //============================================================================
    public void OnClick_Assemble() {
        if (targetObject == null) return;
        Debug.Log("ASSEMBLE BUTTON CLICK");
        // AR Twin Model Assemble Handler를 이용
        if (targetObject.GetComponent<ARTwinModelAssembleHandler>()) {
            int transformSendAssignType =
                (int)EnumTransformAssignType.EnumTransformAssignType_Assemble;
            ARTwinModelAssembleHandler handler =
                        targetObject.GetComponent<ARTwinModelAssembleHandler>();
            int assembleState = handler.ProcAssmebleState();
            CustomMessages.Instance.
                SendObjectTransform(targetObject,transformSendAssignType,
                    targetObject.transform,assembleState,0);
        }
    }

    //============================================================================
    //  물체의 여러가지 상태를 초기 상태로 되돌리는 버튼
    //============================================================================
    public void OnClick_Reset() {
        if (targetObject == null) return;
        Debug.Log("RESET BUTTON CLICK");
        // 일단 Position / Rotation / Scale을 원복함
        targetObject.transform.rotation = originalTransformValue.rotation;
        targetObject.transform.localScale = originalTransformValue.localScale;
        targetObject.transform.localPosition =
                                        originalTransformValue.localPosition;
        // AR Twin Model Assemble Handler 리셋
        foreach (ARTwinModelAssembleHandler handler in
            targetObject.GetComponentsInChildren<ARTwinModelAssembleHandler>())
            handler.ResetAssmebleState();
        // Button 의 상태도 초기화 시켜줌
        foreach (ARRemoteControllerBtn button in buttonScriptArr)
            button.ResetButtonColor();
    }

    //============================================================================
    //  물체가 가지고있는 애니메이션을 재생하는 버튼
    //============================================================================
    public void OnClick_Animation() {
        if (targetObject == null) return;
        Debug.Log("ANIMATION BUTTON CLICK");
        // TO-DO
        // @KimYC1223 애니메이션 재생되도록 설정
    }

    //============================================================================
    //  물체를 제스처 액션을 통해 회전 / 확대 / 축소 하는 버튼
    //============================================================================
    public void OnClick_Rotate() {
        if (targetObject == null) return;
        Debug.Log("ROTATE BUTTON CLICK");
        // Gesture Action 컴포넌트가 있다면, 해당 기능을 토글함
        if (gestureAction != null)
            gestureAction.StartStopGestureAction();
    }

    //============================================================================
    //  물체를 Active 상태를 비활성화 시키는 버튼 (Show Btn과 유사)
    //============================================================================
    public void OnClick_Remove() {
        if (targetObject == null) return;
        Debug.Log("REMOVE BUTTON CLICK");
        // 만약 Vuforia 기반 오브젝트라면, 리셋 해줌
        if (GetComponentsInChildren<PartImageTargetTrackableEventHandler>()
                                                                .Length > 0) {
            PartImageTargetTrackableEventHandler[] handlers =
                GetComponentsInChildren<PartImageTargetTrackableEventHandler>();
            foreach (PartImageTargetTrackableEventHandler handler in handlers)
                handler.ResetFound();
        }
        // 오브젝트 제거
        targetObject.SetActive(false);
        targetObject = null;
        objectNameTitle.text = "3D MODEL CONTROLLER";

        // 버튼 원복
        for(int i = 0; i < buttonArr.Length; i++)
            buttonArr[i].GetComponent<SpriteRenderer>().sprite = activeSprite[i];
    }
}

이슈

  • 현재 물체의 애니메이션이 없다. #116 이후에 수정 할 것.
  • 현재 설정 리셋과 분해/조립에 약간의 버그가 있다. 수정할 것.
  • 현재 버튼의 하이라이트 (트리거 버튼의 ON/OFF 표시)에 버그가 조금 있다. 수정 할 것.