홀로렌즈 AR Controller의 직관성 및 조작의 편리성 향상을 위해, 기존의 오브젝트당 하나의 컨트롤러를 가지는 기존의 방식이 아닌, 하나의 컨트롤러로 다수의 오브젝트를 조종하는 형식으로 변경한다.

홀로렌즈 UI 변경

image

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

image;

결과

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 표시)에 버그가 조금 있다. 수정 할 것.