Publish:

ํƒœ๊ทธ:

์นดํ…Œ๊ณ ๋ฆฌ:

๐Ÿ“Œ 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

1

๐Ÿ“ ์›ํ•˜๋Š” ์‚ฌ์ด์ฆˆ๋กœ ์ž๋ฅด๊ธฐ

์ „์ฒด ํ‹€์˜ ์‚ฌ์ด์ฆˆ๋ฅผ ์žก์•˜๋‹ค๋ฉด ์ด๋ฅผ ์ž˜๊ฒŒ ์ž˜๋ผ๋ณด์ž.

์ด๋ฅผ ์œ„ํ•ด์„œ Node.cs ํด๋ž˜์Šค๋ฅผ ๋งŒ๋“ค์–ด๋ณด์ž.

๊ฐ ๋…ธ๋“œ๋Š” ์œ„์น˜, x, y ๊ฐ’์„ ๊ฐ€์ง„๋‹ค.

๊ทธ๋ฆฌ๊ณ  ์œ„์น˜๊ฐ’์„ ํ† ๋Œ€๋กœ grid ๋ฅผ ๊ทธ๋ฆฌ๋ฉฐ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ๊ทธ๋ ค์ง„๋‹ค.

์ œ์ผ ์™ผ์ชฝ, ์•„๋ž˜๊ฐ€ ์‹œ์ž‘์  ์ด๋ผ๋Š” ๊ฒƒ์„ ๊ธฐ์–ตํ•˜์ž.

1

๐Ÿ“‹ 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

2

๐Ÿ“ ์žฅ์• ๋ฌผ ์ฒดํฌ

์ด์ œ ํ•˜๋‚˜์˜ 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

3

CheckSphere ์˜ ๊ฒฝ์šฐ ๋ฐ˜์ง€๋ฆ„ 0.5 ๋กœ ์ฒดํฌ๋ฅผ ํ•˜๊ธฐ ๋•Œ๋ฌธ์—

์žฅ์• ๋ฌผ์ด ์นธ์„ ์กฐ๊ธˆ์ด๋ผ๋„ ๋„˜์–ด๊ฐ€๋ฉด ์˜†์นธ๋„ ์ด๋™์ด ๋ถˆ๊ฐ€๋Šฅํ•˜๋‹ค๊ณ  ๋‚˜์˜จ๋‹ค.

1
bool walkable = !(Physics.CheckSphere(worldPos, _nodeRadius, _unwalkableMask));

๋ถ€๋ถ„์˜ radius ๋ถ€๋ถ„์„ radius - 0.2f ์ •๋„๋กœ ๊ฒ€์‚ฌ๋ฒ”์œ„๋ฅผ ์ขํ˜€์ฃผ๋ฉด ์กฐ๊ธˆ ๋” ์—ฌ์œ ๋กญ๊ฒŒ ๊ฒ€์‚ฌํ•˜๊ฒŒ ๋œ๋‹ค.

๋ฐฉ๋ฌธํ•ด ์ฃผ์…”์„œ ๊ฐ์‚ฌํ•ฉ๋‹ˆ๋‹ค!๐Ÿ˜Š

์—…๋ฐ์ดํŠธ:

๋Œ“๊ธ€๋‚จ๊ธฐ๊ธฐ