
์๋ ํ์ธ์. ์ค๋์ Unity AR ํ๊ฒฝ์์ ๋ด๋น๊ฒ์ด์ ์์คํ ์ ๊ฐ๋จํ๊ฒ ๊ตฌ์ถํด ๋ณด๋ ๊ธ์ ์ฐ๊ฒ ์ต๋๋ค. ํ์ค์ธ๊ณ์ ์นด๋ฉ๋ผ๋ฅผ ๊ฐ์ง๊ณ ์๋ ํ๋ ์ด์ด์๊ฒ Target์ ์ง์ ํ์ฌ Target๊น์ง์ ๊ฒฝ๋ก๋ฅผ ์๊ฐ์ ์ผ๋ก ํ์ํ๊ฒ ๋ฉ๋๋ค.
https://www.youtube.com/shorts/fxetmnKnh_M
AR Navigation ์์คํ ๊ตฌ์ถ
๊ฐ ๋ณ์์ ํจ์๋ค์ ๋๋ ๊ฐ๋จํ ์ค๋ช ํ ๋ค ์ ์ฒด์ ์ธ ์ฝ๋๊น์ง ์ฌ๋ฆฌ๋ ๋ฐฉ์์ผ๋ก ๊ธ์ ์งํํ๊ฒ ์ต๋๋ค.

๋จผ์ , ํ์ํ ์ค๋ธ์ ํธ๋ค ์ ๋๋ค. AR ํ๊ฒฝ์ ์ํด AR Session Origin๊ณผ AR Session์ ๋ง๋ค์ด ์ค๋๋ค.
Line ์ค๋ธ์ ํธ๋ Line Renderer๋ฅผ ๋ด๊ณ ์๋ ์ค๋ธ์ ํธ๋ก ์ฐ๋ฆฌ์๊ฒ ์๊ฐ์ ์ผ๋ก ๊ฒฝ๋ก๋ฅผ ํ์ํ๊ธฐ ์ํด Line์ ๊ทธ๋ ค์ค๋๋ค.
Collectable Spawner ์ค๋ธ์ ํธ๋ ๋น ์ค๋ธ์ ํธ๋ก, ๋ด๋น๊ฒ์ด์ ์์คํ ์ ๊ด๋ฆฌํ๋ CollectableSpawn.cs ์ปดํฌ๋ํธ๋ฅผ ๋ด๊ณ ์์ต๋๋ค.
Plane 3D ์ค๋ธ์ ํธ๋ฅผ ์์ฑํ์ฌ ๋ด๋น๊ฒ์ด์ ์์คํ ์ด ์งํ์ ๋ฐ๋ผ ๊ฒฝ๋ก๋ฅผ ๊ณ์ฐํ๋๋ก ๋ง๋ค์ด ์ค๋๋ค.

์ด์ด์, Window - AI - Navigation์ ์ ํํด์ค๋๋ค.

๋ง๋ค์๋ Plane ์ค๋ธ์ ํธ๋ฅผ Static ์ฒดํฌํด์ค๋๋ค.

์ด์ Navigation - Bake - Bake๋ฅผ ํด๋ฆญํ์ฌ NavMesh๋ฅผ ์์ฑํด ์ค๋๋ค. ์์ฑํด ์ฃผ์ง ์์ผ๋ฉด ์ด๋ ๊ฒฝ๋ก๋ฅผ ๊ณ์ฐํ ์ ์์ด ์ค๋ฅ๊ฐ ๋ฐ์ํ๊ฒ ๋ฉ๋๋ค.


CollectableSpawn.cs ํด๋์ค๋ ์ง์ ํ Target ์ค๋ธ์ ํธ๋ฅผ ๋ด๋น๊ฒ์ด์ ์์คํ ์ ์ฌ์ฉํ์ฌ ํ๋ ์ด์ด์๊ฒ ๊ฒฝ๋ก๋ฅผ ์๊ฐ์ ์ผ๋ก ํ์ํ๋ ์ญํ ์ ํฉ๋๋ค. NavMesh์ Line Renderer์ด ๊ทธ ์ญํ ์ ํ๋ ๋ฐ ๋์์ ์ค๋๋ค. ์๋ ๊ฐ๋จํ ์์ฝํด์ ์ ๋ฆฌํฉ๋๋ค.
1. using UnityEngine.AI๋ Unity์ ๋ด๋น๊ฒ์ด์ ์์คํ ๊ด๋ จ ํด๋์ค ์ฌ์ฉํ๊ธฐ ์ํ ๋ค์์คํ์ด์ค๋ฅผ ์ํฌํธํฉ๋๋ค.
2. ๋ชจ๋ ๋ณ์๋ ํ๋ผ์ด๋น ๋ณ์๋ก ์ ์ธํ๊ณ SerializeField๋ฅผ ๋ฃ์ด์ฃผ์ด ์ธ์คํํฐ์์ ์ค์ ๊ฐ๋ฅํ๊ฒ ๋ง๋ค์์ต๋๋ค.
๊ฒฝ๋ก ๋์ด์ ์์ดํ ์คํฐ ๋์ด์ ๊ฒฝ๋ก ์ ๋ฐ์ดํธ ์๋๋ ์ํ๋ ๊ฐ์ผ๋ก ์ค์ ํด ์ฃผ์ ๋ ๋ฉ๋๋ค.
3. Awake() ํจ์ ์์์ Triangulation ๋ณ์์ ๋ด๋น๊ฒ์ด์ ๋ฉ์์ ์ผ๊ฐํ ์ ๋ณด๋ฅผ ๊ณ์ฐํด์ ํ ๋นํฉ๋๋ค.
4. Start() ํจ์ ์์์ SpawnNewObject()ํจ์๋ฅผ ํธ์ถํ์ฌ ์๋ก์ด ์์ดํ (Target ์ค๋ธ์ ํธ)์ ์คํฐํฉ๋๋ค.

SpawnNewObject() ํจ์์ ์ญํ ์ ์๋ก์ด ์์ดํ ์ ์คํฐํ๊ณ , ๊ฒฝ๋ก ๊ทธ๋ฆฌ๊ธฐ ์ฝ๋ฃจํด์ ๊ด๋ฆฌํฉ๋๋ค.
1. if (Triangulation.vertices.Length == 0) ์ผ ๋ ์ฆ, ๊ณ์ฐํ NavMesh ์ ๋ณด๊ฐ ์๋ค๋ฉด ๋๋ฒ๊ทธ ๋ก๊ทธ๋ฅผ ํตํด ์๋ฌ ๋ฉ์ธ์ง๋ฅผ ์ถ๋ ฅํฉ๋๋ค. ์ด ์๋ฌ ๋ฉ์ธ์ง๋ ์์ ์์์์ Bake ํ ๋ Clear ํ ๋ ์ฐจ์ด์์ ๋ณด์ฌ์ง๋๋ค.
2. ์ค๋ธ์ ํธ์ ์์น๋ Random.Range()๋ฅผ ํตํด ๋ฌด์์ ์์น์ ์์ฑ๋๊ฒ ๋ฉ๋๋ค.
3. if (DrawPathCoroutine != null ) ๋ถ๊ธฐ๋ฌธ์์ ๊ฒฝ๋ก ๊ทธ๋ฆฌ๊ธฐ ์ฝ๋ฃจํด์ด ์คํ ์ค์ธ์ง ํ๋จํด์ ์์ or ์ค์ง ์ญํ ์ ์ํํฉ๋๋ค.

DrawPathToCollectable() ์ฝ๋ฃจํด ๋ฉ์๋์ ๋๋ค. ํ์ฑํ๋ ์ค๋ธ์ ํธ์ ํ๋ ์ด์ด ์ฌ์ด์ ๊ฒฝ๋ก๋ฅผ ๊ณ์ฐํ๊ณ ์๊ฐ์ ์ผ๋ก ํ์ํ๊ฒ ๋ฉ๋๋ค.
1. wati ๋ณ์๋ ๊ฒฝ๋ก ์ ๋ฐ์ดํธ ์๋์ ๋ฐ๋ฅธ ๋๋ ์ด๋ฅผ ์์ฑํฉ๋๋ค.
2. path ๋ณ์๋ ๋ด๋น๊ฒ์ด์ ๊ฒฝ๋ก๋ฅผ ์ ์ฅํ NavMeshPath ๊ฐ์ฒด์ ๋๋ค. ์์์ using UnityEngine.AI ๋ค์์คํ์ด์ค๋ก ์ ์ฅํ๊ธฐ ๋๋ฌธ์ ์ฌ์ฉํ ์ ์๊ฒ ๋ฉ๋๋ค.
3. while() ๋ฌธ์์ ์ค๋ธ์ ํธ๊ฐ ์กด์ฌํ ๋ ๋์ ์คํํ๊ฒ ๋ฉ๋๋ค.
4. ํด๋์ค NavMesh์ CalculatePath() ํจ์๋ฅผ ํตํด ํ๋ ์ด์ด์ ์์น์ Target์ ์์น๋ฅผ ๊ณ ๋ คํ์ฌ ๊ฒฝ๋ก๋ฅผ ๊ณ์ฐํฉ๋๋ค.
5. for() ๋ฐ๋ณต๋ฌธ์ ์์ ๊ฒฝ๋ก๋ฅผ ์๊ฐ์ ์ผ๋ก ํ์ํ๊ธฐ ์ํด Line Renderer์ ๊ผญ์ง์ ์์น๋ฅผ ๊ฒฝ๋ก ๊ผญ์ง์ ์ ์์น์ ์ ์ฉํ์ฌ ์ค์ ํฉ๋๋ค.
6. ๋ง์ฝ, ์ง์ ๋ NavMesh๋ฅผ ๋ฒ์ด๋ฌ์ ๊ฒฝ์ฐ else() ๋ฌธ์ ๋ง๋๊ฒ ๋ฉ๋๋ค. ์ด๋ ๋ง๋ค์ด ๋์ Plane ์ค๋ธ์ ํธ๋ฅผ ๋ฒ์ด๋ฌ์ ๋ ๋๋ฒ๊ทธ ๋ก๊ทธ๋ฅผ ์ถ๋ ฅํฉ๋๋ค.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
|
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;
public class CollectableSpawn : MonoBehaviour
{
[SerializeField]
private Collectable collectable; // ์์ง ๊ฐ๋ฅํ ์์ดํ
ํ๋ฆฌํน
[SerializeField]
private Transform Player; // ํ๋ ์ด์ด์ ์์น
[SerializeField]
private LineRenderer Path; // ๊ฒฝ๋ก๋ฅผ ์๊ฐ์ ์ผ๋ก ํ์ํ LineRenderer ์ปดํฌ๋ํธ
[SerializeField]
private float PathHeightOffset = 1.25f; // ๊ฒฝ๋ก ๋์ด ์คํ์
[SerializeField]
private float SpawnHeightOffset = 1.5f; // ์์ดํ
์คํฐ ๋์ด ์คํ์
[SerializeField]
private float PathUpdateSpeed = 0.25f; // ๊ฒฝ๋ก ์
๋ฐ์ดํธ ์๋
private Collectable ActiveInstance; // ํ์ฌ ํ์ฑํ๋ ์์ง ๊ฐ๋ฅํ ์์ดํ
์ธ์คํด์ค
private NavMeshTriangulation Triangulation; // ๋ด๋น๊ฒ์ด์
๋ฉ์์ ์ผ๊ฐํ ์ ๋ณด
private Coroutine DrawPathCoroutine; // ๊ฒฝ๋ก ๊ทธ๋ฆฌ๊ธฐ ์ฝ๋ฃจํด ์ฐธ์กฐ
private void Awake()
{
// ๋ด๋น๊ฒ์ด์
๋ฉ์์ ์ผ๊ฐํ ์ ๋ณด ๊ณ์ฐ
Triangulation = NavMesh.CalculateTriangulation();
}
private void Start()
{
// ์์ํ ๋ ์์ดํ
์คํฐ
SpawnNewObject();
}
public void SpawnNewObject()
{
if (Triangulation.vertices.Length == 0)
{
Debug.LogError("Triangulation vertices array is empty!");
return;
}
// ๋ฌด์์ ์์น์์ ์์ง ๊ฐ๋ฅํ ์์ดํ
์์ฑ
ActiveInstance = Instantiate(collectable,
Triangulation.vertices[Random.Range(0, Triangulation.vertices.Length)] + Vector3.up * SpawnHeightOffset,
Quaternion.Euler(90, 0, 0)
);
// ๊ฒฝ๋ก ๊ทธ๋ฆฌ๊ธฐ ์ฝ๋ฃจํด ์ค์ง
if (DrawPathCoroutine != null )
{
StopCoroutine(DrawPathCoroutine);
}
// ๊ฒฝ๋ก ๊ทธ๋ฆฌ๊ธฐ ์ฝ๋ฃจํด ์์
DrawPathCoroutine = StartCoroutine(DrawPathToCollectable());
}
private IEnumerator DrawPathToCollectable()
{
WaitForSeconds wait = new WaitForSeconds(PathUpdateSpeed);
NavMeshPath path = new NavMeshPath();
while(ActiveInstance != null)
{
// ํ๋ ์ด์ด์ ์์ดํ
์ฌ์ด์ ๋ด๋น๊ฒ์ด์
๊ฒฝ๋ก ๊ณ์ฐ
if (NavMesh.CalculatePath(Player.position, ActiveInstance.transform.position, NavMesh.AllAreas, path))
{
// ๊ฒฝ๋ก๋ฅผ LineRenderer๋ฅผ ์ฌ์ฉํ์ฌ ์๊ฐ์ ์ผ๋ก ํ์
Path.positionCount = path.corners.Length;
for (int i = 0; i < path.corners.Length; i++)
{
Path.SetPosition(i, path.corners[i] + Vector3.up * PathHeightOffset);
}
}
else
{
Debug.Log("ํด๋น ๊ฒฝ๋ก๋ฅผ ๊ณ์ฐํ ์ ์์ต๋๋ค..");
}
yield return wait;
}
}
}
|
cs |

Target ์ค๋ธ์ ํธ๋ Prefab์ผ๋ก ๋ง๋ค์ด ์ค๋๋ค. ๋ํ, Colletable ์ปดํฌ๋ํธ๋ฅผ ๋ฃ์ด์ฃผ์ธ์. Collectable ํด๋์ค๋ ํ๋ ์ด์ด๊ฐ ์ฐพ์ ์ค๋ธ์ ํธ์ ์ ๋ณด๋ฅผ ๋ด๊ณ ์์ต๋๋ค. ์ ๋ ๊ฐ๋จํ ์ค๋ธ์ ํธ๊ฐ ํ์ ํ๋ ๋ก์ง์ ๋ฃ์์ต๋๋ค.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
|
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Collectable : MonoBehaviour
{
[SerializeField]
private Vector3 SpinAmount = new Vector3(0, 20, 0);
public CollectableSpawn Spawner;
// Update is called once per frame
void Update()
{
transform.rotation = Quaternion.Euler(transform.rotation.eulerAngles + (SpinAmount * Time.deltaTime));
}
}
|
cs |

Colletable Spawn ์ปดํฌ๋ํธ ์ ๋ณด์ ๋๋ค. Colletable์๋ Target ํ๋ฆฌํน ์ ๋ณด๋ฅผ ๋ฃ์ด์ฃผ๊ณ , Player์๋ AR Camera๋ฅผ ๋ฃ์ด์ค๋๋ค. AR ํ๊ฒฝ์์๋ ์นด๋ฉ๋ผ๋ฅผ ๋ค๊ณ ์๋ ์ฃผ์ฒด๊ฐ ํ๋ ์ด์ด์ด๊ธฐ ๋๋ฌธ์ ๋๋ค. Path์๋ ๊ฒฝ๋ก๋ฅผ ์๊ฐํ ํด์ค Line Renderer๋ฅผ ๋ฃ์ด์ค๋๋ค.