Notice
Recent Posts
Recent Comments
Link
«   2025/02   »
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
Archives
Today
Total
관리 메뉴

hueam아카이브

상태 기반 애니메이션 & 리플렉션 기반 FSM 본문

오래남는 공부/Unity

상태 기반 애니메이션 & 리플렉션 기반 FSM

hueam 2023. 11. 27. 21:53

리플렉션으로 만드는 FSM

상태 정의

public enum PlayerStateEnum
{
    Idle,
    Move,
    Dash,
    Jump,
    Fall
}

관리하기 편하게 enum을 통해 상태를 저장해 줬다.

상태 만들기

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PlayerState 
{
    protected PlayerStateMachine _stateMachine;//플레이어 상태머신
    protected Player _player;
    protected Rigidbody2D _rigidbody;

    protected int _animBoolHash;
    protected readonly int _yVelocityHash = Animator.StringToHash("y_velocity");

    public PlayerState(Player player, PlayerStateMachine stateMachine, string animationBoolName)
    {
        _player = player;
        _stateMachine = stateMachine;
        _animBoolHash = Animator.StringToHash(animationBoolName);
        _rigidbody = _player.RigidbodyCompo;
    }
    public virtual void Enter()
    {
        _player.AnimatorCompo.SetBool(_animBoolHash, true);
    }
    public virtual void UpdateState()
    {
        _player.AnimatorCompo.SetFloat(_yVelocityHash, _rigidbody.velocity.y);
    }
    public virtual void Exit()
    {
        _player.AnimatorCompo.SetBool(_animBoolHash, false);
    }
}

_animBoolHash를 통해 애니메이션을 제어할 수 있게 만들었다.
상태에 들어오면 true로 해주어 애니메이션들 실행 해주고 나가면 false해주어 애니메이션을 나간다.
기본적으로 모든 상태들이 필요한 것들을 만들어 두고 Enter,UpdateState,Exit와 같이 상태마다 다를 걸 생각하여 virtual로 만들어 주었다.

상태 관리

이런식으로 PlayerStateMachine을 만들어 모든 상태들을 관리를 해줬다.

public class PlayerStateMachine
{
    public PlayerState CurrentState { get; private set; }
    public Dictionary<PlayerStateEnum, PlayerState> StateDIctionary
        = new Dictionary<PlayerStateEnum, PlayerState>();

    private Player _player;
    public void Initialize(PlayerStateEnum startState, Player player)
    {
        _player = player;
        CurrentState = StateDIctionary[startState];
        CurrentState.Enter();
    }
    public void ChangeState(PlayerStateEnum newState)
    {
        CurrentState.Exit();
        CurrentState = StateDIctionary[newState];
        CurrentState.Enter();
    }
    public void AddState(PlayerStateEnum state, PlayerState playerState)
    {
        StateDIctionary.Add(state, playerState);
    }
}

Dictionary를 통해 모든 상태들을 저장하고
CurremtState를 통해 현재 상태를 저장한다.

이 스크립트를 통해 현재상태를 업데이트하며 상태를 바꿀 수 있다.

더욱 자세한 상태

그냥 방식대로 만들게 되면 무한 점프 같은 것이 가능하게 된다.
이를 막기 위해 더욱 더욱 자세한 상태를 만들어 들고 만들 상태를 상속 받으면 된다.

public abstract class PlayerGroundState : PlayerState
{
    public PlayerGroundState(Player player, PlayerStateMachine stateMachine, string animationBoolName) : base(player, stateMachine, animationBoolName)
    {
    }

    public override void Enter()
    {
        base.Enter();
        _player.PlayerInput.JumpEvent += HandleJumpInput;
    }

    public override void Exit()
    {
        base.Exit();
        _player.PlayerInput.JumpEvent -= HandleJumpInput;
    }

    public override void UpdateState()
    {
        base.UpdateState();
    }

    private void HandleJumpInput()
    {
        if (_player.IsGroundDetected())
        {
            _stateMachine.ChangeState(PlayerStateEnum.Jump);
        }
    }
}

이런 식으로 만들어줬다.

리플렉션을 이용해 상태 인스턴스 만들기

public class` Player : MonoBehaviour
{
    public PlayerStateMachine StateMachine { get; private set; }

    #region 콤포넌트 영역
    public Animator AnimatorCompo { get; private set; }
    public Rigidbody2D RigidbodyCompo { get; private set; }
    public CapsuleCollider2D ColliderCompo { get; private set; }
    #endregion

    public int FacingDirection { get; private set; } = 1;
    protected bool _facingRight = true;

    protected virtual void Awake()
    {
        Transform visualTrm = transform.Find("Visual");
        AnimatorCompo = visualTrm.GetComponent<Animator>();
        RigidbodyCompo = GetComponent<Rigidbody2D>();
        ColliderCompo = GetComponent<CapsuleCollider2D>();

        //여기서 스테이트 머신에서 AddState를 실행시킴
        StateMachine = new PlayerStateMachine();
        foreach (PlayerStateEnum state in Enum.GetValues(typeof(PlayerStateEnum)))
        {
            string typeName = state.ToString();
            Type t = Type.GetType($"Player{typeName}State");

            PlayerState newState = Activator.CreateInstance(t, this, StateMachine, typeName) as PlayerState;
            StateMachine.AddState(state, newState);
        }
        StateMachine.Initialize(PlayerStateEnum.Idle, this);
    }
}

Enum의 값을 string 으로 변환해서 바꾼 값을 통해 리플렉션을 이용해 상태 스크립트들은 인스턴스로 만들어줘 이를 PlayerStateMachine넣어 관리한다.

상태 기반 애니메이션

위에서 만들 상태들를 기반으로 애니메이션을 실행시키는 거다.
Pasted image 20231127134808.png
PlayerState 의 셍성자를 통해 상태 이름을 Hash로 바꿔 _animBoolHash에 넣는다.
또한 애니메이션의 Parameter또한 상태 이름에 맞게 만들어줘
상태에 들어갈 때 true로 해주어 그 상태의 맞는 애니메이션을 실행시켜주고 나갈때 false로 해주에 Exit로 나가 Entry로 돌아와 현재 상태에 맞는 애니메이션을 실행시켜준다.

'오래남는 공부 > Unity' 카테고리의 다른 글

InputSystem  (0) 2023.11.25