-
유니티 쉽게 배우는 디자인 패턴 - 상태 패턴 / Unity Design Pattern - State Pattern유니티 2024. 8. 19. 17:22
상태패턴이란?
상태마다 동작을 수행해야 할 때 사용하는 패턴입니다.
이때 상태는 동시에 가질 수 없고 반드시 하나의 상태만 갖습니다.
Animator 메카님의 각 상태에 따라 캐릭터 애니메이션이 바꾸거나
FSM 유한상태머신에서 상태에 따라 다른 스크립트가 실행되야 할 때 용이하게 사용되고 있습니다.
상태패턴 반영 전
StateManager.cs
using UnityEngine; public enum EState { Idle, Walk } public class StateManager : MonoBehaviour { [SerializeField] EState currentState; public void TransitionTo(EState nextState) { if (currentState == nextState) return; currentState = nextState; } void Update() { switch (currentState) { case EState.Idle: Idle(); break; case EState.Walk: Walk(); break; } } void Idle() { print("Idle"); } void Walk() { print("Walk"); } }
EState enum을 만들어 가만히 있기와 걷기 상태를 만들었습니다.
TransitionTo 함수로 EState 상태를 바꿉니다.
Update에서는 switch 문으로 분기하여 상태에 맞는 함수가 발동되게 됩니다.
Test.cs
using UnityEngine; public class Test : MonoBehaviour { [SerializeField] StateManager stateManager; void Update() { EState finalState = Input.GetMouseButton(0) ? EState.Walk : EState.Idle; stateManager.TransitionTo(finalState); } }
간단하게 마우스를 누르는 동안 Walk이고 마우스를 떼면 Idle 로 트랜지션하도록 합니다.
하지만 상태 매니저에서 switch로 분기처리 하는 것은 코드가 길어지게 하는 주범으로 크기가 커지면 헷갈리게 됩니다.
상태패턴 반영 후
StateManager.cs
using System.Collections.Generic; using UnityEngine; public enum EState { Idle, Walk } public abstract class StateBase : MonoBehaviour { public EState eState; public abstract void Enter(); public abstract void Exit(); public abstract void UpdateState(); } public class StateManager : MonoBehaviour { [SerializeField] EState curState; [SerializeField] StateBase[] stateBases; Dictionary<EState, StateBase> stateDic; public void TransitionTo(EState nextState) { if (curState == nextState) return; stateDic[curState].Exit(); curState = nextState; stateDic[nextState].Enter(); } void Awake() { stateDic = new(); foreach (StateBase stateBase in stateBases) { stateDic.TryAdd(stateBase.eState, stateBase); } stateDic[curState].Enter(); } void Update() { stateDic[curState].UpdateState(); } }
IdleState.cs
using System.Collections; using System.Collections.Generic; using UnityEngine; public class IdleState : StateBase { public override void Enter() { print("IdleEnter"); } public override void Exit() { print("IdleExit"); } public override void UpdateState() { print("IdleUpdate"); } }
WalkState.cs
using System.Collections; using System.Collections.Generic; using UnityEngine; public class WalkState : StateBase { public override void Enter() { print("WalkEnter"); } public override void Exit() { print("WalkExit"); } public override void UpdateState() { print("WalkUpdate"); } }
Test는 반영 전과 동일합니다.
StateBase는 추상클래스와 추상함수 Enter, Exit, UpdateState 를 만들어
현재 상태에 들어올 때 한번, 나갈 때 한번, 매 프레임마다 계속되는 함수를 만듭니다.
StateManager는
Awake에서 stateDic 딕셔너리를 써서 EState를 키, StateBase를 값으로 캐싱합니다.
Awake에서 처음에 갖고 있는 상태로 Enter를 호출하며
TransitionTo로 다음 상태로 바꿀 때 기존 건 Exit, 다음 상태는 Enter를 호출합니다.
Update에서는 현재 상태의 클래스의 UpdateState를 매 프레임 호출합니다.
IdleState와 WalkState에는 StateBase를 상속받아 각각 로그만 찍어두었습니다.
여기에 로직에 들어가면 됩니다.
각각의 스크립트를 각각의 게임오브젝트로 만들고
StateManager에는 CurrentState에 첫 시작시 상태를 넣습니다.
또 StateBases에는 IdleState와 WalkState 게임오브젝트를 넣어둡니다.
IdleState와 WalkState에는 EState를 이름과 동일하게 맞춰줍니다.
상태가 바뀔 때 위와같은 로그가 뜨는 걸 볼 수 있습니다.
마무리
상태에 따라 다른 스크립트가 실행되는 게 매력적인 상태패턴이었습니다.
'유니티' 카테고리의 다른 글