FSM - 상태 패턴
목차
1. 상태 패턴 이란?
- 객체의 상태에 따라 객체의 행동이 변하는 패턴
- 객체의 상태를 클래스로 캡슐화하고, 상태 전환을 객체 내에서 관리 할 수 있게 만듬
2. 핵심 개념
- 상태 캡슐화
- 상태를 독립적인 클래스로 정의
- 각 상태는 특정 행동을 구현
- 상태 전환
- 현재 상태에 따라 동작을 결정
- 유연성
- 상태별 행동은 독립적으로 존재
3. 장.단점
- 장점
- 조건문을 줄이고 가독성을 높임
- 새로운 상태 추가가 쉬움
- 유지 보수가 용이
- 단점
- 상태가 많으면 복잡해 짐
4. 상태 패턴 구현 방법들
1. enum
enum은 가장 단순하고 직관적인 방법입니다. 상태를 열거형으로 정의하고, 이를 기반으로 분기처리합니다.
- 장점:
- 구현이 간단하고 코드가 직관적임.
- 상태 값이 제한적일 경우 적합.
- 단점:
- 상태별로 동작을 분리하기 어려움. 분기문(if, switch)의 남용으로 코드가 비대해질 수 있음.
- 상태와 행동이 서로 강하게 결합됨.
2. 인터페이스
상태를 독립된 인터페이스(IState)로 정의하고, 각 상태별로 인터페이스를 구현한 클래스를 만듭니다.
- 장점:
- 상태와 행동을 분리하여 유연하게 관리 가능.
- 상태 추가/확장이 용이.
- 객체지향적인 설계.
- 단점:
- 상태별로 클래스를 많이 만들어야 할 수 있음.
- 복잡성이 증가할 가능성이 있음.
3. 클래스
상태를 독립적인 클래스로 분리하고, 이를 조합해 객체의 상태를 전환합니다. 여기에는 상태 전환 로직을 컨텍스트(Context)에 넣을 수도 있고, 상태 자체에서 관리할 수도 있습니다.
- 장점:
- 상태별 행동을 캡슐화하여 응집도가 높아짐.
- 상태 전환 로직을 관리하기 쉽고 확장성이 높음.
- 단점:
- 상태를 구현하는 클래스가 많아질 가능성이 있음.
- 설계와 구현이 다소 복잡할 수 있음.
4. 함수
상태를 함수(또는 델리게이트, 람다)로 정의하여 동작을 할당합니다. 유니티에서는 상태를 업데이트하는 방식으로 델리게이트를 사용하는 경우가 많습니다.
- 장점:
- 상태 전환과 행동 구현이 간단.
- 상태별로 별도의 클래스를 정의하지 않아도 됨.
- 단점:
- 상태가 많아질수록 코드가 관리하기 어려워질 수 있음.
- 상태 전환이 명시적으로 보이지 않을 수 있음.
5. ScriptableObject
유니티에서 ScriptableObject를 활용하면 상태를 데이터 자산으로 정의할 수 있습니다. 이는 상태와 데이터를 함께 캡슐화하는 방식입니다.
- 장점:
- 상태를 데이터와 함께 재사용 가능.
- 상태를 에디터에서 시각적으로 관리할 수 있음.
- 유니티에서 자주 사용되는 방식.
- 단점:
- 설계가 까다로울 수 있음.
- 상태와 데이터의 관계를 잘 정의해야 함.
6. 플래그
비트 연산자를 이용해 상태를 비트로 표현합니다. 여러 상태를 동시에 표현하거나 체크해야 할 때 유용합니다.
- 장점:
- 메모리와 성능에 최적화.
- 상태를 조합하거나 중첩된 상태를 관리하기 쉬움.
- 단점:
- 상태에 대한 직관적인 이해가 어렵고, 구현 난이도가 높을 수 있음.
- 복잡한 상태 트랜지션에는 적합하지 않음.
7. FSM
유니티나 C#에는 FSM 라이브러리나 상태 전환을 쉽게 관리할 수 있는 툴들이 있습니다. 대표적으로 UniRx, Stateless 등이 있습니다.
- 장점:
- 복잡한 상태 전환 로직을 손쉽게 관리 가능.
- 재사용 가능하고 테스트하기 쉬움.
- 단점:
- 라이브러리를 이해하고 학습하는 시간이 필요함.
8. 플로우 차트
유니티에서는 Animator의 상태 머신을 활용하거나, Playmaker와 같은 시각적 스크립팅 툴을 사용해 상태를 관리할 수도 있습니다.
- 장점:
- 상태 전환이 직관적이고 시각적으로 표현됨.
- 디자이너와 협업에 적합.
- 단점:
- 복잡한 상태일수록 관리가 어려워질 수 있음.
- 추가적인 플러그인이나 툴 사용이 필요.
9. Dictionary
상태와 행동을 Dictionary(또는 해시맵)로 매핑하는 방식입니다. 상태 값(key)과 해당 행동(value)을 매핑해 동작을 결정합니다.
- 장점:
- 상태와 행동을 데이터적으로 관리 가능.
- 상태 추가가 간단.
- 단점:
- 복잡한 상태 전환 로직은 별도로 구현해야 함.
- 상태가 많아질 경우 관리가 어려울 수 있음.
5. 코드
1. enum
더보기
using UnityEngine;
public class Player : MonoBehaviour
{
// 1. Enum 정의
public enum PlayerState
{
Idle,
Running,
Jumping,
Attacking
}
// 2. 현재 상태를 저장할 변수
private PlayerState currentState;
// 3. Unity의 Start 메서드에서 초기 상태를 설정
private void Start()
{
SetState(PlayerState.Idle);
}
// 4. 상태에 따른 업데이트 처리
private void Update()
{
switch (currentState)
{
case PlayerState.Idle:
HandleIdleState();
break;
case PlayerState.Running:
HandleRunningState();
break;
case PlayerState.Jumping:
HandleJumpingState();
break;
case PlayerState.Attacking:
HandleAttackingState();
break;
}
// 예: 키 입력에 따라 상태 전환
if (Input.GetKeyDown(KeyCode.Space))
{
SetState(PlayerState.Jumping);
}
else if (Input.GetKey(KeyCode.W))
{
SetState(PlayerState.Running);
}
else if (Input.GetMouseButtonDown(0))
{
SetState(PlayerState.Attacking);
}
else if (!Input.anyKey)
{
SetState(PlayerState.Idle);
}
}
// 5. 상태 전환 메서드
private void SetState(PlayerState newState)
{
// 상태가 변할 때만 변경
if (currentState == newState) return;
currentState = newState;
Debug.Log($"State changed to: {currentState}");
}
// 6. 상태별 행동 정의
private void HandleIdleState()
{
// Idle 상태에서 해야 할 행동
}
private void HandleRunningState()
{
// Running 상태에서 해야 할 행동
}
private void HandleJumpingState()
{
// Jumping 상태에서 해야 할 행동
}
private void HandleAttackingState()
{
// Attacking 상태에서 해야 할 행동
}
}
2. 인터페이스
더보기
using UnityEngine;
// 1. 상태를 정의하는 인터페이스
public interface IPlayerState
{
void EnterState(Player player); // 상태 진입 시 호출
void UpdateState(Player player); // 매 프레임 호출
void ExitState(Player player); // 상태 종료 시 호출
}
// 2. Player 클래스 (컨텍스트)
public class Player : MonoBehaviour
{
private IPlayerState currentState; // 현재 상태
private void Start()
{
// 초기 상태 설정
SetState(new IdleState());
}
private void Update()
{
// 현재 상태의 UpdateState 호출
currentState?.UpdateState(this);
}
// 상태 전환 메서드
public void SetState(IPlayerState newState)
{
// 기존 상태의 ExitState 호출
currentState?.ExitState(this);
// 새 상태로 변경
currentState = newState;
// 새 상태의 EnterState 호출
currentState?.EnterState(this);
}
// 예시로 사용할 행동 메서드
public void Move()
{
Debug.Log("Player is moving...");
}
public void Jump()
{
Debug.Log("Player is jumping...");
}
public void Attack()
{
Debug.Log("Player is attacking...");
}
}
// 3. Idle 상태
public class IdleState : IPlayerState
{
public void EnterState(Player player)
{
Debug.Log("Entering Idle State");
}
public void UpdateState(Player player)
{
if (Input.GetKey(KeyCode.W))
{
player.SetState(new RunningState());
}
else if (Input.GetKeyDown(KeyCode.Space))
{
player.SetState(new JumpingState());
}
else if (Input.GetMouseButtonDown(0))
{
player.SetState(new AttackingState());
}
}
public void ExitState(Player player)
{
Debug.Log("Exiting Idle State");
}
}
// 4. Running 상태
public class RunningState : IPlayerState
{
public void EnterState(Player player)
{
Debug.Log("Entering Running State");
player.Move();
}
public void UpdateState(Player player)
{
if (!Input.GetKey(KeyCode.W))
{
player.SetState(new IdleState());
}
}
public void ExitState(Player player)
{
Debug.Log("Exiting Running State");
}
}
// 5. Jumping 상태
public class JumpingState : IPlayerState
{
public void EnterState(Player player)
{
Debug.Log("Entering Jumping State");
player.Jump();
}
public void UpdateState(Player player)
{
// 점프 중인 상태에서 특정 조건(착지 등)이 만족되면 Idle 상태로 전환
if (Input.GetKeyDown(KeyCode.W))
{
player.SetState(new RunningState());
}
}
public void ExitState(Player player)
{
Debug.Log("Exiting Jumping State");
}
}
// 6. Attacking 상태
public class AttackingState : IPlayerState
{
public void EnterState(Player player)
{
Debug.Log("Entering Attacking State");
player.Attack();
}
public void UpdateState(Player player)
{
// 공격이 끝나면 Idle 상태로 복귀
if (!Input.GetMouseButton(0))
{
player.SetState(new IdleState());
}
}
public void ExitState(Player player)
{
Debug.Log("Exiting Attacking State");
}
}
'유니티 > 멋쟁이사자처럼' 카테고리의 다른 글
[멋쟁이 사자처럼 부트 캠프 TIL 회고] Unity 게임 개발 3기 - 디자인 패턴 이란? (0) | 2025.01.01 |
---|---|
[멋쟁이 사자처럼 부트 캠프 TIL 회고] Unity 게임 개발 3기 4일차 오브젝트 Object (0) | 2024.11.26 |
[멋쟁이 사자처럼 부트 캠프 TIL 회고] Unity 게임 개발 3기 3일차 Unity Layout, 화면 구성 (1) | 2024.11.26 |
[멋쟁이 사자처럼 부트 캠프 TIL 회고] Unity 게임 개발 3기 2일차 Native C# 과 Unity C# (0) | 2024.11.26 |
[멋쟁이 사자처럼 부트 캠프 TIL 회고] Unity 게임 개발 3기 1일차 Unity 설치와 Visual Studio 설치 (0) | 2024.11.24 |