상태 패턴 ( State Pattern )
상태 패턴은 게임 구현에서 자주 사용되는 디자인 패턴이다.
객체가 가진 상태를 관리하고 , 각 상태에 따른 행동을 미리 지정할 수 있다.
다른 디자인 패턴과 마찬가지로 상황과 목적에 따라 다른 형태로 구현될 수 있다.
상태를 분기문 ( if / switch )로 처리하지 말고
각 상태를 '클래스( 객체 )'로 분리해 유연하게 전환할 수 있게 디자인하는 패턴
현업에서는 코드의 재사용성과 유지보수성을 높이기 위해 객체지향적인 설계를 지향하고 있다.
같은 프로그램이라도 설계자에 따라 , 혹은 객체지향을 바라보는 관점에 따라 다른 설계 방법이 나오지만 궁극적으로는 프로그램을 더 견고하게 만들고자 하는 목적이 있다. 상태 패턴은 어떠한 객체의 상태를 직관적으로 선언하고 제어하기 위해 사용되므로 게임 프로그래밍에서는 몰라선 안 될 중요한 디자인 패턴 중 하나이다.
상태 패턴이 필요한 이유
▼ 기본적으로 캐릭터 로직을 이렇게 생기기 쉽다.
void Update()
{
if (state == Idle) { ... }
else if (state == Run) { ... }
else if (state == Jump) { ... }
else if (state == Attack) { ... }
}
▼ 캐릭터가 Idle , Move , Attack 등의 상태를 가졌을 때 객체지향적인 설계가 고려되지 않고 작성
public enum StateList
{
Idle, Move, Jump, Attack
}
public class PlayerState : MonoBehaviour
{
private int _direction;
[SerializeField] private StateList CurrentState;
private void Update()
{
// 키 입력 에 따른 각 상태 변화
}
private void Idle()
{
// 기능 로직
// 애니메이션 변화 로직
// 예외처리 등
}
private void Move()
{
// 기능 로직
// 애니메이션 변화 로직
// 예외처리 등
}
private void Jump()
{
// 기능 로직
// 애니메이션 변화 로직
// 예외처리 등
}
private void Attack()
{
// 기능 로직
// 애니메이션 변화 로직
// 예외처리 등
}
}
문제점 :
- 상태가 늘어날수록 조건문이 무한히 길어진다
- 공격 중 → 피격 → 넉백 등 "전이 규칙" 이 복잡해질수록 유지보수가 어렵다
- 각 상태 별 행동이 섞여 코드가 스파게티화된다.
플레이어의 캐릭터가 Move, Attack 등의 상태를 가졌을 때 객체지향적 설계를 하지 않으면 다음과 같은 문제가 발생한다.
상태가 하나씩 추가될 때마다 하나의 클래스에 각 기능 구현과 더불어 각 상황에 대한 예외처리까지 생각해야 한다.
결과적으로 버그나 오류 발생 확률이 높아지게 된다.
그리고 State를 플레이어 뿐만 아니라 플레이어와 상태와 동작이 동일한 몬스터 객체에도 적용해야 할 경우
몬스터 클래스 내부에서 다시 새롭게 개별 동작을 정의해야 한다는 단점이 있다.
상태 패턴은 어떤 객체의 상태를 효과적으로 관리하기 위해 고안된 디자인 패턴이다.
상태 패턴은 각 개별적인 상태를 하나의 클래스 , 객체로 지정해 놓고 각 상태에 따른 동작을 미리 정의하는 것이 핵심이다.
그래서 상태 자체를 하나의 객체로 캡슐화해서 해결한다.
작동 원리
1. 공통 인터페이스 정의
public interface IState
{
void Enter(); // 상태 진입 시 한 번 호출
void Update(); // 매 프레임 호출
void Exit(); // 상태 종료 시 한 번 호출
}
2. 상태 클래스 구현
public class IdleState : IState
{
public void Enter() { Debug.Log("Idle 시작"); }
public void Update() { /* 가만히 있음 */ }
public void Exit() { Debug.Log("Idle 종료"); }
}
public class RunState : IState
{
public void Enter() { Debug.Log("Run 시작"); }
public void Update() { /* 이동 처리 */ }
public void Exit() { Debug.Log("Run 종료"); }
}
3. 상태머신 클래스
public class StateMachine
{
public IState CurrentState { get; private set; }
public void ChangeState(IState newState)
{
CurrentState?.Exit();
CurrentState = newState;
CurrentState.Enter();
}
public void Update()
{
CurrentState?.Update();
}
}
4. 유니티에서 사용
public class PlayerController : MonoBehaviour
{
private StateMachine _stateMachine;
private void Start()
{
_stateMachine = new StateMachine();
_stateMachine.ChangeState(new IdleState());
}
private void Update()
{
_stateMachine.Update();
}
}
State Pattern 과 FSM 의 차이점
FSM ( Finite State Machine )
- 상태 전이를 표로 관리한다
- 하나의 스크립트 안에 모든 상태 로직과 전이 조건이 존재한다
- if / switch 기반이 많아 커지면 복잡해진다
State Pattern
- 상태를 독립된 클래스 ( 객체 ) 로 분리한다
- 전이 로직도 각 상태 내부에서 작성 가능하다
- 유지보수가 쉽고 확장성이 뛰어나다
State Pattern 은 "객체지향적으로 구현한 FSM" 이라고 볼 수 있다.
| 상황 | FSM | State Pattern |
| 간단한 적 AI | 사용하기 좋다 | 가능하다 |
| 플레이어 행동 시스템 | 가능하다 | 강력 추천한다 |
| 몬스터가 다양한 패턴과 상태를 가질 때 | 가능하다 | 최적 |
| 대규모 캐릭터 행동 트리 | 비추천 | 가능하다 ( BT 추천 ) |
State Pattern 장점
- if / switch 가 사라져 코드가 깔끔해진다
- 새로운 상태 추가가 쉬워져서 유지 보수에 좋다
- 각 상태의 로직이 독립되어 SRP ( 단일 책임 원칙 ) 준수하다
- 상태 간 전환 흐름을 눈으로 보기 쉽다
State Pattern 단점
- 상태마다 클래스를 만들어야 해서 파일이 많아진다
- 작은 규모 게임에서는 오히려 과한 설계가 될 수도 있다
비유
캐릭터의 행동을 역할극이라고 가정
- IdleState : 앉아있기 배우
- RunState : 뛰기 배우
- AttackState : 공격 배우
각 배우는 자신의 역할 ( 행동 ) 에 필요한 대사만 알고 있고
감독 ( StateMachine ) 이 지금 어떤 배우가 무대에 올라갈지 결정한다.
'🧊Unity Basic > 디자인패턴' 카테고리의 다른 글
| FSM ( Finite State Machine ) (0) | 2025.11.11 |
|---|---|
| MVC 패턴 (0) | 2025.11.10 |
| Adapter Pattern 핵심 코드 (0) | 2025.10.24 |
| Observer Pattern (0) | 2025.10.21 |
| Singleton 과 Generic (0) | 2025.10.21 |