09. AR 갤러리 만들기
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로 변경하여 설정
메인 메뉴에서 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 이미지를 설정합니다.
- 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 항목에 +버튼을 클릭하여 항목을 추가합니다.
-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로 설정합니다.