오늘은 State 패턴에 대해 알아보는 시간을 가지겠습니다.

- 객체의 상태에 따라 그 객체의 행동이 달라지도록 설계하는 디자인 패턴입니다.
- 주로 상태를 변경해야 할 필요가 있을 때 객체의 행동을 수정하지 않고, 상태별로 객체를 나누어 코드의 유연성과 확장성을 높이는 데 도움을 줍니다
- 객체의 상태를 캡슐화
- 상태를 나타내는 클래스들이 각각의 상태별 행동을 구현하며, 이러한 클래스를 통해 상태를 캡슐화합니다.
- 상태 전환을 명확하게 정의
- 상태 객체들이 현재 상태에 따라 다른 상태로 전환할 수 있도록 도와줍니다. 상태 전환은 Context 객체나 상태 클래스에서 관리할 수 있습니다.
- 행동의 다형성 사용
- 각 상태 클래스가 공통 인터페이스 또는 추상 클래스를 구현하여 다형성을 제공합니다. 이를 통해 현재 상태에 따라 다른 행동이 자연스럽게 호출됩니다.
- 상태 독립성
- 각 상태는 다른 상태로부터 독립적이며, 개별 상태를 수정하더라도 다른 상태에 영향을 미치지 않습니다.
public class StateMachine
{
private IState currentState;
// 상태 변경 메서드
public void SetState(IState newState)
{
if (currentState != null)
{
currentState.Exit(); // 현재 상태에서 나가기
}
currentState = newState;
if (currentState != null)
{
currentState.Enter(); // 새 상태로 들어가기
}
}
// 현재 상태 유지 및 업데이트
public void Update()
{
if (currentState != null)
{
currentState.Execute(); // 현재 상태 실행
}
}
}
- 현재 상태 객체를 유지합니다.
- 상태 전환을 요청할 때 상태 객체를 교체합니다.
- 행동의 실행을 각 상태 객체에 위임합니다.
public interface IState
{
void Enter(); // 상태로 진입할 때 호출되는 메서드
void Execute(); // 상태가 유지되는 동안 호출되는 메서드
void Exit(); // 상태에서 벗어날 때 호출되는 메서드
}
- 공통된 메서드를 정의하여, 상태별로 일관된 행동을 보장합니다.
- 각 상태가 수행해야 할 메서드들을 명확하게 정의하여 상태별 구현을 쉽게 만듭니다.
public class IdleState : IState
{
public void Enter()
{
Debug.Log("Entering Idle State");
}
public void Execute()
{
Debug.Log("Executing Idle Behavior: Standing still");
}
public void Exit()
{
Debug.Log("Exiting Idle State");
}
}
public class AttackState : IState
{
public void Enter()
{
Debug.Log("Entering Attack State");
}
public void Execute()
{
Debug.Log("Executing Attack Behavior: Attacking the target");
}
public void Exit()
{
Debug.Log("Exiting Attack State");
}
}
- 상태 전환 시 수행해야 할 초기화 코드나 종료 코드를 Enter와 Exit 메서드에 정의합니다.
- 해당 상태에서 지속적으로 수행할 행동을 Execute 메서드에 정의합니다.
- 상태 전환 조건이 충족되면, Context의 상태를 변경할 수 있습니다.
public class GameManager : MonoBehaviour
{
private StateMachine stateMachine; // 상태 머신
private IdleState idleState; // Idle 상태
private AttackState attackState; // Attack 상태
void Start()
{
// 상태 머신 및 상태 객체 초기화
stateMachine = new StateMachine();
idleState = new IdleState();
attackState = new AttackState();
stateMachine.SetState(idleState);
}
void Update()
{
stateMachine.Update();
// 상태 전환 조건
if (Input.GetKeyDown(KeyCode.A))
{
stateMachine.SetState(attackState); // 공격 상태로 전환
}
else if (Input.GetKeyDown(KeyCode.I))
{
stateMachine.SetState(idleState); // Idle 상태로 전환
}
}
}
- 실제 사용하는 경우입니다. 객체 초기화가 이루어 진 뒤 Input.GetKey()로 상태 변화를 체크합니다.
- 초기 상태 설정
- 상태 유지
- 상태 전환
- SetState() : 초기 상태 설정
- Update() : 상태 유지
- Input.GetKeyDown() : 상태 전환
- 유지보수 용이성
- 코드의 가독성 증가
- 중복 제거
- 명확한 상태 전환 관리
- 클래스 수 증가로 인한 복잡성 증가
- 의사결정의 분산
- 상태가 변하는 "대기","이동","공격"... 등등 상황인 NPC AI, 플레이어 상태 관리, 애니메이션 트리거 등등의 사용됩니다.

- 초기 상태인 Idle의 값이 계속 나오다 Input.Getkey()로 상태를 변경하니 상태 값이 변경됩니다.
구현 중 가장 큰 장점은 상태 전환 로직의 명확성이었습니다. 이전에는 조건문을 사용하거나 엉성하게 구현하였는데 공부하면서 구현해보니 각 상태가 무엇을 하는지 명확하게 알 수 있어 편리하였습니다..NPC AI 설계의 여러 패턴들과 조합하면 개인적으로 만족하는 AI 설계가 나오지 않을까 싶습니다..