유한 상태 기계 (Finite State Machines)
관련 소스 파일
•
GameFramework/Base/Variable/GenericVariable.cs
•
GameFramework/Base/Variable/Variable.cs
•
GameFramework/DataNode/DataNodeManager.DataNode.cs
•
GameFramework/DataNode/DataNodeManager.cs
•
GameFramework/DataNode/IDataNode.cs
•
GameFramework/DataNode/IDataNodeManager.cs
•
GameFramework/Fsm/Fsm.cs
•
GameFramework/Fsm/FsmBase.cs
•
GameFramework/Fsm/FsmManager.cs
•
GameFramework/Fsm/IFsm.cs
•
GameFramework/Fsm/IFsmManager.cs
•
GameFramework/ObjectPool/IObjectPool.cs
•
GameFramework/ObjectPool/IObjectPoolManager.cs
•
GameFramework/ObjectPool/ObjectPoolBase.cs
•
GameFramework/ObjectPool/ObjectPoolManager.cs
GameFramework의 유한 상태 기계(FSM) 시스템은 명확히 정의된 전이를 갖춘 이산 상태(discrete states)를 통해 엔티티 동작을 모델링하기 위한 구조화된 접근 방식을 제공합니다. 이 패턴은 게임 캐릭터 동작, AI 로직, UI 네비게이션 흐름, 기타 상태 기반 게임 시스템을 구현하는 데 특히 유용합니다.
개요
FSM 시스템은 개발자가 다음을 할 수 있도록 합니다:
•
명확한 진입, 업데이트, 종료 로직을 가진 상태 정의
•
특정 소유자 타입을 가진 강력한 형식의 FSM 생성
•
게임 조건에 따라 상태 간 전이 관리
•
유연한 변수 시스템을 사용하여 상태 간 데이터 공유
•
복잡한 동작을 관리 가능한 이산 컴포넌트로 구성
FSM 라이프사이클
(다이어그램 설명 부분)
핵심 구성 요소
FsmManager
FsmManager는 애플리케이션의 모든 FSM에 대한 중앙 레지스트리 역할을 합니다. GameFrameworkModule 인터페이스를 구현하여 프레임워크 라이프사이클에 참여할 수 있습니다:
// 특정 소유자 타입과 상태들로 FSM 생성
IFsm<Player> playerFsm = fsmManager.CreateFsm("PlayerFSM", player,
new IdleState(), new MovingState(), new JumpingState());
// FSM 존재 여부 확인
bool exists = fsmManager.HasFsm<Player>("PlayerFSM");
// 기존 FSM 가져오기
IFsm<Player> fsm = fsmManager.GetFsm<Player>("PlayerFSM");
// 더 이상 필요하지 않을 때 FSM 제거
fsmManager.DestroyFsm(fsm);
C#
복사
매니저는 TypeNamePair(소유자 타입 + FSM 이름) 키로 딕셔너리를 유지하여 동일한 소유자 타입에 여러 FSM을 생성할 수 있습니다.
Fsm
Fsm 클래스는 특정 소유자 타입에 대한 FSM의 구체적인 구현체입니다. 이 클래스는 다음을 관리합니다:
•
소유자 인스턴스
•
상태 모음
•
현재 상태 및 타이밍 데이터
•
상태 간 공유 데이터(Variable 시스템 사용)
•
상태 전이
각 FSM은 상태 타입별로 딕셔너리를 유지하여 하나의 FSM 내에 동일한 상태 타입 인스턴스가 하나만 존재하도록 합니다.
FsmState
모든 상태는 FsmState<T> 클래스를 상속하며, 상태가 반드시 구현해야 하는 라이프사이클 메서드를 정의합니다:
메서드 | 설명 |
OnInit | FSM 생성 중 상태가 초기화될 때 호출 |
OnEnter | 상태가 현재 상태가 되었을 때 호출 |
OnUpdate | 상태가 활성화된 동안 매 프레임마다 호출 |
OnLeave | 다른 상태로 전환될 때 호출 |
OnDestroy | FSM이 파괴될 때 호출 |
상태 간 데이터 공유
FSM 시스템은 Variable 시스템과 통합되어 상태 간 데이터 공유가 가능합니다. 이는 전이 중에도 지속되어야 하는 상태를 유지하는 데 유용합니다:
// 한 상태에서 데이터 저장
fsm.SetData<VarInt32>("JumpCount", new VarInt32(0));
// 다른 상태에서 데이터 가져오기
if (fsm.HasData("JumpCount"))
{
VarInt32 jumpCount = fsm.GetData<VarInt32>("JumpCount");
// 데이터 사용
}
// 더 이상 필요하지 않을 때 데이터 제거
fsm.RemoveData("JumpCount");
C#
복사
FSM은 이름으로 키를 둔 딕셔너리에 변수를 저장하며, 제네릭 메서드를 통해 타입 안정성을 보장합니다.
FSM 생성 및 사용
일반적인 워크플로우:
1.
FsmState<T>를 상속받아 상태 클래스 정의
2.
FsmManager를 사용해 FSM 생성
3.
초기 상태로 FSM 시작
4.
게임 조건에 따라 상태 전이 처리
예제:
public class PlayerIdleState : FsmState<Player>
{
protected override void OnEnter(IFsm<Player> fsm)
{
fsm.Owner.PlayAnimation("Idle");
}
protected override void OnUpdate(IFsm<Player> fsm, float elapseSeconds, float realElapseSeconds)
{
if (fsm.Owner.IsMoving)
{
// 상태 변경 트리거 (구현 방식에 따라 다름)
}
}
protected override void OnLeave(IFsm<Player> fsm, bool isShutdown)
{
if (!isShutdown)
{
fsm.Owner.StopAnimation("Idle");
}
}
}
C#
복사
오브젝트 풀링과의 통합
FSM 시스템은 메모리 효율을 위해 프레임워크의 참조 풀링(reference pooling) 메커니즘을 활용합니다.
Fsm<T> 클래스는 IReference를 구현하므로 FSM 인스턴스는 재사용될 수 있습니다. FSM이 파괴되면 즉시 가비지 컬렉션되지 않고 풀로 반환되어 메모리 할당 오버헤드를 줄입니다.
모범 사례
•
명확한 상태 책임: 각 상태는 진입·종료 조건이 잘 정의된 개별 동작을 표현해야 함
•
상태 수 최소화: 상태가 너무 많으면 관리가 어려움 → 필요 시 계층적 FSM 고려
•
리소스 정리: OnLeave에서 isShutdown 여부 확인 후 정리 수행
•
데이터 공유 효율화: Variable 시스템을 통해 공유하되, 불필요해지면 제거
•
명확한 네이밍: FSM 및 변수 이름을 직관적으로 지정
•
FSM 소유자 고려: FSM의 소유자는 행동이 모델링되는 엔티티여야 하며, 매니저 클래스가 아님
일반적인 사용 사례
•
캐릭터 컨트롤러: 플레이어나 NPC의 상태(대기, 이동, 공격 등) 모델링
•
AI 시스템: 적의 의사결정 및 행동 패턴 구현
•
UI 네비게이션: 화면, 메뉴, UI 흐름 관리
•
게임 흐름: MainMenu, Loading, Gameplay, Paused 등 게임 상태 제어
•
퀘스트 시스템: 퀘스트 진행 상태 추적
FSM 시스템을 활용하면 명확한 상태 경계와 전이를 가진 유지보수 가능, 모듈화된, 테스트 가능한 게임 시스템을 구축할 수 있습니다.