본문 바로가기

유니티/디자인 패턴

[Unity] 상태 패턴 (State Pattern)

반응형

상태 패턴이란?

상태 패턴은 객체의 상태에 따라 그에 따른 행동을 달리하는 상황에서, 상태를 객체화하여 상태가 행동을 할 수 있도록 위임하는 패턴이다.

 

이 디자인 패턴을 활용하면 캐릭터의 상태에 따른 행동을 SOLID원칙을 고수하며 작성할 수 있다.

 

상태 패턴 구조


왜 사용하는 것일까?

아래는 상태 패턴의 장단점을 정리한 것이다.

장점

  1. 상태의 개수를 변경하더라도 기존의 코드 수정 없이 확장하거나 줄일 수 있다.
  2. 상태 전이에 따른 로직 변경이 쉽다.
  3. 캡슐화 하여 동적으로 할당이 가능하다.

단점

  1. 애니메이션 블렌딩이 어렵다. 따라서 CrossFade를 사용하거나 Behavior Tree를 사용해야 한다.
  2. 상태 간의 전환에 관한 로직은 별도로 구현해야 한다. FSM(유한 상태 머신)을 사용해도 된다.

정리하자면 어떠한 객체(특히 캐릭터)의 상태를 나타낼 때 유지보수를 고려하여 작성하기 좋은 방법 중 하나이다.


어떻게 사용할 수 있을까?

상태 패턴은 다음 세 가지의 요소를 중점으로 작성해야 한다.

  • Context : 객체의 상태를 저장하고 행동을 정의한다.
  • IState : 구체 상태 클래스로 연결할 수 있는 인터페이스이다.
  • ConcreteState : 클래스에 의해 인스턴스화되는 상태를 구현한 클래스이다.

 

아래는 캐릭터의 상태(이동, 공격, 피격, 죽음)를 상태 패턴으로 구현한 예시이다.

 

먼저 인터페이스를 구현한다.

public interface ICharacterState
{
    void Convert(CharacterController controller);
}

 

그리고 상태를 담을 수 있는 Context를 구현한다.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class StateController : MonoBehaviour
{
    public ICharacterState Current
    {
        get; private set;
    }

    private readonly CharacterController characterController;

    public StateController(CharacterController characterController)
    {
        this.characterController = characterController;
    }

    public void ChangeState(ICharacterState state)
    {
        if (state == Current)
            return;

        Current = state;
        Current.Convert(characterController);
    }
}

 

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class CharacterController : MonoBehaviour
{
    public int id;
    public string characterName;
    public int moveSpeed;
    public int hp;
    public int damage;
    public int cost;
    public float spawnTime;
    public Animator animator { get; private set; }

    private StateController controller;
    private ICharacterState move, attack, hit, dead;

    void Awake()
    {
        move = new MoveState();
        attack = new AttackState();
        hit = new HitState();
        dead = new DeadState();
    }

    public void Attak()
    {
        controller.ChangeState(attack);
    }

    public void Move()
    {
        controller.ChangeState(move);
    }

    public void Hit()
    {
        controller.ChangeState(hit);
    }

    public void Dead()
    {
        controller.ChangeState(dead);
    }

    void Update()
    {
        
    }
}

 

마지막으로 각 상태를 클래스로 정의한다.

 

Move 클래스

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class MoveState : ICharacterState
{
    public void Convert(CharacterController controller)
    {
        throw new System.NotImplementedException();
    }
}

 

Attack 클래스

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class AttackState : ICharacterState
{
    public void Convert(CharacterController controller)
    {
        controller.animator.SetTrigger("Attack");
    }
}

 

Hit 클래스

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class HitState : ICharacterState
{
    public void Convert(CharacterController controller)
    {
        throw new System.NotImplementedException();
    }
}

 

Dead 클래스

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class DeadState : ICharacterState
{
    public void Convert(CharacterController controller)
    {
        throw new System.NotImplementedException();
    }
}

정리

상태 패턴은 캐릭터의 상태 변경을 유지보수와 확장을 편리한 방식으로 사용할 수 있도록 설계된 디자인 패턴이다.

단독으로 사용할 경우 상태 전환에 대한 문제점이 있으므로 FSM등과 함께 사용하여 이용한다.

반응형