2-1. 유한상태기계(FSM)
유한상태기계(finite state machine)은 초기 인공지능 개발부터 사용되어져 왔다고 합니다.
하지만 저자는 앞으로도 FSM의 사용은 빈번해질 것이라고 예측하였는데요, 그 이유는 다음
과 같습니다.
- 빠르고 코딩하기가 쉽다.
- 오류수정이 용이하다.
- 계산부담이 없다.
- 직관적이다.
- 유연성이 있다.
배가 고프다 => 밥먹기 상태로 돌입한다 => 음식이 있는지 확인한다 => 음식을 먹는다 => 배가 부르다 => 밥먹기 상태에서 탈출한다.
배가 고프면 밥을 먹어야 겠지요. 따라서 밥먹기 상태로 돌입했습니다. 직관적입니다.
또한 새로운 규칙과 상태를 추가함으로써 에이전트의 행동범위를 간단하게 확장시킬 수 있으므로 유연성이 있습니다.
상태 전환표
상태전환에 영향을 미치고 상태들을 조직하는 더 좋은 방법은, 상태전환표(state transition table)을 이용하는 것 입니다. 개략적인 도표는 다음과 같습니다.
현재 상태
|
조건
|
상태전환
|
도망가기
|
안전하다
|
순찰하기
|
공격하기
|
적보다 약하다
|
도망가기
|
순찰하기
|
위협받고_적보다 강하다
|
공격하기
|
이러한 형식의 구조는 매우 유연하여 새로운 '상태'들을 추가함으로써 에이전트의 실행종목을 쉽게 확장할 수 있게 해줍니다.
프로그래머 입장에서 에이전트가 유연해지도록 도와주려면 어떻게 해야 할까요?
바로 State라는 class를 공통으로 공유, 즉 상속 하는 것 입니다.
상태 디자인 패턴
비록 상태 디자인 패턴은 수학적 FSM 형식화로부터의 이탈이지만 직관적이고 코딩하기 간단하며 확장이 용이해집니다. 구조는 다음과 같습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
using UnityEngine;
public class State<Entity_Type> : MonoBehaviour
{
public virtual void Enter(Entity_Type entity)
{
// Do something..
}
public virtual void Execute(Entity_Type entity)
{
// Do something..
}
public virtual void Exit(Entity_Type entity)
{
// Do something..
}
}
| cs |
템플릿으로 작성된 State 클래스를 에이전트의 어떠한 State의 Entity type으로도 받을 수 있습니다. 이 디자인 패턴은 유한상태기계 프로그래밍을 훨씬 쉽게 해주며 유연성을 부여합니다.
이제 각각의 에이전트는, 현재 자신의 상태를 가리키는 포인터를 사용하고 각 상태의 매니저가 상태 전이시 Enter, 그후 Execute를 Update 주기에 맞춰서 반복한 후 상태 전이시 Exit 함수를 호출한 후 상태를 전이시키게 합니다.
상태의 매니저, StateMachine class의 구조는 다음과 같습니다.
이제 각각의 에이전트는, 현재 자신의 상태를 가리키는 포인터를 사용하고 각 상태의 매니저가 상태 전이시 Enter, 그후 Execute를 Update 주기에 맞춰서 반복한 후 상태 전이시 Exit 함수를 호출한 후 상태를 전이시키게 합니다.
상태의 매니저, StateMachine class의 구조는 다음과 같습니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
|
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class StateMachine<Entity_Type>
{
// The pointer that indicate an agent who own this instance
Entity_Type m_pOwner;
State<Entity_Type> m_pCurrentState;
// Trace of last state that this agent constituted.
State<Entity_Type> m_pPreviousState;
// This state logic will be called whenever FSM is updated.
State<Entity_Type> m_pGlobalState;
public StateMachine(Entity_Type owner)
{
m_pOwner = owner;
m_pCurrentState = null;
m_pPreviousState = null;
m_pGlobalState = null;
}
// Use those method to initiate FSM.
public void SetCurrentState(State<Entity_Type> s) { m_pCurrentState = s; }
public void SetGlobalState(State<Entity_Type> s) { m_pGlobalState = s; }
public void SetPreviousState(State<Entity_Type> s) { m_pPreviousState = s; }
// Use this method to uptate FSM
public void Updating()
{
// If static state is existed, call execute method
if (m_pGlobalState)
m_pGlobalState.Execute(m_pOwner);
// Just same as now state
if (m_pCurrentState)
m_pCurrentState.Execute(m_pOwner);
}
// Chage to New state
public void ChangeState(State<Entity_Type> pNewState)
{
if (pNewState == null)
{
Debug.Log("<StateMachine::ChangeState>: trying to change to a null state");
}
// Maintain previous state
m_pPreviousState = m_pCurrentState;
// Call Exit method of Current state
m_pCurrentState.Exit(m_pOwner);
// Change to new state
m_pCurrentState = pNewState;
// Call Enter method of New state
m_pCurrentState.Enter(m_pOwner);
}
// Revert the state to Previous State
public void RevertToPreviousState()
{
ChangeState(m_pPreviousState);
}
// accessor
State<Entity_Type> CurrentState()
{
return m_pCurrentState;
}
State<Entity_Type> GlobalState()
{
return m_pGlobalState;
}
State<Entity_Type> PreviousState()
{
return m_pPreviousState;
}
// Return true when argument is same to Now state
public bool IsInstate(State<Entity_Type> st){
if (st == CurrentState()) return true;
else return false;
}
}
| cs |
Line 32 의 Updating() 메소드에서 현 상태의 Execute를 반복하게 됩니다.
Line 44 의 ChangeState() 메소드에서 Exit - Enter - Execute 구조를 볼 수 있습니다.
이상으로 FSM에 대한 포스팅을 마치겠습니다.
댓글
댓글 쓰기