Path Finding Algorithm 2
ํ๊ทธ: Unity
์นดํ ๊ณ ๋ฆฌ: UnityStudy
๐ A* Algorithm
๊ธธ ์ฐพ๊ธฐ๋ฅผ ๋ณธ๊ฒฉ์ ์ผ๋ก ํด๋ณด์.
๐ Player Grid
ํ๋ ์ด์ด์ ์์น๋ฅผ Grid ์์ ํ์ํด๋ณด์.
๐ Code
MyGrid.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
public Transform _player;
public Node NodeFromWorldPoint(Vector3 worldPosition)
{
float percentX = (worldPosition.x + _gridSize.x * 0.5f) / _gridSize.x;
float percentY = (worldPosition.z + _gridSize.y * 0.5f) / _gridSize.y;
int x = Mathf.FloorToInt(_gridSizeX * percentX);
int y = Mathf.FloorToInt(_gridSizeY * percentY);
return _grid[x, y];
}
private void OnDrawGizmos()
{
Gizmos.DrawWireCube(transform.position, new Vector3(_gridSize.x, 0.5f, _gridSize.y));
if(_grid != null && _displayGizmos)
{
Node playerNode = NodeFromWorldPoint(_player.position);
foreach(var node in _grid)
{
Gizmos.color = (node._walkable) ? Color.white : Color.red;
// ์ถ๊ฐ๋ ๋ถ๋ถ
if(playerNode == node) // ํ๋ ์ด์ด๊ฐ ์์๋ ๋
ธ๋๊ฐ ํ์ฌ ๊ทธ๋ ค์ง๊ณ ์๋ ๋
ธ๋์ ๊ฐ๋ค๋ฉด
{
Gizmos.color = Color.cyan; // ์์ ๋ฐ๊ฟ๋ผ
}
// ์ถ๊ฐ ๋
Gizmos.DrawCube(node._worldPos, Vector3.one * (_nodeDiameter - 0.1f));
}
}
}
ํ๋ ์ด์ด์ ์์น๊ฐ ์ ์ฒด Grid ์ ๋ช ํผ์ผํธ ์ฏค์ ์์๋์ง ํ์ธํ๊ณ ํด๋น Node ๋ฅผ ๋ฐํํด์ค๋ค.
์ด Node ์ OnDrawGizmos() ์์ ๊ทธ๋ ค์ฃผ๋ Node ๋ฅผ ๋น๊ตํ์ฌ ์์ ๋ฐ๊ฟ์ค๋ค.
๐ป Execute
๐ Path Finding
ํ๋ ์ด์ด๊ฐ ์์๋ ์์น์์ ๋ชฉํ ์ง์ ๊น์ง ๊ธธ์ ์ฐพ๋ ๊ธฐ๋ฅ์ ๋ง๋ค์ด๋ณด์.
๐ Code
Node.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
public class Node
{
public Vector3 _worldPos;
public int _gridX;
public int _gridY;
public bool _walkable;
public int _gCost;
public int _hCost;
public int _fCost
{
get
{
return _gCost + _hCost;
}
}
public Node _parent;
public Node(Vector3 worldPos, int gridX, int gridY, bool walkable)
{
_worldPos = worldPos;
_gridX = gridX;
_gridY = gridY;
_walkable = walkable;
}
}
MyGrid.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
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
91
92
93
94
95
96
97
98
99
public class MyGrid : MonoBehaviour
{
public Transform _player;
[SerializeField] bool _displayGizmos;
[SerializeField] Vector2 _gridSize;
[SerializeField] float _nodeRadius;
[SerializeField] LayerMask _unwalkableMask;
Node[,] _grid;
float _nodeDiameter;
int _gridSizeX , _gridSizeY;
public List<Node> _path;
public List<Node> GetNeighbours(Node node)
{
List<Node> neighbours = new List<Node>();
for(int x = -1; x <= 1; x++)
{
for(int y = -1; y <= 1; y++)
{
// ์ ์๋ฆฌ์ด๋ฉด ์์ผ๋ก
if(x == 0 && y == 0) continue;
int checkX = node._gridX + x;
int checkY = node._gridY + y;
if(checkX >= 0 && checkX < _gridSizeX && checkY >= 0 && checkY < _gridSizeY)
{
neighbours.Add(_grid[checkX, checkY]);
}
}
}
return neighbours;
}
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);
bool walkable = !(Physics.CheckSphere(worldPos, _nodeRadius - 0.2f, _unwalkableMask));
_grid[x, y] = new Node(worldPos, x, y, walkable);
}
}
}
public Node NodeFromWorldPoint(Vector3 worldPosition)
{
float percentX = (worldPosition.x + _gridSize.x * 0.5f) / _gridSize.x;
float percentY = (worldPosition.z + _gridSize.y * 0.5f) / _gridSize.y;
int x = Mathf.FloorToInt(_gridSizeX * percentX);
int y = Mathf.FloorToInt(_gridSizeY * percentY);
return _grid[x, y];
}
private void OnDrawGizmos()
{
Gizmos.DrawWireCube(transform.position, new Vector3(_gridSize.x, 0.5f, _gridSize.y));
if(_grid != null && _displayGizmos)
{
Node playerNode = NodeFromWorldPoint(_player.position);
foreach(var node in _grid)
{
Gizmos.color = (node._walkable) ? Color.white : Color.red;
if(playerNode == node)
{
Gizmos.color = Color.cyan;
}
if(_path != null)
{
if(_path.Contains(node))
{
Gizmos.color = Color.black;
}
}
Gizmos.DrawCube(node._worldPos, Vector3.one * (_nodeDiameter - 0.1f));
}
}
}
}
PathFinding.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
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
public class PathFinding : MonoBehaviour
{
public Transform _player , _target;
MyGrid _grid;
private void Awake()
{
_grid = GetComponent<MyGrid>();
}
private void Update()
{
FindPath(_player.position, _target.position);
}
private void FindPath(Vector3 startPos, Vector3 targetPos)
{
Node startNode = _grid.NodeFromWorldPoint(startPos);
Node targetNode = _grid.NodeFromWorldPoint(targetPos);
List<Node> openSet = new List<Node>();
HashSet<Node> closedSet = new HashSet<Node>();
openSet.Add(startNode);
while(openSet.Count > 0)
{
Node currentNode = openSet[0];
for(int i = 1; i < openSet.Count; i++)
{
if(openSet[i]._fCost <= currentNode._fCost && openSet[i]._hCost < currentNode._hCost)
{
currentNode = openSet[i];
}
}
openSet.Remove(currentNode);
closedSet.Add(currentNode);
if(currentNode == targetNode)
{
RetracePath(startNode, targetNode);
return;
}
foreach(Node neighbour in _grid.GetNeighbours(currentNode))
{
if(!neighbour._walkable || closedSet.Contains(neighbour)) continue;
int newMovementCostToNeighbour = currentNode._gCost + GetDistance(currentNode, neighbour);
if(newMovementCostToNeighbour < neighbour._gCost || !openSet.Contains(neighbour))
{
neighbour._gCost = newMovementCostToNeighbour;
neighbour._hCost = GetDistance(neighbour, targetNode);
neighbour._parent = currentNode;
if(!openSet.Contains(neighbour)) openSet.Add(neighbour);
}
}
}
}
private void RetracePath(Node startNode, Node endNode)
{
List<Node> path = new List<Node>();
Node currentNode = endNode;
while(currentNode != startNode)
{
path.Add(currentNode);
currentNode = currentNode._parent;
}
path.Reverse();
_grid._path = path;
}
private int GetDistance(Node nodeA, Node nodeB)
{
int dstX = Mathf.Abs(nodeA._gridX - nodeB._gridX);
int dstY = Mathf.Abs(nodeA._gridY - nodeB._gridY);
if(dstX > dstY) return 14 * dstY + 10 * (dstX - dstY);
return 14 * dstX + 10 * (dstY - dstX);
}
}
๋๊ธ๋จ๊ธฐ๊ธฐ