Awesome Unity3D: 상태 머신 — 효과적인 상태 관리

11041 단어 unity3d
이 기사에서는 FSM(Finite State Machine)이 무엇인지 알고 있다고 가정합니다. 그렇지 않다면 shows common pitfalls when writing code handling character state and why we use FSMs 이 기사를 읽는 것이 좋습니다. "TL;DR"을 선호하는 경우 요점은 FSM이 없으면 프로그램에서 많은 플래그를 사용해야 한다는 것입니다. 이로 인해 모든 곳에서 많은 중첩된 "if-else"문이 발생하여 무시무시한 디버깅 경험이 발생할 수 있습니다. 이 스스로 만든 지옥에서 시간을 낭비하고 싶지 않습니다.

// Example of self-made hell
private void handleInput()
{
  if (Input.GetKeyDown(KeyCode.Space))
  {
    if (!isJumping && !isDucking)
    {
      // Jump...
    }
  }
  else if (Input.GetKeyDown(KeyCode.D))
  {
    if (!isJumping)
    {
      isDucking = true;
      setGraphics(IMAGE_DUCK);
    }
  }
  else if (Input.GetKeyUp(KeyCode.D))
  {
    if (isDucking)
    {
      isDucking = false;
      setGraphics(IMAGE_STAND);
    }
  }
}

FSM은 내가 아는 대부분의 사람들이 애증 관계를 가지고 있는 종류의 패턴입니다. 동적 응용 프로그램 상태를 캡슐화하는 기능을 좋아하고 추가 복잡성을 싫어합니다. 개인적으로 저는 코드 전용 FSM을 사용하는 것을 좋아하지 않았습니다. 저글링 상태 전환은 특히 유닛 간의 복잡한 상호 작용이 있는 게임에서 상당히 소수일 수 있지만 Playmaker와 같은 FSM용 GUI 솔루션을 포함하는 것은 상당히 무거울 수 있기 때문입니다.

참고로 Unity 내에서 사용할 수 있는(또는 하이재킹할 수 있는) 경량 FSM 시스템이 있다면 어떨까요?

AnimationController를 FSM으로 사용



Unity에는 이미 약간의 수정을 거쳐 사용할 수 있는 애니메이션용 상태 머신이 포함되어 있습니다.



설정


  • 애니메이터가 연결된 장면에서 빈 게임 오브젝트를 만듭니다.
  • 프로젝트 폴더 어딘가에 새 AnimatorController 자산을 만듭니다. 만든 Animator에 이것을 첨부합니다.
  • 창→애니메이터→매개변수를 열고 "다음"이라는 트리거를 만듭니다.



  • 커스터마이징



    상속하는 클래스StateMachineBehaviour를 생성하여 사용자 정의 상태를 생성하고 상태 시스템에 보다 깔끔한 인터페이스를 제공하며 디버깅을 활성화할 수 있습니다. 커스텀 클래스LogicalStateMachine를 호출할 수 있습니다. StateMachineBehaviour 는 이미 OnStateEnter , OnStateUpdate , OnStateExit 와 같은 메서드를 제공하며 이러한 인터페이스를 제공하는 데 사용할 수 있습니다.

    protected abstract void OnLogicalStateEnter(GameObject gameObject);
    
    protected abstract void OnLogicalStateUpdate(GameObject gameObject);
    
    protected abstract void OnLogicalStateExit(GameObject gameObject);
    
    protected abstract void ResetState(GameObject gameObject);
    
    public override void OnStateEnter(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
    {
        ResetState(ActiveAnimator.gameObject);
        OnLogicalStateEnter(ActiveAnimator.gameObject);
    }
    
    public override void OnStateUpdate(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
    {
        OnLogicalStateUpdate(ActiveAnimator.gameObject);
    }
    
    public override void OnStateExit(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
    {
        OnLogicalStateExit(ActiveAnimator.gameObject);
    }
    

    디버깅을 더 쉽게 하기 위해 이전에 생성한 트리거를 사용하여 다음 상태가 발생하도록 강제할 수 있는 메서드Continue를 생성합니다.

    protected Animator ActiveAnimator;
    
    protected void Continue()
    {
        if (!ActiveAnimator) return;
        ActiveAnimator.SetTrigger(Animator.StringToHash(Next));
    }
    
    public override void OnStateEnter(Animator animator, AnimatorStateInfo stateInfo, int layerIndex)
    {
        ActiveAnimator = animator;
        ResetState(ActiveAnimator.gameObject);
        ActiveAnimator.ResetTrigger(Animator.StringToHash(Next));
    
        OnLogicalStateEnter(ActiveAnimator.gameObject);
    } 
    

    에디터에서 인스펙터 버튼을 생성하여 이를 더욱 개선할 수 있습니다.



    개인적으로 저는 Odin Inspector를 사용하여 하나를 생성하지만 기본 솔루션을 선호하는 경우 하나here가 있습니다.

    짜잔! 프로젝트에서 사용할 수 있는 간단한 상태 시스템입니다. 여기에서도 코드를 찾을 수 있습니다: https://gitlab.com/glassblade/unityfsm

    게임의 효과적인 상태 관리



    이 조언은 대부분의 복잡한 애플리케이션에 적용되지만 게임이 좋은 예입니다. 게임에는 일반적으로 영구 상태와 동적 상태라는 두 가지 유형의 상태가 포함됩니다. 예를 들어 "Enter the Gungeon"게임에서 지속 상태에는 잠금 해제된 총기 풀이 포함되는 반면 동적 상태에는 플레이어가 집어든 총이 포함됩니다.



    FSM에는 상태 및 전환이라는 두 가지 기본 구성 요소가 포함되어 있으며 FSM에서 상태를 더 쉽게 디버깅할 수 있도록 다음을 권장합니다.
  • 플래그를 사용해야 하는 경우 사용하지 마십시오. 대신 상태를 사용하십시오.
  • FSM과 관련하여 게임에서 지속 상태와 동적 상태가 무엇인지 식별하기 위해 코딩하기 전에 약간의 시간을 할애합니다. 동적 상태는 상태에 들어갈 때마다 재설정되어야 하지만 지속 상태는 재설정되지 않습니다.
  • 상태에 들어갈 때 영구 상태의 변경 불가능한 복사본을 만들고 상태를 종료할 때만 영구 상태를 설정합니다. 나중에 이를 최적화할 수 있지만 오류 지점을 디버깅하기가 훨씬 쉬워집니다.
  • 좋은 웹페이지 즐겨찾기