오토마타 이론?
FSM(finite-state-machine)은 상태(state)를 기반으로 동작을 제어하는 방식을 구현하기 위한 디자인 패턴이다.
다시 말해, 유한 상태 머신은 주어지는 모든 시간에서 처해 있을 수 있는 유한개의 상태를 가지고 주어지는 입력에 따라
어떤 상태에서 다른 상태로 전환하거나 출력이나 액션이 일어나게 하는 장치 또는 그런 장치를 나타낸 모델이다.
보통 게임에선 AI에 많이 사용된다.
특징?
상태(State): 유한 상태 머신은 여러 상태로 이루어져 있다. 각 상태는 시스템이나 프로그램이 가질 수 있는 특정 상황을 나타낸다.
전이(Transition): 상태 간의 전이는 입력 조건에 의해 발생한다. 특정 상태에서 특정 입력이 들어오면, 시스템은 다른 상태로 전이된다.
입력(Input): 시스템은 외부 입력에 반응하여 상태를 변경한다. 이 입력은 주로 특정 조건이나 이벤트에 의해 발생한다.
출력(Output): 각 상태나 전이에 따라 시스템은 특정 출력을 생성하거나 특정 동작을 수행할 수 있다.
순차적 동작: 유한 상태 머신은 상태 간의 전이를 통해 일련의 동작을 수행하는데, 이는 특정 순서대로 진행된다.
장단점?
장점
- AI 개념을 프로그래머 외에 기획자 또는 제 3자가 쉽게 확인/ 설계할 수 있다.
- 직관적이다
단점
- 확장이 힘들다 (FSM 상태를 계속 추가하다 보면 다시 연결하기가 머리 아프다)
너무 복잡할 경우 행동트리(상태를 나무 구조로 표현하여, 각 노드가 특정 동작을 수행하도록 설계된다) 로 구현이 가능하다

그동안 여러번 해볼 수 밖에 없엇던 유니티의 애니메이터 트랜지션의 원리와 똑같아서 이해하기가 쉬웠다
튜토리얼 기능 구현에 적용
튜토리얼이 실행되는 순서에 따라 절차적으로 스크립팅을 하다보면 중간에 어떤 기능을 추가하려다 오류가 발생하기 쉬우므로
FSM기반의 설계로 TutorialController에서 하위 튜토리얼 행동(ex) 만화 재생, 튜토리얼 대화창, 특정 행동 수행 요구) 들을 제어하도록 설계했다
이론 공부와 동시에 현재 진행중인 프로젝트에 한번 적용을 해보는 것이 목표라 최대한 간단하게 구현하려고 노력했다
먼저 작은 단위들의 튜토리얼 행동들은 아래 TutorialBase를 상속받고 각자의 Enter, Execute, Exit메서드를 구현하여 상태에 따른 동작을 정의하도록 한다.
public abstract class TutorialBase : MonoBehaviour
{
public abstract void Enter();
public abstract void Execute(TutorialController controller);
public abstract void Exit();
}
<TutorialController.cs>
일반적인 유한상태머신 설계처럼 n가지의 상태가 대기상태에서 플레이어의 행동에 따라 상태의 진입-진행-탈출이 수행되는 것은 아니다.
리스트에 저장된 튜토리얼 행동의 순서대로 순차적으로 진행되지만 골격은 동일하다.
각 행동이 독립적으로 정의되어 있어 순서만 바꿔주면 원하는 시점에 원하는 기능을 추가할 수 있다. 유지보수 용이
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class TutorialController : MonoBehaviour
{
//튜토리얼 행동들을 저장
[SerializeField] private List<TutorialBase> tutorials;
private TutorialBase currentTutorial = null;
private int currentIndex = -1;
public static bool TutorialCompleted = false;
public void Init()
{
//튜토리얼을 한번도 진행하지 않았을 경우에만 진행
if (!GetTutorialCompleted())
{
InGameManager.Instance.PauseGame();
SetNextTutorial();
}
}
public void SetNextTutorial()
{
//현재 튜토리얼의 Exit() 호출
if(currentTutorial != null)
{
currentTutorial.Exit();
}
//마지막 튜토리얼을 진행했다면 CompletedAllTutorials()호출
if (currentIndex >= tutorials.Count -1)
{
CompletedAllTutorials();
return;
}
//다음 튜토리얼 과정을 currentTutorial로 등록 후 Enter()
currentIndex++;
currentTutorial = tutorials[currentIndex];
currentTutorial.Enter();
}
public void Update()
{
if(currentTutorial !=null)
{
currentTutorial.Execute(this);
}
}
public void CompletedAllTutorials()
{
currentTutorial = null;
Debug.Log("All Tutorials Done");
SetTutorialCompleted(true);
InGameManager.Instance.ResumeGame();
}
#region TutorialCompleted
public bool GetTutorialCompleted()
{
return JsonLoader.Load("TutorialController.TutorialCompleted", TutorialCompleted);
}
public static void SetTutorialCompleted(bool tutorialCompleted )
{
TutorialCompleted = tutorialCompleted;
JsonLoader.Save("TutorialController.TutorialCompleted", TutorialCompleted);
}
#endregion
}
tutorials[currentIndex] 의 튜토리얼 행동들은 Enter() -> 매 프레임마다 Execute() -> 탈출 조건에 맞을 경우 Exit()을 수행하고 currentIndex를 증가시켜 다음 행동들이 수행된다

만화 튜토리얼 행동 예시
<TutorialCartoon.cs>
using System.Collections;
using PokerDefense;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.SceneManagement;
public class TutorialCartoon : TutorialBase
{
public GameObject mainObj;
public Image comicPanel;
public Sprite[] comicPages;
private Button nextPageBtn;
public float fadeDuration = 10f;
private int currentPage = 0;
private bool isProceeding = false;
public override void Enter()
{
Debug.Log("StartCartoon");
isProceeding = true;
mainObj.SetActive(isProceeding);
InGameManager.Instance.PauseGame();
nextPageBtn = comicPanel.GetComponent<Button>();
ShowPage(currentPage);
}
public override void Execute(TutorialController controller)
{
if (!isProceeding)
{
//StartCoroutine(FadeInFadeOut.Instance.FadeOut(comicPanel, fadeDuration));
controller.SetNextTutorial();
}
}
void ShowPage(int pageIndex)
{
StartCoroutine(FadeInFadeOut.Instance.FadeIn(comicPanel, nextPageBtn, fadeDuration));
comicPanel.sprite = comicPages[pageIndex];
}
public void NextPage() //만화 누르면 호출
{
if (currentPage < comicPages.Length - 1)
{
currentPage++;
StartCoroutine(FadeInFadeOut.Instance.FadeOut(comicPanel, nextPageBtn, fadeDuration));
ShowPage(currentPage);
}
else
{
StartCoroutine(EndCartoon());
}
}
public IEnumerator EndCartoon()
{
yield return StartCoroutine(FadeInFadeOut.Instance.FadeOut(comicPanel, fadeDuration + 1));
isProceeding = false;
mainObj.SetActive(isProceeding);
}
public override void Exit()
{
Debug.Log("Comic Ended");
}
}
이 외의 행동들도 같은 방식으로 구현된다
https://dodobug.tistory.com/16
[Unity] FSM - 유한 상태 기계
FSM(finite-state machine)은 상태(state)를 기반으로 동작을 제어하는 방식을 구현하기 위한 디자인 패턴이다. FSM의 핵심은 단 하나의 상태만을 가진다는 점이다. 상태를 기준으로 어떤 동작을 수행할지
dodobug.tistory.com
참고
오토마타 이론?
FSM(finite-state-machine)은 상태(state)를 기반으로 동작을 제어하는 방식을 구현하기 위한 디자인 패턴이다.
다시 말해, 유한 상태 머신은 주어지는 모든 시간에서 처해 있을 수 있는 유한개의 상태를 가지고 주어지는 입력에 따라
어떤 상태에서 다른 상태로 전환하거나 출력이나 액션이 일어나게 하는 장치 또는 그런 장치를 나타낸 모델이다.
보통 게임에선 AI에 많이 사용된다.
특징?
상태(State): 유한 상태 머신은 여러 상태로 이루어져 있다. 각 상태는 시스템이나 프로그램이 가질 수 있는 특정 상황을 나타낸다.
전이(Transition): 상태 간의 전이는 입력 조건에 의해 발생한다. 특정 상태에서 특정 입력이 들어오면, 시스템은 다른 상태로 전이된다.
입력(Input): 시스템은 외부 입력에 반응하여 상태를 변경한다. 이 입력은 주로 특정 조건이나 이벤트에 의해 발생한다.
출력(Output): 각 상태나 전이에 따라 시스템은 특정 출력을 생성하거나 특정 동작을 수행할 수 있다.
순차적 동작: 유한 상태 머신은 상태 간의 전이를 통해 일련의 동작을 수행하는데, 이는 특정 순서대로 진행된다.
장단점?
장점
- AI 개념을 프로그래머 외에 기획자 또는 제 3자가 쉽게 확인/ 설계할 수 있다.
- 직관적이다
단점
- 확장이 힘들다 (FSM 상태를 계속 추가하다 보면 다시 연결하기가 머리 아프다)
너무 복잡할 경우 행동트리(상태를 나무 구조로 표현하여, 각 노드가 특정 동작을 수행하도록 설계된다) 로 구현이 가능하다

그동안 여러번 해볼 수 밖에 없엇던 유니티의 애니메이터 트랜지션의 원리와 똑같아서 이해하기가 쉬웠다
튜토리얼 기능 구현에 적용
튜토리얼이 실행되는 순서에 따라 절차적으로 스크립팅을 하다보면 중간에 어떤 기능을 추가하려다 오류가 발생하기 쉬우므로
FSM기반의 설계로 TutorialController에서 하위 튜토리얼 행동(ex) 만화 재생, 튜토리얼 대화창, 특정 행동 수행 요구) 들을 제어하도록 설계했다
이론 공부와 동시에 현재 진행중인 프로젝트에 한번 적용을 해보는 것이 목표라 최대한 간단하게 구현하려고 노력했다
먼저 작은 단위들의 튜토리얼 행동들은 아래 TutorialBase를 상속받고 각자의 Enter, Execute, Exit메서드를 구현하여 상태에 따른 동작을 정의하도록 한다.
public abstract class TutorialBase : MonoBehaviour
{
public abstract void Enter();
public abstract void Execute(TutorialController controller);
public abstract void Exit();
}
<TutorialController.cs>
일반적인 유한상태머신 설계처럼 n가지의 상태가 대기상태에서 플레이어의 행동에 따라 상태의 진입-진행-탈출이 수행되는 것은 아니다.
리스트에 저장된 튜토리얼 행동의 순서대로 순차적으로 진행되지만 골격은 동일하다.
각 행동이 독립적으로 정의되어 있어 순서만 바꿔주면 원하는 시점에 원하는 기능을 추가할 수 있다. 유지보수 용이
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class TutorialController : MonoBehaviour
{
//튜토리얼 행동들을 저장
[SerializeField] private List<TutorialBase> tutorials;
private TutorialBase currentTutorial = null;
private int currentIndex = -1;
public static bool TutorialCompleted = false;
public void Init()
{
//튜토리얼을 한번도 진행하지 않았을 경우에만 진행
if (!GetTutorialCompleted())
{
InGameManager.Instance.PauseGame();
SetNextTutorial();
}
}
public void SetNextTutorial()
{
//현재 튜토리얼의 Exit() 호출
if(currentTutorial != null)
{
currentTutorial.Exit();
}
//마지막 튜토리얼을 진행했다면 CompletedAllTutorials()호출
if (currentIndex >= tutorials.Count -1)
{
CompletedAllTutorials();
return;
}
//다음 튜토리얼 과정을 currentTutorial로 등록 후 Enter()
currentIndex++;
currentTutorial = tutorials[currentIndex];
currentTutorial.Enter();
}
public void Update()
{
if(currentTutorial !=null)
{
currentTutorial.Execute(this);
}
}
public void CompletedAllTutorials()
{
currentTutorial = null;
Debug.Log("All Tutorials Done");
SetTutorialCompleted(true);
InGameManager.Instance.ResumeGame();
}
#region TutorialCompleted
public bool GetTutorialCompleted()
{
return JsonLoader.Load("TutorialController.TutorialCompleted", TutorialCompleted);
}
public static void SetTutorialCompleted(bool tutorialCompleted )
{
TutorialCompleted = tutorialCompleted;
JsonLoader.Save("TutorialController.TutorialCompleted", TutorialCompleted);
}
#endregion
}
tutorials[currentIndex] 의 튜토리얼 행동들은 Enter() -> 매 프레임마다 Execute() -> 탈출 조건에 맞을 경우 Exit()을 수행하고 currentIndex를 증가시켜 다음 행동들이 수행된다

만화 튜토리얼 행동 예시
<TutorialCartoon.cs>
using System.Collections;
using PokerDefense;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.SceneManagement;
public class TutorialCartoon : TutorialBase
{
public GameObject mainObj;
public Image comicPanel;
public Sprite[] comicPages;
private Button nextPageBtn;
public float fadeDuration = 10f;
private int currentPage = 0;
private bool isProceeding = false;
public override void Enter()
{
Debug.Log("StartCartoon");
isProceeding = true;
mainObj.SetActive(isProceeding);
InGameManager.Instance.PauseGame();
nextPageBtn = comicPanel.GetComponent<Button>();
ShowPage(currentPage);
}
public override void Execute(TutorialController controller)
{
if (!isProceeding)
{
//StartCoroutine(FadeInFadeOut.Instance.FadeOut(comicPanel, fadeDuration));
controller.SetNextTutorial();
}
}
void ShowPage(int pageIndex)
{
StartCoroutine(FadeInFadeOut.Instance.FadeIn(comicPanel, nextPageBtn, fadeDuration));
comicPanel.sprite = comicPages[pageIndex];
}
public void NextPage() //만화 누르면 호출
{
if (currentPage < comicPages.Length - 1)
{
currentPage++;
StartCoroutine(FadeInFadeOut.Instance.FadeOut(comicPanel, nextPageBtn, fadeDuration));
ShowPage(currentPage);
}
else
{
StartCoroutine(EndCartoon());
}
}
public IEnumerator EndCartoon()
{
yield return StartCoroutine(FadeInFadeOut.Instance.FadeOut(comicPanel, fadeDuration + 1));
isProceeding = false;
mainObj.SetActive(isProceeding);
}
public override void Exit()
{
Debug.Log("Comic Ended");
}
}
이 외의 행동들도 같은 방식으로 구현된다
https://dodobug.tistory.com/16
[Unity] FSM - 유한 상태 기계
FSM(finite-state machine)은 상태(state)를 기반으로 동작을 제어하는 방식을 구현하기 위한 디자인 패턴이다. FSM의 핵심은 단 하나의 상태만을 가진다는 점이다. 상태를 기준으로 어떤 동작을 수행할지
dodobug.tistory.com
참고