Path Finding Algorithm
ํ๊ทธ: Unity
์นดํ ๊ณ ๋ฆฌ: UnityStudy
๐ A* Algorithm
โ๏ธ A* Algorithm ์ด๋?
LOL ํน์ Starcraft ์ ๊ฐ์ ๊ฒ์์ ํ๋ค๋ณด๋ฉด ์ ํํ ์บ๋ฆญํฐ๋ฅผ ๋ง์ฐ์ค๋ก ์กฐ์ํ ์ ์๋ค.
์ฐ๋ฆฌ๊ฐ ์ํ๋ ๋ชฉํ๋ก ์บ๋ฆญํฐ๋ฅผ ์ด๋์ํฌ ์ ์๋๋ฐ,
์ด ๋ ์บ๋ฆญํฐ๋ ์ต๋จ๊ฒฝ๋ก๋ก ๋ชฉํ์ง์ ๊น์ง ์ด๋ํ๋ค.
A* ์๊ณ ๋ฆฌ์ฆ์ ์ต๋จ๊ฒฝ๋ก๋ฅผ ์ฐพ๋ ์๊ณ ๋ฆฌ์ฆ ์ค ํ๋์ด๋ค.
์์์ง์ ๊ณผ ๋ชฉํ์ง์ ์ด ๋ช ํํ ์ฃผ์ด์ง ์ํ์์ ์๋ํ๊ณ ,
๊ฐ ๋ ธ๋๋ ์ด๋์ ํ์ํ ๊ฒฝ๋น(Cost)๋ฅผ 3๊ฐ์ง ๊ฐ์ง๊ฒ ๋๋ค.
G_Cost = ์์์ ์์ ํ์ฌ ์์น๊น์ง์ ๊ฑฐ๋ฆฌ
H_Cost = ํ์ฌ ์์น์์ ๋์ ๊น์ง์ ๊ฑฐ๋ฆฌ
F_Cost = G_Cost + H_Cost
์ด๋ ๊ฒ 3๊ฐ์ง์ ๋น์ฉ์ ๊ฐ์ง๋ฉฐ ์ด๋ค์ ๋น๊ตํ์ฌ ์ต๋จ๊ฑฐ๋ฆฌ๋ฅผ ๊ตฌํ๊ฒ ๋๋ค.
๐ ์ ์ฒด ํฌ๊ธฐ
๋ชจ๋ ๊ฒ์์ ๋งต์ด ์กด์ฌํ๋ค.
์ฌ์ฉ์๊ฐ ํธํ๊ฒ Grid ์ค์ ์ ์กฐ์ ํ๊ธฐ ์ฝ๊ฒ ํด๋ณด์.
์คํฌ๋ฆฝํธ๋ MyGrid.cs ๋ฅผ ๋ง๋ค์ด์ ํด๋ณด์.
๐ Code
1
2
3
4
5
[SerializeField] Vector2 _gridSize;
private void OnDrawGizmos()
{
Gizmos.DrawWireCube(transform.position, new Vector3(_gridSize.x, 0.5f, _gridSize.y));
}
DrawWireCube ๋ฅผ ํตํด ์์ด์ดํ๋ ์์ ํ๋ธ ํํ๋ก ๊ทธ๋ ค์ค๋ค.
ํ์ฌ ์คํฌ๋ฆฝํธ๊ฐ ๋ฌ๋ฆฐ ์์น์ new Vector3 ์ x, y, z ์ฌ์ด์ฆ๋ก ๊ทธ๋ฆฌ๊ฒ ๋๋ค.
๐ป Execute
๐ ์ํ๋ ์ฌ์ด์ฆ๋ก ์๋ฅด๊ธฐ
์ ์ฒด ํ์ ์ฌ์ด์ฆ๋ฅผ ์ก์๋ค๋ฉด ์ด๋ฅผ ์๊ฒ ์๋ผ๋ณด์.
์ด๋ฅผ ์ํด์ Node.cs ํด๋์ค๋ฅผ ๋ง๋ค์ด๋ณด์.
๊ฐ ๋ ธ๋๋ ์์น, x, y ๊ฐ์ ๊ฐ์ง๋ค.
๊ทธ๋ฆฌ๊ณ ์์น๊ฐ์ ํ ๋๋ก grid ๋ฅผ ๊ทธ๋ฆฌ๋ฉฐ ๋ค์๊ณผ ๊ฐ์ด ๊ทธ๋ ค์ง๋ค.
์ ์ผ ์ผ์ชฝ, ์๋๊ฐ ์์์ ์ด๋ผ๋ ๊ฒ์ ๊ธฐ์ตํ์.
๐ Code
Node.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
public class Node
{
public Vector3 _worldPos;
public int _gridX;
public int _gridY;
public Node(Vector3 worldPos, int gridX, int gridY)
{
_worldPos = worldPos;
_gridX = gridX;
_gridY = gridY;
}
}
Grid.cs
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
[SerializeField] bool _displayGizmos;
[SerializeField] Vector2 _gridSize;
[SerializeField] float _nodeRadius;
Node[,] _grid;
float _nodeDiameter;
int _gridSizeX , _gridSizeY;
void Awake()
{
_nodeDiameter = _nodeRadius * 2;
_gridSizeX = Mathf.RoundToInt(_gridSize.x / _nodeDiameter);
_gridSizeY = Mathf.RoundToInt(_gridSize.y / _nodeDiameter);
CreateGrid();
}
void CreateGrid()
{
_grid = new Node[_gridSizeX, _gridSizeY];
Vector3 worldBottomLeft =
transform.position - Vector3.right * (_gridSize.x / 2) - Vector3.forward * (_gridSize.y / 2);
for(int x = 0; x < _gridSizeX; x++)
{
for(int y = 0; y < _gridSizeY; y++)
{
Vector3 worldPos =
worldBottomLeft + Vector3.right * (x * _nodeDiameter + _nodeRadius)
+ Vector3.forward * (y * _nodeDiameter + _nodeRadius);
_grid[x, y] = new Node(worldPos, x, y);
}
}
}
private void OnDrawGizmos()
{
Gizmos.DrawWireCube(transform.position, new Vector3(_gridSize.x, 0.5f, _gridSize.y));
if(_grid != null && _displayGizmos)
{
foreach(var node in _grid)
{
Gizmos.color = Color.white;
Gizmos.DrawCube(node._worldPos, Vector3.one * (_nodeDiameter - 0.1f));
}
}
}
๐ป Execute
๐ ์ฅ์ ๋ฌผ ์ฒดํฌ
์ด์ ํ๋์ Node ์์ ์ฅ์ ๋ฌผ์ด ์กด์ฌํ๋์ง ์ฒดํฌ๋ฅผ ํด์ฃผ์ด์ผ ํ๋ค.
๊ทธ๋์ผ player ๊ฐ ๊ฐ ์ ์๋์ง ์๋์ง๋ฅผ ์ฒดํฌํ ์ ์๊ธฐ ๋๋ฌธ!
์ฝ๊ฐ์ ์ฝ๋๋ง ์ถ๊ฐํด์ฃผ๋ฉด ๋๋ค.
๐ Code
Node.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Node
{
public Vector3 _worldPos;
public int _gridX;
public int _gridY;
public bool _walkable;
public Node(Vector3 worldPos, int gridX, int gridY, bool walkable)
{
_worldPos = worldPos;
_gridX = gridX;
_gridY = gridY;
_walkable = walkable;
}
}
Grid.cs
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
public class MyGrid : MonoBehaviour
{
[SerializeField] bool _displayGizmos;
[SerializeField] Vector2 _gridSize;
[SerializeField] float _nodeRadius;
[SerializeField] LayerMask _unwalkableMask;
Node[,] _grid;
float _nodeDiameter;
int _gridSizeX , _gridSizeY;
void Awake()
{
// ๊ทธ๋๋ก
}
void CreateGrid()
{
// ์๋ ๊ทธ๋๋ก
Vector3 worldPos =
worldBottomLeft + Vector3.right * (x * _nodeDiameter + _nodeRadius)
+ Vector3.forward * (y * _nodeDiameter + _nodeRadius);
// ์๋ ๋ณ๊ฒฝ
bool walkable = !(Physics.CheckSphere(worldPos, _nodeRadius, _unwalkableMask));
_grid[x, y] = new Node(worldPos, x, y, walkable);
}
private void OnDrawGizmos()
{
// Gizmos.color = Color.white; ์ ์ง์ฐ๊ณ ์๋ซ์ค ์ถ๊ฐ
Gizmos.color = (node._walkable) ? Color.white : Color.red;
}
}
๐ป Execute
CheckSphere ์ ๊ฒฝ์ฐ ๋ฐ์ง๋ฆ 0.5 ๋ก ์ฒดํฌ๋ฅผ ํ๊ธฐ ๋๋ฌธ์
์ฅ์ ๋ฌผ์ด ์นธ์ ์กฐ๊ธ์ด๋ผ๋ ๋์ด๊ฐ๋ฉด ์์นธ๋ ์ด๋์ด ๋ถ๊ฐ๋ฅํ๋ค๊ณ ๋์จ๋ค.
1
bool walkable = !(Physics.CheckSphere(worldPos, _nodeRadius, _unwalkableMask));
๋ถ๋ถ์ radius ๋ถ๋ถ์ radius - 0.2f ์ ๋๋ก ๊ฒ์ฌ๋ฒ์๋ฅผ ์ขํ์ฃผ๋ฉด ์กฐ๊ธ ๋ ์ฌ์ ๋กญ๊ฒ ๊ฒ์ฌํ๊ฒ ๋๋ค.
๋๊ธ๋จ๊ธฐ๊ธฐ