๐ท Cloud Anchor
- local anchor๋ฅผ cloud anchor๋ก ์ฌ๋ฆฌ๊ธฐ
๐ถ ๋งตํ ํ์ง ๋ง์กฑํ๋ฉด cloud anchor ์์ฑ
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.XR.ARFoundation;
using UnityEngine.XR.ARSubsystems;
// Google ARCore Extensions
using Google.XR.ARCoreExtensions;
using System;
public class CloudAnchorManager : MonoBehaviour
{
// ์ํ ๋ณ์
public enum Mode { READY, HOST, HOST_PENDING, RESOLVE, RESOLVE_PENDING };
// ๋ฒํผ
public Button hostButton; // ํด๋ผ์ฐ๋ ์ต์ปค ๋ฑ๋ก
public Button resolveButton; // ํด๋ผ์ฐ๋ ์ต์ปค ์กฐํ
public Button resetButton; // ๋ฆฌ์
// ๋ฉ์์ง ์ถ๋ ฅ ํ
์คํธ
public Text messageText;
// ์ํ๋ณ์
public Mode mode = Mode.READY;
// AnchorManager // ๋ก์ปฌ ์ต์ปค๋ฅผ ์์ฑํ๊ธฐ ์ํ ํด๋์ค
public ARAnchorManager anchorManager;
// ArRaycastManager
public ARRaycastManager raycastManager;
// ์ฆ๊ฐ์ํฌ ๊ฐ์ฒด ํ๋ฆฌํน
public GameObject anchorPrefab;
// ์ ์ฅ ๊ฐ์ฒด ๋ณ์ (์ญ์ ํ๊ธฐ ์ํ ์ฉ๋)
private GameObject anchorGameObject;
// ๋ก์ปฌ์ต์ปค ์ ์ฅ ๋ณ์
private ARAnchor localAnchor;
// ํด๋ผ์ฐ๋ ์ต์ปค ๋ณ์
private ARCloudAnchor cloudAnchor;
// Raycast Hit
private List<ARRaycastHit> hits = new List<ARRaycastHit>();
// ํด๋ผ์ฐ๋ ์ต์ปค ID ์ ์ฅํ๊ธฐ ์ํ ํค ๊ฐ (PlayerPregs.SetString("ํค", ๊ฐ));
private const string cloudAnchorKey = "CLOUD_ANCHOR_ID";
// ํด๋ผ์ฐ๋ ์ต์ปค ID
private string strCloudAnchorId;
void Start()
{
// ๋ฒํผ ์ด๋ฒคํธ ์ฐ๊ฒฐ
hostButton.onClick.AddListener(() => OnHostClick());
resolveButton.onClick.AddListener(() => OnResolveClick());
resetButton.onClick.AddListener(() => OnResetClick());
}
void Update()
{
if (mode == Mode.HOST)
{
Hosting();
HostProcessing();
}
}
void Hosting()
{
if (Input.touchCount < 1) return;
Touch touch = Input.GetTouch(0);
if (touch.phase != TouchPhase.Began) return;
// ๋ก์ปฌ ์ต์ปค๊ฐ ์กด์ฌํ๋์ง ์ฌ๋ถ๋ฅผ ํ์ธ
if (localAnchor == null)
{
// Raycast ๋ฐ์ฌ
if (raycastManager.Raycast(touch.position, hits, TrackableType.PlaneWithinPolygon))
{
// ๋ก์ปฌ ์ต์ปค๋ฅผ ์์ฑ
localAnchor = anchorManager.AddAnchor(hits[0].pose);
// ๋ก์ปฌ ์ต์ปค ์์น์ ์ฌ์ด ์ฆ๊ฐ์ํค๊ณ ๋ณ์์ ์ ์ฅ
anchorGameObject = Instantiate(anchorPrefab, localAnchor.transform);
}
}
}
// ํด๋ผ์ฐ๋ ์ต์ปค ๋ฑ๋ก
void HostProcessing()
{
if (localAnchor == null) return;
// ํผ์ณํฌ์ธํธ์ ๊ฐฏ์ ๋ฐ ํ๋ฆฌํฐ ์ธก์
FeatureMapQuality quality = anchorManager.EstimateFeatureMapQualityForHosting(GetCameraPose());
string mappingText = string.Format("๋งตํ ํ์ง = {0}", quality);
// ๋งตํ ํ๋ฆฌํฐ๊ฐ 1 ์ด์์ผ ๋ ํธ์คํ
์์ฒญ
if (quality == FeatureMapQuality.Sufficient || quality == FeatureMapQuality.Good)
{
// 1์ผ์ง๋ฆฌ ์ต์ปคํฌ์ธํธ
cloudAnchor = anchorManager.HostCloudAnchor(localAnchor, 1);
if (cloudAnchor == null)
{
mappingText = "ํด๋ผ์ฐ๋ ์ต์ปค ์์ฑ ์คํจ";
}
else
{
// ์ฌ๊ธฐ์ ํด๋ผ์ฐ๋ ์ต์ปค ID ์ฐ์ด๋ ์๋์ด
// ์๋ฒ์์ ์์
ํ๋ ์๊ฐ์ด ์๊ธฐ ๋๋ฌธ์
mappingText = "ํด๋ผ์ฐ๋ ์ต์ปค ์์ฑ ์์";
mode = Mode.HOST_PENDING;
}
}
messageText.text = mappingText;
}
// MainCamera ํ๊ทธ๋ก ์ง์ ๋ ์นด๋ฉ๋ผ์ ์์น์ ๊ฐ๋๋ฅผ Pose ๋ฐ์ดํฐ ํ์
์ผ๋ก ๋ฐํ
public Pose GetCameraPose()
{
return new Pose(Camera.main.transform.position, Camera.main.transform.rotation);
}
private void OnHostClick()
{
mode = Mode.HOST;
}
private void OnResolveClick()
{
mode = Mode.RESOLVE;
}
private void OnResetClick()
{
}
}
-

- ์ฐ๊ฒฐ
- Build & Run
- Host ๋ฒํผ ํด๋ฆญ > ์ฌ์ด ์์ฑ
- Text ๋ณํจ : ๋งตํ ํ์ง > Cloud Anchor ์์ฑ ์์
๐ถ cloud anchor ์์ฑ
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.XR.ARFoundation;
using UnityEngine.XR.ARSubsystems;
// Google ARCore Extensions
using Google.XR.ARCoreExtensions;
using System;
public class CloudAnchorManager : MonoBehaviour
{
// ์ํ ๋ณ์
public enum Mode { READY, HOST, HOST_PENDING, RESOLVE, RESOLVE_PENDING };
// ๋ฒํผ
public Button hostButton; // ํด๋ผ์ฐ๋ ์ต์ปค ๋ฑ๋ก
public Button resolveButton; // ํด๋ผ์ฐ๋ ์ต์ปค ์กฐํ
public Button resetButton; // ๋ฆฌ์
// ๋ฉ์์ง ์ถ๋ ฅ ํ
์คํธ
public Text messageText;
// ์ํ๋ณ์
public Mode mode = Mode.READY;
// AnchorManager // ๋ก์ปฌ ์ต์ปค๋ฅผ ์์ฑํ๊ธฐ ์ํ ํด๋์ค
public ARAnchorManager anchorManager;
// ArRaycastManager
public ARRaycastManager raycastManager;
// ์ฆ๊ฐ์ํฌ ๊ฐ์ฒด ํ๋ฆฌํน
public GameObject anchorPrefab;
// ์ ์ฅ ๊ฐ์ฒด ๋ณ์ (์ญ์ ํ๊ธฐ ์ํ ์ฉ๋)
private GameObject anchorGameObject;
// ๋ก์ปฌ์ต์ปค ์ ์ฅ ๋ณ์
private ARAnchor localAnchor;
// ํด๋ผ์ฐ๋ ์ต์ปค ๋ณ์
private ARCloudAnchor cloudAnchor;
// Raycast Hit
private List<ARRaycastHit> hits = new List<ARRaycastHit>();
// ํด๋ผ์ฐ๋ ์ต์ปค ID ์ ์ฅํ๊ธฐ ์ํ ํค ๊ฐ (PlayerPregs.SetString("ํค", ๊ฐ));
private const string cloudAnchorKey = "CLOUD_ANCHOR_ID";
// ํด๋ผ์ฐ๋ ์ต์ปค ID
private string strCloudAnchorId;
void Start()
{
// ๋ฒํผ ์ด๋ฒคํธ ์ฐ๊ฒฐ
hostButton.onClick.AddListener(() => OnHostClick());
resolveButton.onClick.AddListener(() => OnResolveClick());
resetButton.onClick.AddListener(() => OnResetClick());
strCloudAnchorId = PlayerPrefs.GetString(cloudAnchorKey, "");
}
void Update()
{
if (mode == Mode.HOST)
{
Hosting();
HostProcessing();
}
if (mode == Mode.HOST_PENDING)
{
HostPending();
}
}
void Hosting()
{
if (Input.touchCount < 1) return;
Touch touch = Input.GetTouch(0);
if (touch.phase != TouchPhase.Began) return;
// ๋ก์ปฌ ์ต์ปค๊ฐ ์กด์ฌํ๋์ง ์ฌ๋ถ๋ฅผ ํ์ธ
if (localAnchor == null)
{
// Raycast ๋ฐ์ฌ
if (raycastManager.Raycast(touch.position, hits, TrackableType.PlaneWithinPolygon))
{
// ๋ก์ปฌ ์ต์ปค๋ฅผ ์์ฑ
localAnchor = anchorManager.AddAnchor(hits[0].pose);
// ๋ก์ปฌ ์ต์ปค ์์น์ ์ฌ์ด ์ฆ๊ฐ์ํค๊ณ ๋ณ์์ ์ ์ฅ
anchorGameObject = Instantiate(anchorPrefab, localAnchor.transform);
}
}
}
// ํด๋ผ์ฐ๋ ์ต์ปค ๋ฑ๋ก
void HostProcessing()
{
if (localAnchor == null) return;
// ํผ์ณํฌ์ธํธ์ ๊ฐฏ์ ๋ฐ ํ๋ฆฌํฐ ์ธก์
FeatureMapQuality quality = anchorManager.EstimateFeatureMapQualityForHosting(GetCameraPose());
string mappingText = string.Format("๋งตํ ํ์ง = {0}", quality);
// ๋งตํ ํ๋ฆฌํฐ๊ฐ 1 ์ด์์ผ ๋ ํธ์คํ
์์ฒญ
if (quality == FeatureMapQuality.Sufficient || quality == FeatureMapQuality.Good)
{
// 1์ผ์ง๋ฆฌ ์ต์ปคํฌ์ธํธ
cloudAnchor = anchorManager.HostCloudAnchor(localAnchor, 1);
if (cloudAnchor == null)
{
mappingText = "ํด๋ผ์ฐ๋ ์ต์ปค ์์ฑ ์คํจ";
}
else
{
// ์ฌ๊ธฐ์ ํด๋ผ์ฐ๋ ์ต์ปค ID ์ฐ์ด๋ ์๋์ด
// ์๋ฒ์์ ์์
ํ๋ ์๊ฐ์ด ์๊ธฐ ๋๋ฌธ์
mappingText = "ํด๋ผ์ฐ๋ ์ต์ปค ์์ฑ ์์";
mode = Mode.HOST_PENDING;
}
}
messageText.text = mappingText;
}
void HostPending()
{
string mappingText = "";
if (cloudAnchor.cloudAnchorState == CloudAnchorState.Success)
{
mappingText = $"ํด๋ผ์ฐ๋ ์ต์ปค ์์ฑ ์ฑ๊ณต, CloudAnchor ID = {cloudAnchor.cloudAnchorId}";
// ์ต์ปคID ์ ์ฅ
strCloudAnchorId = cloudAnchor.cloudAnchorId;
// ํด๋ผ์ฐ๋ ์ต์ปค ID๋ฅผ ๊ณต์ ํ๋ ๋ก์ง
// ํฌํค ๋คํธ์ํฌ : RPC ํธ์ถ
// ํ์ด์ด๋ฒ ์ด์ค : ๋ฐ์ดํฐ ์ ์ฅ
PlayerPrefs.SetString(cloudAnchorKey, strCloudAnchorId);
// ์ด๊ธฐํ
cloudAnchor = null;
// ๊ธฐ์กด์ ์ฆ๊ฐ๋ ๊ฐ์ฒด๋ฅผ ์ญ์
Destroy(anchorGameObject);
mode = Mode.READY;
}
else
{
mappingText = $"ํด๋ผ์ฐ๋ ์ต์ปค ์์ฑ ์งํ์ค...{cloudAnchor.cloudAnchorState}";
}
messageText.text = mappingText;
}
// MainCamera ํ๊ทธ๋ก ์ง์ ๋ ์นด๋ฉ๋ผ์ ์์น์ ๊ฐ๋๋ฅผ Pose ๋ฐ์ดํฐ ํ์
์ผ๋ก ๋ฐํ
public Pose GetCameraPose()
{
return new Pose(Camera.main.transform.position, Camera.main.transform.rotation);
}
private void OnHostClick()
{
mode = Mode.HOST;
}
private void OnResolveClick()
{
mode = Mode.RESOLVE;
}
private void OnResetClick()
{
}
}
-
- Build & Run
- Host ๋ฒํผ ํด๋ฆญ > ์ฌ์ด ์์ฑ
- ํด๋ผ์ฐ๋ ์ต์ปค ์์ฑ ์ฑ๊ณตํ๋ฉด ์ฌ์ด ์ญ์ ๋จ

- ์์ฒญ ์ ์ฌ๋ผ๊ฐ ๊ฒ ํ์ธ
๐ถ Resolve
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.XR.ARFoundation;
using UnityEngine.XR.ARSubsystems;
// Google ARCore Extensions
using Google.XR.ARCoreExtensions;
using System;
public class CloudAnchorManager : MonoBehaviour
{
// ์ํ ๋ณ์
public enum Mode { READY, HOST, HOST_PENDING, RESOLVE, RESOLVE_PENDING };
// ๋ฒํผ
public Button hostButton; // ํด๋ผ์ฐ๋ ์ต์ปค ๋ฑ๋ก
public Button resolveButton; // ํด๋ผ์ฐ๋ ์ต์ปค ์กฐํ
public Button resetButton; // ๋ฆฌ์
// ๋ฉ์์ง ์ถ๋ ฅ ํ
์คํธ
public Text messageText;
// ์ํ๋ณ์
public Mode mode = Mode.READY;
// AnchorManager // ๋ก์ปฌ ์ต์ปค๋ฅผ ์์ฑํ๊ธฐ ์ํ ํด๋์ค
public ARAnchorManager anchorManager;
// ArRaycastManager
public ARRaycastManager raycastManager;
// ์ฆ๊ฐ์ํฌ ๊ฐ์ฒด ํ๋ฆฌํน
public GameObject anchorPrefab;
// ์ ์ฅ ๊ฐ์ฒด ๋ณ์ (์ญ์ ํ๊ธฐ ์ํ ์ฉ๋)
private GameObject anchorGameObject;
// ๋ก์ปฌ์ต์ปค ์ ์ฅ ๋ณ์
private ARAnchor localAnchor;
// ํด๋ผ์ฐ๋ ์ต์ปค ๋ณ์
private ARCloudAnchor cloudAnchor;
// Raycast Hit
private List<ARRaycastHit> hits = new List<ARRaycastHit>();
// ํด๋ผ์ฐ๋ ์ต์ปค ID ์ ์ฅํ๊ธฐ ์ํ ํค ๊ฐ (PlayerPregs.SetString("ํค", ๊ฐ));
private const string cloudAnchorKey = "CLOUD_ANCHOR_ID";
// ํด๋ผ์ฐ๋ ์ต์ปค ID
private string strCloudAnchorId;
void Start()
{
// ๋ฒํผ ์ด๋ฒคํธ ์ฐ๊ฒฐ
hostButton.onClick.AddListener(() => OnHostClick());
resolveButton.onClick.AddListener(() => OnResolveClick());
resetButton.onClick.AddListener(() => OnResetClick());
strCloudAnchorId = PlayerPrefs.GetString(cloudAnchorKey, "");
}
void Update()
{
if (mode == Mode.HOST)
{
Hosting();
HostProcessing();
}
if (mode == Mode.HOST_PENDING)
{
HostPending();
}
if (mode == Mode.RESOLVE)
{
Resolving();
}
if (mode == Mode.RESOLVE_PENDING)
{
ResolvePending();
}
}
void Hosting()
{
if (Input.touchCount < 1) return;
Touch touch = Input.GetTouch(0);
if (touch.phase != TouchPhase.Began) return;
// ๋ก์ปฌ ์ต์ปค๊ฐ ์กด์ฌํ๋์ง ์ฌ๋ถ๋ฅผ ํ์ธ
if (localAnchor == null)
{
// Raycast ๋ฐ์ฌ
if (raycastManager.Raycast(touch.position, hits, TrackableType.PlaneWithinPolygon))
{
// ๋ก์ปฌ ์ต์ปค๋ฅผ ์์ฑ
localAnchor = anchorManager.AddAnchor(hits[0].pose);
// ๋ก์ปฌ ์ต์ปค ์์น์ ์ฌ์ด ์ฆ๊ฐ์ํค๊ณ ๋ณ์์ ์ ์ฅ
anchorGameObject = Instantiate(anchorPrefab, localAnchor.transform);
}
}
}
// ํด๋ผ์ฐ๋ ์ต์ปค ๋ฑ๋ก
void HostProcessing()
{
if (localAnchor == null) return;
// ํผ์ณํฌ์ธํธ์ ๊ฐฏ์ ๋ฐ ํ๋ฆฌํฐ ์ธก์
FeatureMapQuality quality = anchorManager.EstimateFeatureMapQualityForHosting(GetCameraPose());
string mappingText = string.Format("๋งตํ ํ์ง = {0}", quality);
// ๋งตํ ํ๋ฆฌํฐ๊ฐ 1 ์ด์์ผ ๋ ํธ์คํ
์์ฒญ
if (quality == FeatureMapQuality.Sufficient || quality == FeatureMapQuality.Good)
{
// 1์ผ์ง๋ฆฌ ์ต์ปคํฌ์ธํธ
cloudAnchor = anchorManager.HostCloudAnchor(localAnchor, 1);
if (cloudAnchor == null)
{
mappingText = "ํด๋ผ์ฐ๋ ์ต์ปค ์์ฑ ์คํจ";
}
else
{
// ์ฌ๊ธฐ์ ํด๋ผ์ฐ๋ ์ต์ปค ID ์ฐ์ด๋ ์๋์ด
// ์๋ฒ์์ ์์
ํ๋ ์๊ฐ์ด ์๊ธฐ ๋๋ฌธ์
mappingText = "ํด๋ผ์ฐ๋ ์ต์ปค ์์ฑ ์์";
mode = Mode.HOST_PENDING;
}
}
messageText.text = mappingText;
}
void HostPending()
{
string mappingText = "";
if (cloudAnchor.cloudAnchorState == CloudAnchorState.Success)
{
mappingText = $"ํด๋ผ์ฐ๋ ์ต์ปค ์์ฑ ์ฑ๊ณต, CloudAnchor ID = {cloudAnchor.cloudAnchorId}";
// ์ต์ปคID ์ ์ฅ
strCloudAnchorId = cloudAnchor.cloudAnchorId;
// ํด๋ผ์ฐ๋ ์ต์ปค ID๋ฅผ ๊ณต์ ํ๋ ๋ก์ง
// ํฌํค ๋คํธ์ํฌ : RPC ํธ์ถ
// ํ์ด์ด๋ฒ ์ด์ค : ๋ฐ์ดํฐ ์ ์ฅ
PlayerPrefs.SetString(cloudAnchorKey, strCloudAnchorId);
// ์ด๊ธฐํ
cloudAnchor = null;
// ๊ธฐ์กด์ ์ฆ๊ฐ๋ ๊ฐ์ฒด๋ฅผ ์ญ์
Destroy(anchorGameObject);
mode = Mode.READY;
}
else
{
mappingText = $"ํด๋ผ์ฐ๋ ์ต์ปค ์์ฑ ์งํ์ค...{cloudAnchor.cloudAnchorState}";
}
messageText.text = mappingText;
}
void Resolving()
{
if (PlayerPrefs.HasKey(cloudAnchorKey) == false) return;
messageText.text = "";
// ํด๋ผ์ฐ๋ ์ต์ปค ID ๋ฐ์์ด. (ํฌํค, ํ์ด์ด๋ฒ ์ด์ค)
strCloudAnchorId = PlayerPrefs.GetString(cloudAnchorKey);
// ํด๋ผ์ฐ๋ ์ต์ปค ID๋ก CloudAnchor ๋ก๋
cloudAnchor = anchorManager.ResolveCloudAnchorId(strCloudAnchorId);
if (cloudAnchor == null)
{
messageText.text = "ํด๋ผ์ฐ๋ ์ต์ปค ๋ฆฌ์กธ๋ธ ์คํจ";
}
else
{
messageText.text = $"ํด๋ผ์ฐ๋ ์ต์ปค ๋ฆฌ์กธ๋ธ ์ฑ๊ณต : {cloudAnchor.cloudAnchorId}";
// id๋ง ๊ฐ์ ธ์จ ๊ฒ
// ํผ์ณํฌ์ธํธ๋ฅผ ํ์์ผ๋ก ๊ฐ์ ธ์ด
mode = Mode.RESOLVE_PENDING;
}
}
void ResolvePending()
{
if (cloudAnchor.cloudAnchorState == CloudAnchorState.Success)
{
messageText.text = "๋ฆฌ์กธ๋ธ ์ฑ๊ณต";
// ๊ฐ์ฒด ์ฆ๊ฐ
anchorGameObject = Instantiate(anchorPrefab, cloudAnchor.transform);
mode = Mode.READY;
}
else
{
messageText.text = $"๋ฆฌ์กธ๋น ์งํ ์ค...{cloudAnchor.cloudAnchorState}";
}
}
// MainCamera ํ๊ทธ๋ก ์ง์ ๋ ์นด๋ฉ๋ผ์ ์์น์ ๊ฐ๋๋ฅผ Pose ๋ฐ์ดํฐ ํ์
์ผ๋ก ๋ฐํ
public Pose GetCameraPose()
{
return new Pose(Camera.main.transform.position, Camera.main.transform.rotation);
}
private void OnHostClick()
{
mode = Mode.HOST;
}
private void OnResolveClick()
{
mode = Mode.RESOLVE;
}
private void OnResetClick()
{
if (anchorGameObject != null)
{
Destroy(anchorGameObject);
}
cloudAnchor = null;
localAnchor = null;
messageText.text = "์ค๋น์๋ฃ";
mode = Mode.READY;
}
}
-
- Build & Run
- ํด๋ผ์ฐ๋ ์ต์ปค ์์ฑ
- Resolve ๋ฒํผ ํด๋ฆญ : ํด๋ผ์ฐ๋์์ ์ต์ปค๋ฅผ ๊ฐ์ ธ์์ ์ฌ์ด์ ์ฆ๊ฐ์ํด
- Reset ๋ฒํผ : ๋ค์ hostํ ์ ์๋ ์ํ๋ก reset
'Unity > AR' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
11. AR Foundation - Cloud Anchor (2) Local Anchor (0) | 2021.09.21 |
---|---|
10. AR Foundation - Cloud Anchor (1) Setting (0) | 2021.09.20 |
09. AR Foundation - Occlusion culling (0) | 2021.09.18 |
08. AR Foundation - ์ผ๊ตด ์ธ์ (0) | 2021.09.17 |
07. AR Foundation - ์ค์น (2) | 2021.09.16 |