1) 자식 오브젝트에서 사용하는 PlayAnimationTrigger
기존에 PlayerController에서 소개해드린 AnimationTrigger 함수의 경우
Animator가 있어야 설정이 가능합니다.
그러나 Animator의 경우
위와 같이 PlayerController가 부착된 오브젝트의 자식에 존재합니다.
따라서 Animator에서 사용하는 컴포넌트를 하나 만들어 다음과 같이 사용할 수 있도록 하겠습니다.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerAnimationTrigger : MonoBehaviour
{
private PlayerController _controller => GetComponentInParent<PlayerController>();
private void AnimationTrigger()
{
_controller.AnimationTrigger();
}
}
위와 같이 로직이 구성되어 Animation의 이벤트가 설정되면
PlayerController의 AnimationTrigger 함수를 호출하여
현재 State의 AnimationFinishTrigger 함수를 호출하게 됩니다.
2) 현재 State에 대한 도입과 전환을 수행하는 PlayerStateMachine
public class PlayerStateMachine
{
public PlayerState _currentState { get; private set; }
// 제일 처음 아무것도 아닌 상태에서 특정 State에 돌입하는 경우 호출
public void Init(PlayerState startState)
{
_currentState = startState;
_currentState.Enter();
}
// 특정 State인 상태에서 다른 State에 돌입하는 경우 호출
public void ChangeState(PlayerState newState)
{
_currentState.Exit();
_currentState = newState;
_currentState.Enter();
}
}
PlayerStateMachine은
Init 함수를 통해 _currentState를 초기화하고 현재 State의 Enter 함수를 실행하고,
ChangeState 함수를 통해
현재 State의 Exit 함수를 실행한 후 _currentState를 업데이트하고 현재 State의 Enter 함수를 실행합니다.
3) 모든 State의 부모인 PlayerState
public class PlayerState
{
// Animtion StateMachine
protected PlayerStateMachine _stateMachine;
protected PlayerController _controller;
private string _animatorBoolParamName;
// Player Input
protected float _xInput;
protected float _yInput;
protected bool _isJumping;
protected bool _isAttacking;
protected Rigidbody2D _rigidbody2D;
// Animation Event
protected bool _triggerCalled;
// State Timer
protected float _stateTimer;
public PlayerState(PlayerController inController, PlayerStateMachine inStateMachine, string inParamName)
{
this._controller = inController;
this._stateMachine = inStateMachine;
this._animatorBoolParamName = inParamName;
}
// State에 돌입
public virtual void Enter()
{
_controller._animtor.SetBool(_animatorBoolParamName, true);
_rigidbody2D = _controller._rigidbody2D;
_triggerCalled = false;
}
// State에서 탈출
public virtual void Exit()
{
_controller._animtor.SetBool(_animatorBoolParamName, false);
}
// State에서 매 프레임마다 진행
public virtual void Update()
{
_stateTimer -= Time.deltaTime;
_xInput = _controller._horizontalValue;
_yInput = _controller._verticalValue;
_isJumping = _controller._isJumpPressed;
_isAttacking = _controller._isAttackClicked;
_controller._animtor.SetFloat("yVelocity", _rigidbody2D.velocity.y);
}
public virtual void AnimationFinishTrigger()
{
_triggerCalled = true;
}
}
1) 멤버 변수
가장 먼저 볼수 있는 3개의 멤버변수는
각각 _stateMachine과 _controller, 그리고 _animatorBoolParamName 입니다.
_stateMachine을 통해 CurrentState를 설정하고 여기에 맞춰 Init과 ChangeState를 수행할 수 있습니다.
_controller를 통해 PlayerController에서 업데이트되는 값들을 State들에게 반영합니다.
마지막으로 _animatorBoolParamName은 Animator에서 사용할 bool 변수를 의미합니다.
그 아래의 Player Input 변수들은 State들이 플레이어를 조작하기 위해 사용하게 됩니다.
_triggerCalled 변수는 AnimationEvent에 사용될 불리언 변수입니다.
마지막 변수인 _stateTimer는 State들이 공용적으로 사용할 타이머를 의미합니다.
2) 멤버 함수
생성자에서는 가장 중요한 3가지 변수를 초기화하게 됩니다.
각각 _stateMachine, _controller 그리고 _animatorBoolParamName 입니다.
State에 돌입하는 Enter 함수에서는
현재 State의 _animatorBoolParamName을 true로 설정하고
_rigidBody2D를 초기화, _triggerCalled 변수를 false로 초기화하여 AnimationEvent의 발생을 막겠습니다.
State에서 탈출하는 Exit 함수에서는
현재 State의 _animatorBoolParamName을 false로 설정합니다.
State에서 매 프레임마다 수행하는 Update 함수에서는
_stateTimer를 감소시키고,
PlayerController로부터 데이터를 가져와 PlayerInput 변수들을 초기화합니다.
마지막인 AnimationFinishTrigger 함수에서는
_triggerCalled 변수를 true로 설정하여 AnimationEvent를 발생시키겠습니다.
4) 발을 딛고 있는 상태의 PlayerStateGrounded
public class PlayerStateGrounded : PlayerState
{
public PlayerStateGrounded(PlayerController inController, PlayerStateMachine inStateMachine, string inParamName)
: base(inController, inStateMachine, inParamName)
{
}
public override void Enter()
{
base.Enter();
}
public override void Exit()
{
base.Exit();
}
public override void Update()
{
base.Update();
if (_isAttacking)
_stateMachine.ChangeState(_controller._priamaryAttackState);
if (!_controller.DoDetectIsGrounded())
_stateMachine.ChangeState(_controller._inAirState);
if (_isJumping && _controller.DoDetectIsGrounded())
_stateMachine.ChangeState(_controller._jumpState);
}
}
발을 딛고 있는 상태를 나타내는 PlayerStateGrounded의 경우
PlayerState로부터 상속받아 위와 같이 구성됩니다.
기본적으로 부모인 PlayerState의 구조를 실행하되
Update의 경우 차이점이 존재합니다.
만약, 공격입력이 들어왔다면 CurrentState를 PlayerStatePrimaryAttack으로 변경합니다.
또, 발을 딛고 있지 않은 경우 CurrentState를 PlayerStateInAir로 변경합니다.
마지막으로 발을 딛고 있는 상태에서 점프입력이 들어왔다면
CurrentState를 PlayerStateJump로 변경하게 됩니다.
5) 가만히 있는 상태의 PlayerStateIdle
public class PlayerStateIdle : PlayerStateGrounded
{
public PlayerStateIdle(PlayerController inController, PlayerStateMachine inStateMachine, string inParamName)
: base(inController, inStateMachine, inParamName)
{
}
// State에 돌입
public override void Enter()
{
base.Enter();
_controller.SetZeroVelocity();
}
// State에서 탈출
public override void Exit()
{
base.Exit();
}
// State에서 매 프레임마다 진행
public override void Update()
{
base.Update();
if (_xInput == _controller._facingDir && _controller.DoDetectIsFacingWall())
return;
if (_xInput != 0 && !_controller._doingSomething)
_stateMachine.ChangeState(_controller._moveState);
}
PlayerStateIdle의 경우 PlayerStateGrounded로부터 상속받아
기본적으로 발을 딛고 있는 상태에서 이뤄지게 됩니다.
마찬가지로 Update 함수에서 차이점이 존재하는데,
만약, 세로축 입력이 플레이어가 보는 방향과 같고 벽을 마주하고 있는 상태에서는
return을 걸어 아무것도 수행하지 않게끔 합니다.
하지만, 세로축 입력이 들어왔고 플레이어가 무언가 수행한다면
플레이어의 CurrentState를 PlayerStateMove로 변경하게 됩니다.
'유니티 > 게임 프로젝트' 카테고리의 다른 글
2D RPG - (5) StateMachine의 구성과 완성 (마지막) (0) | 2024.12.09 |
---|---|
2D RPG - (4) StateMachine의 구성과 완성 (3) (1) | 2024.12.09 |
2D RPG - (2) StateMachine의 구성과 완성 (1) (0) | 2024.12.08 |
2D RPG - (1) StateMachine의 설계 (0) | 2024.11.25 |
간단한 2D RPG 프로젝트 - (3) MonsterController (0) | 2024.11.20 |