728x90
반응형
지난 포스트에 이어 이번에는 플레이어 코드를 구현해보록 하겠습니다.
1) PlayerController
public class PlayerController : BaseCharacterController
{
.
.
.
#region States
.
.
.
public PlayerStateCounterAttack _counterAttackState { get; private set; }
#endregion
// Player Input
.
.
.
[SerializeField]
InputAction _counterAttackAction;
.
.
.
// Attack Info
[Header("Attack Details")]
.
.
.
public float _counterAttackDuration;
public bool _isCounterAttackClicked;
.
.
.
// InputSystem 활성화
private void OnEnable()
{
.
.
.
_counterAttackAction.performed += DoCounterAttack;
_counterAttackAction.canceled += DoStopCounterAttack;
_counterAttackAction.Enable();
}
// InputSystem 비활성화
private void OnDisable()
{
.
.
.
_counterAttackAction.performed -= DoCounterAttack;
_counterAttackAction.canceled -= DoStopCounterAttack;
_counterAttackAction.Disable();
}
// Controller의 Awake에서는 StateMachine과 StateMachine에서 사용할 State를 설정
protected override void Awake()
{
.
.
.
_counterAttackState = new PlayerStateCounterAttack(this, _stateMachine, "CounterAttack");
}
.
.
.
void DoCounterAttack(InputAction.CallbackContext value)
{
_isCounterAttackClicked = value.ReadValueAsButton();
}
void DoStopCounterAttack(InputAction.CallbackContext value)
{
_isCounterAttackClicked = value.ReadValueAsButton();
}
.
.
.
}
PlayerController에 카운터 어택을 수행하기 위한 여러 구성들을 추가하였습니다.
2) PlayerState
public class PlayerState
{
.
.
.
// Player Input
.
.
.
protected bool _isCounterAttacking;
.
.
.
// State에서 매 프레임마다 진행
public virtual void Update()
{
.
.
.
_isCounterAttacking = _controller._isCounterAttackClicked;
}
.
.
.
}
플레이어의 상태의 조상 클래스인 PlayerState에서
카운터 어택의 입력을 전달하기 위해 위와 같이 구조를 수정하였습니다.
3) PlayerStateCounterAttack
public class PlayerStateCounterAttack : PlayerState
{
public PlayerStateCounterAttack(PlayerController inController, PlayerStateMachine inStateMachine, string inParamName)
: base(inController, inStateMachine, inParamName)
{
}
// 카운터 어택이 성공해야만 카운터 어택 성공 애니메이션을 재생하므로 다음과 같이
// 카운터 어택 상태로 들어가는 경우, 해당 애니메이터 인자를 false로 설정
public override void Enter()
{
base.Enter();
_stateTimer = _controller._counterAttackDuration;
_controller._animator.SetBool("SuccesfulCounterAttack", false);
}
public override void Exit()
{
base.Exit();
}
public override void Update()
{
base.Update();
// 카운터 어택동안 이동하지 못하도록 막음
_controller.SetZeroVelocity();
// 카운터 어택을 하는 동안 원 형태로 오버랩된 대상들을 저장
Collider2D[] colliders = Physics2D.OverlapCircleAll(_controller._attackCheck.position, _controller._attackCheckRadius);
foreach (Collider2D hit in colliders)
{
if (hit.GetComponent<EnemyController>() != null)
{
// 저장된 대상들이 스턴할 수 있는 상태라면,
if (hit.GetComponent<EnemyController>().DoDefineCanBeStunned())
{
// _stateTimer를 10초로 설정하여 카운터 어택 상태를 유지하고
// 카운터 어택이 성공하였으므로 카운터 어택 성공 애니메이션을 재생
_stateTimer = 10.0f;
_controller._animator.SetBool("SuccesfulCounterAttack", true);
}
}
}
// _stateTimer가 0보다 작거나, 카운터 어택 애니메이션의 마지막 프레임에 설정된
// Animation Event가 호출되었다면, 상태를 다시 Idle로 바꿈
if (_stateTimer < 0 || _triggerCalled)
_stateMachine.ChangeState(_controller._idleState);
}
}
플레이어의 카운터 어택을 수행하기 위한 PlayerStateCounterAttack을 추가하였습니다.
로직은 위와 같이 구성되었습니다.
4) PlayerStateGrounded
public class PlayerStateGrounded : PlayerState
{
.
.
.
public override void Update()
{
.
.
.
if (_isCounterAttacking)
_stateMachine.ChangeState(_controller._counterAttackState);
.
.
.
}
}
마지막으로 플레이어가 땅을 딛고 서 있는 PlayerStateGrounded의 경우
PlayerStateCounterAttack으로 전환하기 위한 조건을 위와 같이 설정하였습니다.
마지막으로 에디터에서 애니메이터에 설정된 애니메이션의 전환을 확인하도록 하겠습니다.
위에서 C.A는 CounterAttack을,
S.C.A는 SuccesfulCounterAttack을 의미합니다.
728x90
반응형
'유니티 > 게임 프로젝트' 카테고리의 다른 글
2D RPG - (14) 분신술 스킬 구현 (0) | 2025.01.07 |
---|---|
2D RPG - (13) 스킬 시스템 구현을 위한 사전설정 (0) | 2025.01.06 |
2D RPG - (12 - 1) 카운터 어택 구현하기 (0) | 2024.12.31 |
2D RPG - (11) 피격에 따른 효과 구현하기 (0) | 2024.12.30 |
2D RPG - (10) 스켈레톤 만들기 (마무리) (0) | 2024.12.18 |