목차
서론: 게임 AI에서의 장애물 회피 중요성
게임에서의 캐릭터 이동은 그 자체로 중요한 요소입니다. 특히, 실시간으로 변하는 환경에서 캐릭터가 장애물을 피해 이동할 수 있도록 만드는 것은 더욱 중요합니다. 예를 들어, 전투 게임에서는 적이 공격을 회피하거나, 롤플레잉 게임에서는 NPC들이 장애물을 피하며 자연스럽게 환경을 탐험할 수 있어야 합니다. 이를 가능하게 해주는 기술 중 하나가 바로 *A 알고리즘입니다. *A 알고리즘은 목표 지점까지의 최적 경로를 탐색할 때 장애물을 피할 수 있는 유연한 방법을 제공합니다.
이번 글에서는 유니티 엔진을 활용하여 *A 알고리즘**을 사용한 동적 장애물 회피 경로 탐색을 구현하는 방법에 대해 자세히 다뤄보겠습니다. 실제 게임에서 어떻게 활용될 수 있는지 다양한 예시를 들어 설명하고, 이를 위한 고급 코드 구현법도 소개할 예정입니다.
1. A 알고리즘 개요*
A* 알고리즘은 시작 지점에서 목표 지점까지 가는 최단 경로를 찾는 알고리즘으로, 비용을 계산하여 가장 효율적인 경로를 탐색합니다. 이 알고리즘은 그래프 탐색을 사용하며, 각 지점에 대한 휴리스틱 함수를 통해 탐색 효율을 높입니다.
A* 알고리즘의 핵심은 **g(n)**와 h(n) 값입니다:
- g(n): 현재 지점까지의 이동 비용 (start부터 n까지)
- h(n): 목표 지점까지의 예상 비용 (휴리스틱 함수로 계산)
A* 알고리즘은 **f(n) = g(n) + h(n)**을 최소화하는 경로를 선택하여 탐색합니다.
2. 유니티에서 A* 알고리즘 구현하기
이제 A* 알고리즘을 유니티에서 어떻게 구현할 수 있는지에 대해 설명하겠습니다. 기본적으로 A* 알고리즘은 그리드 기반 경로 탐색에서 많이 사용되며, 각 그리드 칸은 장애물 유무에 따라 이동 가능 여부가 결정됩니다. 장애물이 있는 칸은 통과 불가로 설정하고, 비어있는 칸은 이동 가능한 경로로 설정합니다.
예시 코드: A* 알고리즘 구현 (기본 경로 탐색)
using UnityEngine;
using System.Collections.Generic;
public class AStarPathfinding : MonoBehaviour
{
public Transform startPoint;
public Transform endPoint;
public LayerMask obstacles; // 장애물 레이어
private Vector3[] path;
private Grid grid;
void Start()
{
grid = new Grid(10, 10, 1f); // 10x10 그리드
FindPath(startPoint.position, endPoint.position);
}
void FindPath(Vector3 start, Vector3 end)
{
Node startNode = grid.NodeFromWorldPoint(start);
Node endNode = grid.NodeFromWorldPoint(end);
HashSet<Node> openSet = new HashSet<Node>(); // A* open list
HashSet<Node> closedSet = new HashSet<Node>(); // A* closed list
openSet.Add(startNode);
while (openSet.Count > 0)
{
Node currentNode = GetLowestCostNode(openSet);
if (currentNode == endNode)
{
RetracePath(startNode, endNode);
return;
}
openSet.Remove(currentNode);
closedSet.Add(currentNode);
foreach (Node neighbor in grid.GetNeighbors(currentNode))
{
if (closedSet.Contains(neighbor) || !neighbor.walkable)
continue;
float newCostToNeighbor = currentNode.gCost + GetDistance(currentNode, neighbor);
if (newCostToNeighbor < neighbor.gCost || !openSet.Contains(neighbor))
{
neighbor.gCost = newCostToNeighbor;
neighbor.hCost = GetDistance(neighbor, endNode);
neighbor.parent = currentNode;
if (!openSet.Contains(neighbor))
openSet.Add(neighbor);
}
}
}
}
void RetracePath(Node startNode, Node endNode)
{
List<Vector3> waypoints = new List<Vector3>();
Node currentNode = endNode;
while (currentNode != startNode)
{
waypoints.Add(currentNode.worldPosition);
currentNode = currentNode.parent;
}
waypoints.Reverse();
path = waypoints.ToArray();
}
void OnDrawGizmos()
{
if (path != null)
{
foreach (Vector3 waypoint in path)
{
Gizmos.color = Color.green;
Gizmos.DrawCube(waypoint, Vector3.one);
}
}
}
Node GetLowestCostNode(HashSet<Node> openSet)
{
Node lowestCostNode = null;
foreach (Node node in openSet)
{
if (lowestCostNode == null || node.fCost < lowestCostNode.fCost)
lowestCostNode = node;
}
return lowestCostNode;
}
float GetDistance(Node nodeA, Node nodeB)
{
float distX = Mathf.Abs(nodeA.gridX - nodeB.gridX);
float distY = Mathf.Abs(nodeA.gridY - nodeB.gridY);
return distX + distY;
}
}
코드 설명
- Grid: 게임 공간을 그리드로 나누고, 각 그리드의 상태(이동 가능 여부, 장애물 여부)를 관리하는 클래스입니다.
- Node: 각 그리드 칸을 나타내며, 각 노드는
gCost
,hCost
,fCost
및 부모 노드 정보를 가지고 있습니다. - FindPath(): A* 알고리즘을 구현한 핵심 함수입니다. 시작 지점에서 목표 지점까지 경로를 계산하고, 장애물을 피해 최적의 경로를 찾습니다.
- OnDrawGizmos(): 유니티 에디터에서 경로를 시각적으로 확인할 수 있도록 경로를 그립니다.
3. 동적 장애물 회피: 실시간 경로 변경
게임에서는 장애물이 실시간으로 변할 수 있기 때문에, 경로를 찾아내는 동시에 장애물을 피할 수 있도록 경로를 동적으로 수정해야 합니다. 이를 위해서는 경로 추적 중 실시간으로 장애물이 등장할 경우 경로를 다시 탐색하는 기능을 추가해야 합니다.
예시 코드: 동적 장애물 회피
void Update()
{
// 장애물이 발생하면 경로 재탐색
if (ObstacleDetected())
{
FindPath(startPoint.position, endPoint.position);
}
}
bool ObstacleDetected()
{
// 예시로 간단히 Raycast를 통해 장애물 감지
RaycastHit hit;
if (Physics.Raycast(transform.position, transform.forward, out hit, 5f, obstacles))
{
return true;
}
return false;
}
코드 설명
ObstacleDetected()
는 현재 캐릭터 앞에 장애물이 있을 경우true
를 반환하고, 이 때FindPath()
를 호출하여 경로를 재탐색합니다. 장애물이 이동 경로에 영향을 미치는 상황에서 실시간으로 경로를 조정할 수 있습니다.
4. 게임에서의 활용 사례
이 알고리즘은 다양한 게임에서 활용될 수 있습니다. 예를 들어:
- 전략 게임에서 유닛들이 장애물을 피하며 적군에게 접근하는 경로를 계산할 때.
- RPG에서 NPC가 장애물을 피하며 자연스러운 경로로 마을을 돌아다닐 때.
- 전투 게임에서 적들이 플레이어를 추적하며 장애물을 피할 때.
이렇게 A* 알고리즘을 활용하면 게임 내에서 자연스럽고 지능적인 이동을 구현할 수 있습니다.
마무리
유니티에서 *A 알고리즘을 사용한 장애물 회피 경로 탐색**은 게임 AI의 중요한 부분으로, 게임의 몰입감을 높이는 핵심 기술입니다. 이 글에서는 기본적인 경로 탐색부터 동적 장애물 회피까지 구현하는 방법을 소개했습니다. 이와 같은 기술을 사용하면 게임 내의 캐릭터와 유닛들이 더 자연스럽게 환경과 상호작용하며, 플레이어에게 더 나은 경험을 제공할 수 있습니다.