유니티/게임 프로젝트

2D RPG - (12 - 1) 카운터 어택 구현하기

monstro 2024. 12. 31. 18:20
728x90
반응형

이번 포스트에서는 적의 공격을 반격하는 카운터 어택 기능을 구현해보도록 하겠습니다.

 

위와 같이 카운터 어택이 성공하였을 때,

평범하게 공격받을 때와 다르게 이펙트가 발생하는 것을 볼 수 있습니다

 

적에게서 보여지는 빨간색 사각형이 유요한 동안에만 카운터 어택을 수행할 수 있으며,

플레이어의 경우 카운터 어택이 성공하면 성공 시의 애니메이션이 연출되는 것을 확인할 수 있습니다.

 

1) BaseEffectController

public class BaseEffectController : MonoBehaviour
{
	.
	.
	.

    void RedColorBlink()
    { 
        if(_spriteRenderer.color != Color.white)
            _spriteRenderer.color = Color.white;
        else
            _spriteRenderer.color = Color.red;
    }

    void CancelRedColorBlink()
    {
        CancelInvoke();
        _spriteRenderer.color= Color.white;
    }
}

 

이펙트를 발동시키는 BaseEffectController에 2개의 함수를 추가하였습니다.

 

RedColorBlink의 경우, 카운터 어택에 당한 대상빨간색이 점멸하는 것처럼 연출합니다.

해당 함수는 InvokeRepeating함수로 반복수행하도록 합니다.

CancleRedColorBlink 함수의 경우, RedColorBlink 함수를 중단하는 역할을 수행합니다.

CancelInvoke 함수를 통해 Invoke로 수행되는 모든 함수를 중단합니다.

 

2) 몬스터 작업

2 - 1) EnemyController

public class EnemyController : BaseCharacterController
{
    // 적 스턴 정보
    [Header("Stunned Info")]
	.
	.
	.
    protected bool _canBeStunned;
    [SerializeField]
    protected GameObject _counterImage; // 카운터 어택을 당할 수 있는지 조건을 판별하는 오브젝트, 빨간 사각형에 해당함
	
	.
	.
	.
   
    // 카운터 어택을 당하기 위한 조건을 설정하는 함수
    public virtual void DoOpenCounterAttackWindow()
    {
        _canBeStunned = true;
        _counterImage.SetActive(true);
    }

    // 카운터 어택을 당하기 위한 조건을 회수하는 함수
    public virtual void DoCloseCounterAttackWindow()
    {
        _canBeStunned = false;
        _counterImage.SetActive(false); 
    }

    // 스턴 상태로 전환될 수 있는지 판단하는 함수
    public virtual bool DoDefineCanBeStunned()
    {
        if (_canBeStunned)
        {
            DoCloseCounterAttackWindow();
            return true;
        }

        return false;
    }
}

 

몬스터 클래스의 조상 클래스인 BaseEnemyController를 위와 같이 수정하였습니다.

영상에서 보여지는 빨간색 사각형이 바로 _counterImage이며,

_counterImage가 유효한 동안에만 카운터 어택에 당하게 됩니다.

 

2 - 2) SkeletonController

public class SkeletonController : EnemyController
{
    // Skeleton States
	.
	.
	.
    public SkeletonStateStunnded _stunnedState { get; private set; }

    // Awake에서는 Skeleton이 가질 State들을 설정함
    protected override void Awake()
    {
		.
		.
		.
        _stunnedState = new SkeletonStateStunnded(this, _stateMachine, "Stunned", this);
    }

	.
	.
	.

	// 부모 클래스의 메서드를 개별 클래스에 적용하기 쉽도록 오버라이드
    public override bool DoDefineCanBeStunned()
    {
        if (base.DoDefineCanBeStunned())
        {
            _stateMachine.ChangeState(_stunnedState);
            return true;
        }

        return false;
    }
}

 

스켈레톤을 조종하는 SkeletonController 클래스를 위와 같이 수정하였습니다.

 

2 - 3) SkeletonStateStunned

public class SkeletonStateStunnded : EnemyState
{
    SkeletonController _skeletonController;

    public SkeletonStateStunnded(EnemyController enemyBaseController, EnemyStateMachine enemyStateMachine, string animatorBoolParamName, SkeletonController enemyController)
        : base(enemyBaseController, enemyStateMachine, animatorBoolParamName)
    {
        this._skeletonController = enemyController;
    }

    public override void Enter()
    {
        base.Enter();

        // 스턴 상태에 돌입한 경우, 0.1초 간격으로 즉시 이펙트 발생
        _enemyBaseController._baseEffectController.InvokeRepeating("RedColorBlink", 0, 0.1f);

        _stateTimer = _skeletonController._stunDuration;

        // SetVelocity를 하는 경우, 적이 스턴 상태에서 이동하게
        // 되므로 다음과 같이 새로운 벡터를 만들어 이동을 새로운 벡터 쪽으로 수행하게끔 고정함
        _rigidbody2D.velocity = new Vector2(_skeletonController._facingDir * _skeletonController._stunDirection.x, _skeletonController._stunDirection.y);
    }

    public override void Exit()
    {
        base.Exit();

        // 스턴 상태가 끝나는 경우, 발동중인 Invoke 함수를 전부 즉시 취소함
        _enemyBaseController._baseEffectController.Invoke("CancelRedColorBlink", 0);
    }

    public override void Update()
    {
        base.Update();

        if (_stateTimer < 0)
            _enemyStateMachine.ChangeState(_skeletonController._idleState);
    }
}

 

마찬가지로 스켈레톤의 스턴 상태를 위한 새로운 State를 추가하였습니다.

동작은 위와 같이 이뤄집니다.

 

2 - 4) SkeletonAnimationTrigger

public class SkeletonAnimationTrigger : MonoBehaviour
{
   .
   .
   .

    private void DoOpenCounterWindow() => _skeletonController.DoOpenCounterAttackWindow();
    private void DoCloseCounterWindow() => _skeletonController.DoCloseCounterAttackWindow();
}

 

스켈레톤의 애니메이션에 Animation Event로 사용할 수 있도록

SkeletonController가 상속받은 메소드들을 위와 같은 구조로 클래스에 포함시켰습니다.

 

이어지는 포스트에서는 플레이어의 설정을 다뤄보도록 하겠습니다. 

728x90
반응형