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에 대한 포스팅을 마치겠습니다.

댓글
댓글 쓰기