개발블로그
[유니티 C#] 유한 상태머신(Finite State Machine, FSM) 본문
💪 GOAL
- 유한 상태머신의 뜻을 이해한다.
- 유한 상태머신을 이용한 캐릭터 애니메이션을 구현한다.
게임을 개발할 때 '유한 상태머신(FSM)'이라는 말을 한번쯤은 들어봤을 것이다.
유한 상태머신은 유한한 수의 상태가 존재하며, 한 번에 한 상태만 '현재 상태'가 되도록 프로그램을 설계하는 모델이다.
유한 상태 머신에서는 어떤 상태에서 다른 상태로 전이하여 현재 상태를 전환할 수 있다.
가장 기본적인 예로 게임의 Monster AI를 유한 상태머신으로 구현한다고 가정해보자.
가장 기본적으로 몬스터는 Idle(대기), Trace(추적), Attack(공격) 로 세가지 상태를 가질 수 있다.
화면에서 대기하고 있다가, 플레이어(공격대상)가 몬스터의 시야에 들어오면 추적하고, 사정거리에 있다면 공격한다.
만약 시야에서 벗어나면 다시 대기상태로 변화하고, 사정거리에 들어오면 공격하는 것으로 상태머신을 구현할 수 있다.
🔶 애니메이터(Animator)
인공지능 몬스터 AI를 만든다고 가정했을 때, 애니메이터에서 애니메이션을 추가해서 각각 연결해준다.
몬스터는 처음엔 대기상태였다가 시야에 공격 대상이 들어오면 공격 대상을 향해 걸어간다. 그리고 공격을 당하면 죽는다.
이 때, 애니메이션 상태는 Make Transition으로 이어져있어야 상황에 따라 애니메이션이 전환 된다.
Die 라는 이름의 Trigger 파라미터를 생성 후, AnyState에서 Die로 가는 Transition에 Die 파라미터를 Conditions으로 추가했다.
이렇게만 해서는 몬스터는 죽음 상태로 가지않는다.
스크립트 작업이 필요하다.
using Photon.Pun;
using System;
using UnityEngine;
public class Entity : MonoBehaviourPun, IDamageable
{
public bool dead { get; protected set; }
public event Action onDeath;
[PunRPC]
public virtual void OnDamage(float damage, Vector3 hitPoint, Vector3 hitNormal)
{
if(PhotonNetwork.IsMasterClient)
{
health -= damage;
photonView.RPC("ApplyUpdateHealth", RpcTarget.Others, health, dead); // 호스트에서 클라이언트로 동기화
photonView.RPC("OnDamage", RpcTarget.Others, damage, hitPoint, hitNormal);
}
if(health <= 0 && !dead)
{
Die();
}
}
public virtual void Die()
{
if(onDeath != null)
{
onDeath();
}
dead = true;
}
}
using System.Collections;
using UnityEngine;
using UnityEngine.AI;
public class Monster : Entity
{
NavMeshAgent pathFinder;
public override void Die()
{
base.Die();
Collider[] MonsterColliders = GetComponents<Collider>();
for(int i = 0; i < MonsterColliders.Length; i++)
{
MonsterColliders[i].enabled = false;
}
pathFinder.isStopped = true;
pathFinder.enabled = false;
MonsterAnim.SetTrigger("Die");
MonsterAudioPlayer.PlayOneShot(deathSound);
}
}
-현재 설명에서 필요없는 부분은 생략하고 코드 일부만 가져옴-
Entity 클래스에서 OnDamage함수에서 Damage를 입었을 때,
만약 health 가 0보다 작거나 같고, dead가 true이면 Die 메소드를 실행한다.
Monster클래스에서 Die메소드에서 몬스터의 활성화된 모든 콜라이더를 해제시키고
몬스터의 NavMeshAgent를 멈추고, 해제시킨 뒤
MonsterAnim.SetTrigger("Die"); 로 상태를 전이 시킨다.
이렇게 되면 몬스터의 애니메이션은 공격을 받은 후 체력이 0이 되어 죽은 상태가 true가 되면 Die 상태로 변경된다.
다른 방법으로는 enum문을 사용하는 방법이 있다.
enum SKILLSTATE
{
READY,
CREATE,
LAUNCH,
ARRIVED,
DESTROY,
DEACTIVATE
}
public class SkillController : MonoBehaviour
{
[SerializeField] List<Skill> skills;
SKILLSTATE skillState = SKILLSTATE.READY;
void ChangeState(SKILLSTATE state)
{
switch(state)
{
case SKILLSTATE.READY:
break;
case SKILLSTATE.CREATE:
break;
case SKILLSTATE.LAUNCH:
break;
case SKILLSTATE.ARRIVED:
break;
case SKILLSTATE.DESTROY:
break;
case SKILLSTATE.DEACTIVATE:
break;
default:
break;
}
}
}
이런 식으로 switch-case 문을 이용해서 상태별 동작을 정해줄 수 도 있다.
* 공부하는 단계입니다. 잘못된 부분이 있다면 피드백 부탁드립니다☺
* e-mail : heehee970@naver.com
'Unity > 스크립팅' 카테고리의 다른 글
[유니티 C#] 다형성(Polymorphism) (0) | 2021.10.20 |
---|---|
[유니티 C#] Action 사용 - 이벤트(event), 강한 결합(Tight Coupling) 해소 (0) | 2021.10.13 |
[유니티 C#] GetAxis, GetAxisRaw. 오브젝트가 바라보는 방향으로 이동하기 (0) | 2021.09.29 |
[유니티 C#] Action 과 Func (0) | 2021.09.04 |
[유니티 C#] 유니티의 시간 마법사 Time.timeScale, 그리고 Time.unscaledTime (0) | 2021.08.11 |