๋ฐ˜์‘ํ˜•

๐Ÿ”„ Unity ๋””์ž์ธ ํŒจํ„ด - ์ƒํƒœ ํŒจํ„ด(State Pattern) vs FSM

1. ์ƒํƒœ๋ฅผ ๊ด€๋ฆฌํ•˜๋Š” ๋‘ ๊ฐ€์ง€ ๋ฐฉ์‹

๊ฒŒ์ž„ ์บ๋ฆญํ„ฐ์˜ ํ–‰๋™์ด๋‚˜ UI์˜ ์ „ํ™˜์ฒ˜๋Ÿผ ์ƒํƒœ์— ๋”ฐ๋ผ ๋™์ž‘์ด ๋‹ฌ๋ผ์ ธ์•ผ ํ•  ๋•Œ, ์šฐ๋ฆฌ๋Š” ๋‹ค์Œ ๋‘ ๊ฐ€์ง€ ์ ‘๊ทผ ๋ฐฉ์‹์„ ๊ณ ๋ คํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค:

  • FSM (Finite State Machine): ์ „ํ†ต์ ์ธ ์กฐ๊ฑด ๋ถ„๊ธฐ ๊ธฐ๋ฐ˜ ๊ตฌ์กฐ
  • ์ƒํƒœ ํŒจํ„ด (State Pattern): ๊ฐ์ฒด์ง€ํ–ฅ ์›์น™์„ ์ ์šฉํ•œ ํ™•์žฅ ๊ฐ€๋Šฅํ•œ ๊ตฌ์กฐ

2. FSM์ด๋ž€?

FSM(Finite State Machine)์€ ํ•˜๋‚˜์˜ ์ƒํƒœ๋งŒ ์œ ์ง€ํ•˜๋ฉฐ, ์กฐ๊ฑด์— ๋”ฐ๋ผ ๋‹ค๋ฅธ ์ƒํƒœ๋กœ ์ „์ด๋˜๋Š” ๋ฐฉ์‹์ž…๋‹ˆ๋‹ค.

๐Ÿ“ฆ ํŠน์ง•

  • enum, switch-case ๊ตฌ์กฐ๋กœ ๋‹จ์ˆœ ๊ตฌํ˜„ ๊ฐ€๋Šฅ
  • ๋น ๋ฅด๊ณ  ์ง๊ด€์ ์ธ ๋กœ์ง ๊ตฌ์„ฑ์— ์œ ๋ฆฌ

โœ… ์˜ˆ์ œ ์ฝ”๋“œ (Unity ๊ธฐ์ค€)

public enum PlayerState { Idle, Move, Jump }

public class PlayerFSM : MonoBehaviour
{
    private PlayerState state = PlayerState.Idle;

    void Update()
    {
        switch (state)
        {
            case PlayerState.Idle:
                if (Input.GetKeyDown(KeyCode.W))
                    state = PlayerState.Move;
                break;
            case PlayerState.Move:
                if (Input.GetKeyDown(KeyCode.Space))
                    state = PlayerState.Jump;
                break;
            case PlayerState.Jump:
                if (IsGrounded())
                    state = PlayerState.Idle;
                break;
        }
    }

    private bool IsGrounded() => true; // ์ฐฉ์ง€ ํŒ์ •์€ ์ƒ๋žต
}

๐Ÿ‘ ์žฅ์ 

  • ๊ตฌํ˜„์ด ๊ฐ„๋‹จํ•˜๋‹ค
  • ๋กœ์ง ํ๋ฆ„์ด ๋ˆˆ์— ์ž˜ ๋ณด์ธ๋‹ค

๐Ÿ‘Ž ๋‹จ์ 

  • ์ƒํƒœ๊ฐ€ ๋งŽ์•„์งˆ์ˆ˜๋ก switch ๋ฌธ์ด ๊ธธ์–ด์ง
  • ์ƒํƒœ๋ณ„ ๋กœ์ง์ด ์„ž์ด๊ธฐ ์‰ฌ์›€ → ์œ ์ง€๋ณด์ˆ˜ ์–ด๋ ค์›€

3. ์ƒํƒœ ํŒจํ„ด์ด๋ž€?

์ƒํƒœ ํŒจํ„ด์€ ๊ฐ ์ƒํƒœ๋ฅผ ๋…๋ฆฝ๋œ ํด๋ž˜์Šค๋กœ ๋ถ„๋ฆฌํ•˜์—ฌ, ์ƒํƒœ ์ „ํ™˜๊ณผ ์‹คํ–‰ ๋กœ์ง์„ ์บก์Аํ™”ํ•˜๋Š” ๊ฐ์ฒด์ง€ํ–ฅ ๋ฐฉ์‹์ž…๋‹ˆ๋‹ค.

๐Ÿ“ฆ ๊ตฌ์กฐ

  • ๊ณตํ†ต ์ธํ„ฐํŽ˜์ด์Šค IState
  • ๊ฐ๊ฐ์˜ ์ƒํƒœ ํด๋ž˜์Šค (IdleState, MoveState, JumpState ๋“ฑ)
  • ์ƒํƒœ๋ฅผ ๊ด€๋ฆฌํ•˜๋Š” ์ปจํ…์ŠคํŠธ (PlayerStateMachine ๋“ฑ)

โœ… ์˜ˆ์ œ ์ฝ”๋“œ

public interface IState
{
    void Enter();
    void Execute();
    void Exit();
}

public class IdleState : IState
{
    public void Enter() => Debug.Log("Idle ์ง„์ž…");
    public void Execute() => Debug.Log("๋Œ€๊ธฐ ์ค‘...");
    public void Exit() => Debug.Log("Idle ์ข…๋ฃŒ");
}

public class MoveState : IState
{
    public void Enter() => Debug.Log("์ด๋™ ์‹œ์ž‘");
    public void Execute() => Debug.Log("์ด๋™ ์ค‘...");
    public void Exit() => Debug.Log("์ด๋™ ์ข…๋ฃŒ");
}

public class PlayerStateMachine : MonoBehaviour
{
    private IState currentState;

    void Start()
    {
        ChangeState(new IdleState());
    }

    void Update()
    {
        currentState?.Execute();

        // ์ž„์‹œ ์˜ˆ์ œ ์ „ํ™˜ ๋กœ์ง
        if (Input.GetKeyDown(KeyCode.W)) ChangeState(new MoveState());
    }

    public void ChangeState(IState newState)
    {
        currentState?.Exit();
        currentState = newState;
        currentState.Enter();
    }
}

๐Ÿ‘ ์žฅ์ 

  • ์ƒํƒœ๋ณ„ ๋กœ์ง์ด ๋ถ„๋ฆฌ๋˜์–ด ๊น”๋”ํ•จ
  • ์ƒˆ๋กœ์šด ์ƒํƒœ๋ฅผ ํด๋ž˜์Šค ์ถ”๊ฐ€๋งŒ์œผ๋กœ ํ™•์žฅ ๊ฐ€๋Šฅ
  • ์ƒํƒœ ์ „์ด๊ฐ€ ๋‚ด๋ถ€์ ์œผ๋กœ ์ฒ˜๋ฆฌ๋˜์–ด ์œ ์—ฐํ•จ

๐Ÿ‘Ž ๋‹จ์ 

  • ์ฒ˜์Œ ๊ตฌ์„ฑํ•  ๋•Œ ์ฝ”๋“œ ์–‘์ด ๋งŽ์•„ ๋ณด์ž„
  • ์ž‘์€ ๊ทœ๋ชจ์˜ FSM์—๋Š” ์˜คํžˆ๋ ค ๊ณผํ•  ์ˆ˜ ์žˆ์Œ

4. FSM vs ์ƒํƒœ ํŒจํ„ด ๋น„๊ต

ํ•ญ๋ชฉ FSM ์ƒํƒœ ํŒจํ„ด
๊ตฌํ˜„ ๋‚œ์ด๋„ ๋‚ฎ์Œ ์ค‘๊ฐ„ ์ด์ƒ
์ฝ”๋“œ ๊ตฌ์กฐ ์ ˆ์ฐจ์  ๊ฐ์ฒด์ง€ํ–ฅ์ 
ํ™•์žฅ์„ฑ ๋‚ฎ์Œ ๋†’์Œ
ํ…Œ์ŠคํŠธ ์šฉ์ด์„ฑ ๋‚ฎ์Œ ๋†’์Œ
๊ถŒ์žฅ ๋Œ€์ƒ ๊ฐ„๋‹จํ•œ ์ƒํƒœ ์ „์ด ์ƒํƒœ๊ฐ€ ๋งŽ๊ฑฐ๋‚˜ ๋ณต์žกํ•œ ์‹œ์Šคํ…œ

โœ… ๋งˆ๋ฌด๋ฆฌ ํ•œ ์ค„ ์š”์•ฝ

FSM์€ ๋น ๋ฅด๊ฒŒ ์‹œ์ž‘ํ•  ์ˆ˜ ์žˆ๋Š” ๋„๊ตฌ, ์ƒํƒœ ํŒจํ„ด์€ ๊ฒฌ๊ณ ํ•œ ๊ตฌ์กฐ๋ฅผ ์œ„ํ•œ ์„ค๊ณ„์ž…๋‹ˆ๋‹ค.

ํ”„๋กœํ† ํƒ€์ž… → FSM → ๋ณต์žกํ•ด์งˆ์ˆ˜๋ก ์ƒํƒœ ํŒจํ„ด์œผ๋กœ ๋ฆฌํŒฉํ† ๋งํ•˜๋Š” ํ๋ฆ„์ด ์ด์ƒ์ ์ž…๋‹ˆ๋‹ค.

๋ฐ˜์‘ํ˜•