danmujicat 2023. 10. 6. 17:27

AR 개발을 위한 프로젝트를 준비하기 위해 Window | Package Manangement 메뉴를 선택하여  Package Manangement 창을 열고 AR 장치 플러그인을 설치합니다.

1장의  ARCore 개발을 위한 환경 구축하기 참고하세요

더보기
  •  AR 장치용 XR plugins 설치하기
  • ARCore XR Plugin 설치하기
  • AR Foundation 패키지 설치하기
  • Input System 설치하기
  • Androd 플랫폼의 ARcore 설정하기
  • Player 설정하기

1. 기본 AR 장면 만들기

① File | Save As 메뉴를 선택하여  Assets/Scenes/ 폴더에 이름을 ARGallery로  저장합니다.

② Hierarchy 창에서 Main Camera를 삭제합니다.

③ GameObject |  XR | AR Session 메뉴를 선택하여 Hierarchy 창에 AR Session을 추가합니다.

④ GameObject |  XR | XR Origin  메뉴를 선택하여 Hierarchy 창에 XR Origin 을 추가합니다.

 

1.1  AR Default Plane 프리팹 설정하기

① Hierarchy 창에서 AR Session Origin 오브젝트를 선택하고 Inspector 창에 Add Component 버튼을 클릭하여 AR  Plane Manager 컴포넌트를 추가합니다.

② Hierarchy 창에서 GameObject | XR | AR Default Plane을 선택하여 오브젝트를 추가합니다.

③ Hierarchy 창에서 Project 창의 Prefabs 폴더로 AR Default Plan 오브젝트를 드래그하여 프리팹으로 만듭니다.

 Hierarchy 창에서  AR Default Plane  오브젝틀르 삭제합니다.

⑤  Hierarchy 창에서 XR Origin 오브젝트를 선택하고 Inspector 창에서 AR  Plane Manager 의 Plane Prefab에 AR Default Plane 프리팹을 설정합나다.

    - Detection Mode : Vertical

1.2  AR 오브젝트 추적하기 

 Hierarchy 창에서 XR Origin 오브젝트를 선택하고 Inspector 창에 Add Component 버튼을 클릭하여 AR Raycast Manager 컴포넌트를 추가합니다.

 

2. UI 만들기

2.1  기본 프레임 UI 만들기

  메인 메뉴에서  GameObject | UI | Canvas를 선택하여 Canvas를 추가하고 이름을 UI Canvas로 변경하고 Inspector 창에 Canvas Scaler의 UI Scale Mode를 Scale With Screen Size로 설정합니다. 

 

메인 메뉴에서  GameObject | UI | Panel 를 선택하여 UI Canvas  하위에 Panel를 추가하고 이름을 App Title Panel로 변경합니다. 

메인 메뉴에서   GameObject | UI | Button를 선택하여  App Title Panel 하위에 Button을 추가하고  이름을 Home Button로 변경합니다.

- Image 컴포넌트의 Source Image  : home.png을 sprite로 변경하여 설정

home.png
0.01MB

 

메인 메뉴에서  GameObject | UI | Text를 선택하여   App Title Panel 하위에 Text를 추가하고 이름을 Title Text로 변경합니다.

    - Text Input : Photo Gallery

 

  메인 메뉴에서  GameObject | Create Empty를 선택하여 UI Canvas   하위에 GameObject를 추가하고 이름을 Startup UI로 변경하고  Inspector 창에 Add Component 버튼을 클릭하여 Canvas Renderer와 Canvas Group 컴포넌트를 추가합니다. 메인 메뉴에서  GameObject | UI | Text를 선택하여  Startup UI  하위에 Text를 추가합니다.

 

  메인 메뉴에서  GameObject | Create Empty를 선택하여 UI Canvas   하위에 GameObject을 추가하고 이름을 Main UI로 변경하고  Inspector 창에 Add Component 버튼을 클릭하여 Canvas Renderer와 Canvas Group 컴포넌트를 추가합니다. 

 

메인 메뉴에서  GameObject | UI | Button를 선택하여  Main UI 하위에 Button을 추가한후 이름을 Add Button으로 변경합니다.

- Add Button 하위의 Text를 선택한 후 인스펙터 창에서 Text Input에 +를 입력합니다.

- Add Button 하위의 메인 메뉴에서  GameObject | UI | Text를 선택하여 Text를 추가한 후 인스펙터 창에서 Text Input에 Add를 입력합니다.

 

  메인 메뉴에서  GameObject | Create Empty를 선택하여 UI Canvas   하위에 GameObject을 추가하고 이름을 AddPicture UI로 변경하고  Inspector 창에 Add Component 버튼을 클릭하여 Canvas Renderer와 Canvas Group 컴포넌트를 추가합니다. 

2.2  이미지 선택 UI 만들기

 

① 메인 메뉴에서  GameObject | UI | Image 를 선택하여 UI Canvas   하위에 추가하고 이름을 SelectImage UI로 변경하고  Inspector 창에 Add Component 버튼을 클릭하여 Canvas Renderer와 Canvas Group 컴포넌트를 추가합니다. 

- Image 컴포넌트의 Color 속성값을 회색으로 변경

 

② SelectImage UI하위에 메인 메뉴에서  GameObject | UI | Panel를 선택하여 추가하고 이름을 Header로 변경합니다.

 

 - Header 하위에 메인 메뉴에서  GameObject | UI | Text를 선택하여 Text를 추가하고 이름을 Header Text로 변경합니다.       Text Input :  Select Image를 입력

 -  Header  하위에 메인 메뉴에서  GameObject | UI | Button를 선택하여 Button을 추가하고 이름을 Cancel Button로 변경합니다.

 

 

 - Header=> Cancel Button 하위에 Text를 선택한 후 인스펙터 창에서 Text Input에 X를 입력합니다.

③ SelectImage UI하위에 메인 메뉴에서  GameObject | Create Empty를 선택하여 GameObject를 추가하고 이름을 Image Buttons변경하고  Inspector 창에 Add Component 버튼을 클릭하여 Canvas Renderer 컴포넌트를 추가합니다. 

 - Image Buttons를 선택하고 인스펙터 창에 Grid Layout Group 컴포넌트 추가합니다.

 

- Image Buttons 하위에 메인 메뉴에서  GameObject | UI | Raw Image을 선택하여 Raw Image 추가하고 이름을 Image Button으로 변경하고  Inpector  창에서 raw Image의 Texture에 WinterBarm.jpg 이미지를 설정합니다.

WinterBarn.jpg
1.58MB

 

- Image Button을 선택하고 Inpector 창에서 Add component 버튼을 클릭하여 Button 컴포넌트를 추가합니다.

 

- Image Button을 프리팹으로 만든후 Image Buttons 하위에  4개 복사하여 추가합니다.

 

-하이러키 창에  Image Buttons를 선택하고 Inpector 창에서 Add component 버튼을 클릭하여 ImageButtons.cs컴포넌트를 추가합니다.

ImageButtons.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class ImageButtons : MonoBehaviour
{
    [SerializeField] GameObject imageButtonPrefab;
    [SerializeField] AddPictureMode addPicture;
    [SerializeField] ImagesData imagesData;

    void Start()
    {
        for (int i = transform.childCount - 1; i >= 0; i--)
        {
            GameObject.Destroy(transform.GetChild(i).gameObject);
        }

        foreach (ImageInfo image in imagesData.images)
        {
            GameObject obj = Instantiate(imageButtonPrefab, transform);
            RawImage rawimage = obj.GetComponent<RawImage>();
            rawimage.texture = image.texture;
            Button button = obj.GetComponent<Button>();
            button.onClick.AddListener(() => OnClick(image));
        }
    }

    void OnClick(ImageInfo image)
    {

        addPicture.imageInfo = image;
        InteractionController.EnableMode("AddPicture");
    }
}

 

 

 Hierarchy 창에서 UI Canvas를 선택하고 인스펙터 창에서 Add Component 버튼을 클릭하여 UIController.cs 스크립트를 추가합니다.

UIController.cs

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

public class UIController : MonoBehaviour
{
    [SerializeField]
    private CanvasGroup[] canvasGroups;
   
    private Dictionary<string, CanvasGroup> uiPanels = new Dictionary<string, CanvasGroup>();
    private static UIController Instance = null;
    CanvasGroup currentPanel;

    void Awake()
    {       
        uiPanels.Add("Startup", canvasGroups[0]);      
        uiPanels.Add("Main", canvasGroups[1]);
        uiPanels.Add("AddPicture", canvasGroups[2]);
        uiPanels.Add("SelectImage", canvasGroups[3]);


        if (Instance ==null )
        {
            Instance = this;
        }
        ResetAllUI();
    }

    public static void ShowUI(string name)
    {       
        Instance._ShowUI(name);
    }

    void _ShowUI(string name)
    {
        CanvasGroup panel;
        if (uiPanels.TryGetValue(name, out panel))
        {
            ChangeUI(uiPanels[name]);
        }
        else
        {
            Debug.LogError("Undefined ui panel " + name);
        }
    }

    void ResetAllUI()
    {
        foreach (CanvasGroup panel in uiPanels.Values)       
        {
            panel.gameObject.SetActive(false);
        }
    }

    void ChangeUI(CanvasGroup panel)
    {
        if (panel == currentPanel)
            return;
        if (currentPanel)
          //  FadeOut(currentPanel);
            currentPanel.gameObject.SetActive(false);
        currentPanel = panel;
        if (panel)
           // FadeIn(panel);
        panel.gameObject.SetActive(true);
    }
}

 

Hierarchy 창에서 UI Canvas를 선택하고 Inspector 창에서 UIController.cs 스크립트의 UI Panels에서 +버튼을 눌러서 Hierarchy 창의 UI Canvas  하위에 오브젝트들을 설정합니다.

3. AR 오브젝트와 상호작용하기

 Hierarchy 창에서 GameObject | Create Empty메뉴를 선택하여 빈 오브젝트를 생성하고 이름을Interaction Controller로 설정합니다. Transform은 초기화합니다.

-  Hierarchy 창에서 Interaction Controller  오브젝트 하위에 GameObject | Create Empty 메뉴를 선택하여 빈 오브젝트를 생성하고 이름을 Startup Mode로 설정합니다. Transform은 초기화합니다.  Inspector 창에 Add Component 버튼을 클릭하여 StartupMode.cs 컴포넌트를 추가합니다. 

StartupMode.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.XR.ARFoundation;

public class StartupMode : MonoBehaviour
{
    [SerializeField] ARPlaneManager planeManager;
    void OnEnable()
    {
        UIController.ShowUI("Startup");
    }

    void Update()
    {
        if (ARSession.state == ARSessionState.Unsupported)
        {
          
            InteractionController.EnableMode("Startup");
        }
        else if (ARSession.state >= ARSessionState.Ready)
        {
           

            if (planeManager.trackables.count > 0)
            {
                InteractionController.EnableMode("Main");
            }
        }
    }
}

 

 

- Hierarchy 창에서 Interaction Controller  오브젝트 하위에 GameObject | Create Empty 메뉴를 선택하여 빈 오브젝트를 생성하고 이름을 MainMode로 설정합니다. Transform은 초기화합니다.  Inspector 창에 Add Component 버튼을 클릭하여 MainMode.cs 컴포넌트를 추가합니다. 

MainMode.cs

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

public class MainMode : MonoBehaviour
{
    void OnEnable()
    {
        UIController.ShowUI("Main");
    }

    // todo RAdd Main mode interactions

}

 

- Hierarchy 창에서 Interaction Controller  오브젝트 하위에 GameObject | Create Empty 메뉴를 선택하여 빈 오브젝트를 생성하고 이름을 AddPictureMode로 설정합니다. Transform은 초기화합니다.  Inspector 창에 Add Component 버튼을 클릭하여 AddPictureMode.cs 컴포넌트를 추가합니다. 

 

AddPictureMode.cs

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.InputSystem;
using UnityEngine.XR.ARFoundation;
using UnityEngine.XR.ARSubsystems;

public class AddPictureMode : MonoBehaviour
{
    [SerializeField] ARRaycastManager raycaster;
    [SerializeField] GameObject placedPrefab;
    public ImageInfo imageInfo;
    [SerializeField] float defaultScale = 0.5f;

    List<ARRaycastHit> hits = new List<ARRaycastHit>();

    void OnEnable()
    {
        UIController.ShowUI("AddPicture");
    }

    public void OnPlaceObject(InputValue value)
    {
        Vector2 touchPosition = value.Get<Vector2>();        
        PlaceObject(touchPosition);
    }

    void PlaceObject(Vector2 touchPosition)
    {
        if (raycaster.Raycast(touchPosition, hits, TrackableType.PlaneWithinPolygon))
        {
            ARRaycastHit hit = hits[0];

            Vector3 position = hit.pose.position;
            Vector3 normal = -hit.pose.up;
            Quaternion rotation = Quaternion.LookRotation(normal, Vector3.up);

            GameObject spawned = Instantiate(placedPrefab, position, rotation);

            FramedPhoto picture = spawned.GetComponent<FramedPhoto>();
            picture.SetImage(imageInfo);

            spawned.transform.localScale = new Vector3(defaultScale, defaultScale, 1.0f);

            spawned.transform.SetParent(transform.parent);

            InteractionController.EnableMode("Main");
        }
    }

}

 

 

 

 

 

AddPictureMode.cs의 Places Prefab 속성값의 프리팹만들기

  • Hierarchy 창에서 메인메뉴 GameObject | Create Empty 메뉴를 선택하여 빈 오브젝트를 생성하고 이름을 FramedPhoto Prefab로 설정합니다. Transform은 초기화합니다.
  • FramedPhoto Prefab 하위에  메인메뉴 GameObject | Create Empty 메뉴를 선택하여   빈 오브젝트를 생성하고 이름을AspectScaler로 설정합니다. Transform은 초기화하고 Scale(1, 0.75,1)로 설정합니다.
  • AspectScaler  하위에  메인메뉴 GameObject | 3D Object |Cube 메뉴를 선택하여   Cube를 추가하고 이름을 Frame로 설정합니다. Transform은 초기화하고 Poistion(0,0, -0.025), Scale(1, 1,0.05)로 설정합니다. Black 재질을 설정합니다.
  • AspectScaler  하위에  메인메뉴 GameObject | 3D Object | Quad 메뉴를 선택하여   Quad  를 추가하고 이름을 Image로 설정합니다. Transform은 초기화하고 Poistion(0,0, -0.06), Scale(0.9, 0.9,1)로 설정합니다. 액자의 이미지( WinterBarn.jpg)재질을 설정합니다.
  • FramedPhoto Prefab를 선택한 후  Inspector 창에 Add Component 버튼을 클릭하여 FramedPhoto .cs 컴포넌트를 추가합니다. 

FramedPhoto.cs

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

public class FramedPhoto : MonoBehaviour
{
    [SerializeField] Transform scalerObject;
    [SerializeField] GameObject imageObject;

    ImageInfo imageInfo;

    public void SetImage(ImageInfo image)
    {
        imageInfo = image;

        Renderer renderer = imageObject.GetComponent<Renderer>();
        Material material = renderer.material;
        material.SetTexture("_MainTex", imageInfo.texture);

        AdjustScale();
    }

    public void AdjustScale()
    {
        Vector2 scale = ImagesData.AspectRatio(imageInfo.width, imageInfo.height);
        scalerObject.localScale = new Vector3(scale.x, scale.y, 1f);
    }
}
  • Hierarchy 창에서  FramedPhoto Prefab를 프리팹으로 만들고, 삭제합니다.

 

- AddPictureMode   오브젝트 선택하고 Inspector 창에 Add Component 버튼을 클릭하여 ShowTrackablesOnEnable.cs 컴포넌트를 추가합니다. 

 

ShowTrackablesOnEnable.cs

using System.Collections;
using System.Collections.Generic;
using Unity.XR.CoreUtils;
using UnityEngine;
using UnityEngine.XR.ARFoundation;

public class ShowTrackablesOnEnable : MonoBehaviour
{
    [SerializeField] XROrigin sessionOrigin;
    ARPlaneManager planeManager;
    ARPointCloudManager cloudManager;
    bool isStarted;

    void Awake()
    {
        planeManager = sessionOrigin.GetComponent<ARPlaneManager>();
        cloudManager = sessionOrigin.GetComponent<ARPointCloudManager>();
    }

     void Start()
    {
        isStarted = true;
    }

    void OnEnable()
    {
        ShowTrackables(true);
    }

    void OnDisable()
    {
        if (isStarted)
        {
            ShowTrackables(false);
        }
    }


    void ShowTrackables(bool show)
    {
        if (cloudManager)
        {
            cloudManager.SetTrackablesActive(show);
            cloudManager.enabled = show;
        }
        if (planeManager)
        {
            planeManager.SetTrackablesActive(show);
            planeManager.enabled = show;
        }
    }
}

 

 

- Hierarchy 창에서 Interaction Controller  오브젝트 하위에 GameObject | Create Empty 메뉴를 선택하여 빈 오브젝트를 생성하고 이름을 SelectImageMode로 설정합니다. Transform은 초기화합니다.  Inspector 창에 Add Component 버튼을 클릭하여 SelectImageMode.cs 컴포넌트를 추가합니다. 

 

SelectImageMode.cs

using UnityEngine;

public class SelectImageMode : MonoBehaviour
{
    void OnEnable()
    {
        UIController.ShowUI("SelectImage");
    }
}

 

- Hierarchy 창에서 Interaction Controller  오브젝트를 선택하고 Inspector 창에 Add Component 버튼을 클릭하여 InteractionController.cs 컴포넌트를 추가합니다. 

 

InteractionController.cs

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

public class InteractionController : MonoBehaviour
{
  
    [SerializeField]
    private GameObject[] gameObjects;
    private Dictionary<string, GameObject> interactionModes = new Dictionary<string, GameObject>();
    GameObject currentMode;
    private static InteractionController Instance = null;
   
    void Awake()
    {
       
         interactionModes.Add("Startup", gameObjects[0]);            
        interactionModes.Add("Main", gameObjects[1]);
        interactionModes.Add("AddPicture", gameObjects[2]);
        interactionModes.Add("SelectImage", gameObjects[3]);
        if (null == Instance)
        {
            Instance = this;
        }
            ResetAllModes();
    }

    void Start()
    {
       
        _EnableMode("Startup");
    }

    public static void EnableMode(string name)
    {
        Instance._EnableMode(name);
    }

    void _EnableMode(string name)
    {
        GameObject modeObject;
        if (interactionModes.TryGetValue(name, out modeObject))
        {
            StartCoroutine(ChangeMode(modeObject));
        }
        else
        {
            Debug.LogError("undefined mode named " + name);
        }
    }

    void ResetAllModes()
    {
        foreach (GameObject mode in interactionModes.Values)
        {
            mode.SetActive(false);
        }
    }

    IEnumerator ChangeMode(GameObject mode)
    {
        if (mode == currentMode)
            yield break;

        if (currentMode)
        {
            currentMode.SetActive(false);
            yield return null;
        }

        currentMode = mode;
        mode.SetActive(true);
    }

}

 

 

② Hierarchy 창에서 GameObject | Create Empty 메뉴를 선택하여 빈 오브젝트를 생성하고 이름을 Images Data로 설정합니다. Transform은 초기화합니다.  Inspector 창에 Add Component 버튼을 클릭하여 ImagesData.cs 컴포넌트를 추가합니다. 

 

ImagesData.cs

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

[System.Serializable]
public struct ImageInfo
{
    public Texture texture;
    public int width;
    public int height;
}
public class ImagesData : MonoBehaviour
{
    public ImageInfo[] images;

    public static Vector2 AspectRatio(float width, float height)
    {
        Vector2 scale = Vector2.one;

        if (width == 0 || height == 0) 
            return scale;

        if (width > height)
        {
            scale.x = 1f;
            scale.y = height / width;
        }
        else
        {
            scale.x = width / height;
            scale.y = 1f;
        }
        return scale;
    }

}

-Hierarchy 창에서  Images Data  오브젝트를 선택하고  Images 항목에 +버튼을 클릭하여 항목을 추가합니다.

photo.unitypackage
6.62MB

 

 -Hierarchy 창에서 UI Canvas=> SelectImageUI=>Image Buttons 를 선택하고  Inspector 창에 Add Component 버튼을 클릭하여 ImageButtons.cs 컴포넌트를 추가합니다. 

ImageButtons.cs

using UnityEngine;
using UnityEngine.UI;

public class ImageButtons : MonoBehaviour
{
    [SerializeField] GameObject imageButtonPrefab;
    [SerializeField] AddPictureMode addPicture;
    [SerializeField] ImagesData imagesData;

    void Start()
    {
        for (int i = transform.childCount - 1; i >= 0; i--)
        {
            GameObject.Destroy(transform.GetChild(i).gameObject);
        }

        foreach (ImageInfo image in imagesData.images)
        {
            GameObject obj = Instantiate(imageButtonPrefab, transform);
            RawImage rawimage = obj.GetComponent<RawImage>();
            rawimage.texture = image.texture;
            Button button = obj.GetComponent<Button>();
            button.onClick.AddListener(() => OnClick(image));
        }
    }

    void OnClick(ImageInfo image)
    {
       
        addPicture.imageInfo = image;
        InteractionController.EnableMode("AddPicture");
    }
}

 

- MainUI->AddButton을 선택하고 인스펙터 창에 Onclick 이벤트에 하이러키 창에 InteractionController를 설정하고 함수는 _EnaableMode를 설정하고 매개변수에 SelectImage를 설정합니다.

 

실행하기

 

[입력이 안되는 경우]

- Hierarchy 창에서 Interaction Controller  오브젝트를 선택하고  Inspector 창에 Add Component 버튼을 클릭하여 Player Input 컴포넌트를 추가하고 Behavior 속성값에 Broadcast Messages로 설정합니다.

input.unitypackage
0.00MB