게임 AI를 설계할 때, 단순히 “적이 플레이어를 발견 → 공격” 하는 것 이상으로,
다양한 상황에서 복합적인 의사결정을 처리해야 하는 경우가 많습니다. 이번 글에서는 현업 및 인디 개발에서 많이 사용되는 AI 패턴과 의사결정 기법을 정리해보겠습니다.
Idle → Chase → Attack → Die if/else 혹은 switch가 대량 발생해 스파게티화될 위험 // 추상 State
public abstract class State<T>
{
protected T owner;
public State(T owner) { this.owner = owner; }
public virtual void Enter() {}
public virtual void Update() {}
public virtual void Exit() {}
}
public class StateMachine<T>
{
public State<T> CurrentState { get; private set; }
private List<Transition<T>> transitions = new List<Transition<T>>();
public StateMachine(State<T> initialState)
{
CurrentState = initialState;
CurrentState.Enter();
}
public void AddTransition(Transition<T> transition)
{
transitions.Add(transition);
}
public void Update()
{
// 상위 FSM 전환 조건 확인
foreach (var transition in transitions)
{
if (transition.From == CurrentState && transition.Condition())
{
CurrentState.Exit();
CurrentState = transition.To;
CurrentState.Enter();
break;
}
}
// 현재 상태의 Update (CompositeState라면, 내부에서 SubState도 처리)
CurrentState.Update();
}
}
// Pseudo-code: Selector, Sequence, Action 노드 예시
public abstract class BTNode
{
public abstract BTState Tick();
}
public class Selector : BTNode
{
private List<BTNode> children;
public override BTState Tick()
{
foreach (var child in children)
{
var result = child.Tick();
if (result == BTState.SUCCESS)
return BTState.SUCCESS;
}
return BTState.FAILURE;
}
}
// Pseudo-code for GOAP
public class Action
{
public Dictionary<string, bool> PreConditions;
public Dictionary<string, bool> Effects;
public float Cost;
// 실행 로직 등
}
public class GoapAgent
{
public List<Action> actions;
public Dictionary<string, bool> worldState;
public List<string> goals;
public void Plan() {
// 검색 알고리즘(A*, Dijkstra) 등으로
// PreConditions-Effects가 이어지는 Action 시퀀스 구성
}
}
public class UtilityAI
{
public float health;
public float enemyDistance;
public float ComputeHealUtility() => Mathf.Clamp01(1 - (health / 100));
public float ComputeAttackUtility() => Mathf.Clamp01((100 - enemyDistance) / 100);
public void DecideAction()
{
float healScore = ComputeHealUtility();
float attackScore = ComputeAttackUtility();
if (healScore > attackScore)
DoHeal();
else
DoAttack();
}
}
public class DecisionTree
{
public Node root;
public void Update()
{
Node node = root;
while (node is DecisionNode)
{
var decision = node as DecisionNode;
node = decision.Decide() ? decision.trueNode : decision.falseNode;
}
(node as ActionNode).Act();
}
}
public class Blackboard
{
private Dictionary<string, object> data = new Dictionary<string, object>();
public T GetValue<T>(string key) => (T)data[key];
public void SetValue<T>(string key, T value) => data[key] = value;
}
FSM/HFSM
Behaviour Tree
GOAP
Utility AI
Decision Tree
Blackboard
게임 AI는 단순 FSM부터 Behaviour Tree, GOAP, Utility AI, Decision Tree, Blackboard 시스템까지, 다양한 패턴이 존재합니다.
각 기법은 고유한 강점과 제약이 있으며, 실제 프로젝트에서는 2개 이상의 기법을 혼합해서 쓰거나, 특정 부분만 활용하기도 합니다.
자신의 게임 장르, AI 복잡도, 팀 협업 방식을 고려하여 가장 적합한 패턴(또는 패턴 조합)을 선택하면, 유지보수성과 확장성을 모두 잡을 수 있을 것으로 기대됩니다.
“작은 프로젝트는 FSM, 어느 정도 이상이면 BT/GOAP/Utility AI 검토”가 흔한 흐름이며, 그 중에서도 “디자이너 주도 시 BT,” “목표 기반 AI 시 GOAP” 등으로 정리해서 적용해볼 수 있을 것 같습니다.
다음에 기회가 되면, AI 툴(시각 에디터, ScriptableObject 기반 설정)이나, 머신 러닝 RL 기법 등을 접목해보는 방법도 소개해드리겠습니다!