SendMessage로 FSM 구현해보기 (Unity3D)
# Introduce
FSM을 구현하기 위해 State Pattern을 다뤄보던 중,
SendMessage를 이용하여 기존 사용하던 FSM을 더 가독성 좋게 구현하게 되어서 공유하려 합니다.
FSM?
FSM 은 유한 상태 기계(Finite State Machine)를 의미합니다.
유한 상태 기계는 말 그대로 유한한 수의 상태를 가진 기계를 이야기 하는데요.
다음과 같은 특징을 지닙니다.
- 유한 상태 기계는 유한한 수의 상태들을 가진다.
- 그 중 반드시 하나의 상태만 취한다.
- 현재 상태는 특정 조건이 되면 다른 상태로 변할 수 있다.
- 유한 상태 기계는 가능한 상태들의 집합과 각 상태들의 전이 조건으로 정의될 수 있다.
- 상태들을 그래프로 표현이 가능하다.
# Pros & Cons
장점
- 구현이 간단하다.
- 직관적이다.
- 원하는 함수들만 구현할 수 있다.
단점
- 규모가 커진다면 설계가 복잡해진다.
- 구조가 단순한 만큼 예측이 가능하다.
- SendMessage의 비용
장점은 매우 간단합니다.
기본적인 FSM의 장점들을 모두 가져오며 추가로 원하는 항목의 함수들만 구현하면 되기 때문에 코드 가독성은 더 좋아지게 됩니다.
단점으로는 기본적인 FSM의 단점들과 추가로 SendMessage의 비용이 부담되는데,
유니티의 SendMessage는 런타임 중 리플렉션을 통해 함수 호출하는 게 아니라 함수명을 String 형태로 Map으로 관리하기 때문에
일반적으로 아는 SendMessage만큼 느리진 않다고는 하지만
어찌되었든 직접 클래스 내에서 호출 하는 것 보다는 무거울 것이 확실하기 때문입니다.
# For example
#region FSM_Declare
// FSM 선언부
private bool _isNewState; // 상태가 변해야 하는지 알려주는 변수
private enum STATE { Idle, Chase, Flee } // 상태들 선언. 해당 상태들은 상태명 뒤에 Enter,Update,Exit을 붙여 함수화됩니다.
private STATE _State = STATE.Idle; // 현재 상태 변수
// 상태를 변경할 때 호출합니다.
private void SetState(STATE state)
{
_isNewState = true;
_State = state;
}
// FSM을 루프시키는 이터레이터입니다.
private IEnumerator FSM()
{
while (true)
{
_isNewState = false;
yield return StartCoroutine(FSM_State(_State));
}
}
// 현재 상태의 함수를 호출해주는 이터레이터입니다. (ex. IdleEnter(), IdleUpdate(), IdleExit())
private IEnumerator FSM_State(STATE state)
{
SendMessage(string.Format("{0}Enter", state), SendMessageOptions.DontRequireReceiver);
do
{
yield return null;
if (_isNewState)
{
SendMessage(string.Format("{0}Exit", state), SendMessageOptions.DontRequireReceiver);
yield break;
}
SendMessage(string.Format("{0}Update", state), SendMessageOptions.DontRequireReceiver);
} while (!_isNewState);
SendMessage(string.Format("{0}Exit", state), SendMessageOptions.DontRequireReceiver);
}
#endregion
FSM에 꼭 필요한 변수와 함수들을 선언하는 구역입니다.
SetState() 함수를 통해 상태 변경을 할 수 있습니다.
상태가 변할때마다 FSM() 에서 FSM_State() 코루틴을 실행하게 되고,
FSM_State() 에서 현재 상태의 Enter, Update, Exit 함수를 SendMessage를 통해 호출합니다.
#region FSM_Implement
// FSM 구현부. 상태명 + Enter, Update, Exit 으로 구현합니다.
// 필요한 경우에만 구현하면 됩니다. 필요하지 않다면 함수조차 구현하지 않아도 무관합니다.
private void IdleEnter()
{
Debug.Log("Idle Start");
}
private void IdleUpdate()
{
Debug.Log("Idle Update");
}
private void IdleExit()
{
Debug.Log("Idle Start");
}
#endregion
FSM의 상태를 구현하는 구역입니다.
SendMessage(DontRequireReceiver)를 통해 호출 하기 때문에 필요한 상태의 필요한 함수만 구현할 수 있습니다.
예를 들어, IdleUpdate() 함수는 필요하지만 IdleEnter(), IdleExit()이 필요하지 않다면 구현하지 않아도 괜찮습니다.
단, 위에서 선언한 열거형 STATE의 상태들과 동일한 이름을 사용해야 합니다.
# Epilogue
FSM을 코루틴과 SendMessage로 최대한 간단히 구현해 보았습니다.
간단한 AI나 상태 패턴을 구현할 때 도움이 되셨으면 좋겠습니다.
감사합니다.
# Refrences
'개발 > Design Pattern' 카테고리의 다른 글
Command Pattern (0) | 2022.01.24 |
---|---|
Singleton (0) | 2022.01.14 |