유니티/게임 프로젝트

2D RPG - (11) 피격에 따른 효과 구현하기

monstro 2024. 12. 30. 21:13
728x90
반응형

지난 포스트에서는 스켈레톤의 기본적인 구조를 갖춰놓았습니다.

이번에는 공격받았을 경우, 피격 효과를 구현해보도록 하겠습니다.

 

피격 효과의 구현은 Animation Event를 통해 발동시키도록 하겠습니다.

 

1) BaseEffectController

public class BaseEffectController : MonoBehaviour
{
    private SpriteRenderer _spriteRenderer;

    [Header("Flash FX")]
    [SerializeField]
    private float _flashDuration;
    private Material _defaultMaterial;
    [SerializeField]
    private Material _hitMaterial;

    private void Start()
    {
        _spriteRenderer = GetComponentInChildren<SpriteRenderer>();
        _defaultMaterial = _spriteRenderer.material;
    }

    public IEnumerator DoMakeFlashFX()
    {
        _spriteRenderer.material = _hitMaterial;

        yield return new WaitForSeconds(_flashDuration);

        _spriteRenderer.material = _defaultMaterial;
    }
}

 

이펙트의 발생을 수행하는 BaseEffectController입니다.

컴포넌트로서 부착되며,

기본적으로 사용하는 머티리얼외에도 피격시 사용할 머티리얼을 설정할 수 있습니다.

 

피격되는 경우 DoMakeFlashFX() 함수로,

피격 머티리얼을 SpriteRenderer에 적용하고 일정시간 후 기본 머티리얼을 적용합니다.

 

2) BaseCharacterController

public class BaseCharacterController : MonoBehaviour
{
	.
	.
	.

    [Header("Attack Collision Info")]
    public Transform _attackCheck;
    public float _attackCheckRadius;

    [Header("Knockback Info")]
    [SerializeField]
    protected Vector2 _knockbackDirection;
    protected bool _isKnocked;
    [SerializeField]
    float _knockbackDuration;

	.
	.
	.
    
    public BaseEffectController _baseEffectController { get;  private set; }

	.
	.
	.
    
    // 필요한 컴포넌트를 가져옴
    protected virtual void Start()
    {
        _animator = GetComponentInChildren<Animator>();
        _rigidbody2D = GetComponent<Rigidbody2D>();
        _baseEffectController = GetComponent<BaseEffectController>();
    }
    
	.
	.
	.

    // 속력을 설정하고 설정한 속력에 따라 방향 회전 수행
    public virtual void SetVelocity(float xVelocity, float yVelcoity)
    {
        // 피격당해 넉백되고 있는 동안 이동을 막음
        if (_isKnocked)
            return;

        _rigidbody2D.velocity = new Vector2(xVelocity, yVelcoity);
        DoFlip(xVelocity);
    }

    // 속력을 0으로 설정
    public virtual void SetZeroVelocity()
    {
        // 피격당해 넉백당하고 있는 동안에는 이동을 고정시키는 것도 막음
        if (_isKnocked)
            return;

        _rigidbody2D.velocity = Vector2.zero;
    }

	.
	.
	.
    
    // 피격당하는 경우 호출되는 함수
    public virtual void DoGetDamage()
    {
        _baseEffectController.StartCoroutine("DoMakeFlashFX");
        StartCoroutine("DoGetKnockbacked");
    }

	// DoGetDamage에서 호출하는 함수, 피격 시 넉백을 수행함
    protected virtual IEnumerator DoGetKnockbacked()
    {
        _isKnocked = true;

        _rigidbody2D.velocity = new Vector2(_knockbackDirection.x * -_facingDir, _knockbackDirection.y);

        yield return new WaitForSeconds(_knockbackDuration);

        _isKnocked = false;
    }

    // Coliision 영역을 Debug Line을 그려 확인
    protected virtual void OnDrawGizmos()
    {
        Gizmos.DrawLine(
            _groundCheck.position,
            new Vector3(_groundCheck.position.x, _groundCheck.position.y - _groundCheckDistance)
        );

        Gizmos.DrawLine(
            _wallCheck.position,
            new Vector3(_wallCheck.position.x + _wallCheckDistance, _wallCheck.position.y)
        );

        Gizmos.DrawWireSphere(
            _attackCheck.position,
            _attackCheckRadius
        );    
    }
}

 

모든 캐릭터들의 조상 클래스인 BaseCharacterController에는 공격을 위한 요소들을 추가하였습니다.

공격 판정을 위한 Collision넉백을 위한 데이터들프로퍼티로 추가하였습니다.

 

또, Start 함수에서 BaseEffectController를 찾아와 인스턴스로 설정하도록 하였습니다.

이동과 관련한 속도를 설정하는 SetVelocity SetZeroVelocity넉백하는 동안은 수행을 막습니다.

 

공격하는 경우, 자식 클래스에서는 공격당한 대상의 DoGetDamage 함수를 호출합니다.

코루틴을 사용하여 피격 효과와 넉백 효과를 주고 일정시간 후에 다시 되돌리게 됩니다.

 

마지막으로, OnDrawGizmos 함수에서는

공격 판정을 위한 Collision을 시각적으로 그려주게 됩니다.

 

3) SkeletonAnimationTrigger

public class SkeletonAnimationTrigger : MonoBehaviour
{
   .
   .
   .

    private void AttackAnimationTrigger()
    {
        Collider2D[] colliders = Physics2D.OverlapCircleAll(_skeletonController._attackCheck.position, _skeletonController._attackCheckRadius);

        foreach (Collider2D hit in colliders)
        {
            if (hit.GetComponent<PlayerController>() != null)
            {
                hit.GetComponent<PlayerController>().DoGetDamage();
            }
        }
    }
}

 

Skeleton의 공격 애니메이션에서 Animation Event로 추가할 AttackAnimationTrigger 함수입니다.

원 형태로 Overlap을 수행하여 오버랩된 대상을 colliders에 저장합니다.

 

colliders를 순회하면서 Player인지 판단하고

Player라면, DoGetDamage 함수를 호출하게 합니다.

 

* 2024 / 12 / 31 추가 주석 *

추가적으로, 해당하는 AttackAnimationTrigger 같은 경우 특정한 몬스터에 제한없이 사용할수 있으므로

Base 몬스터 클래스에서 수행할 수 있도록 코드 구조를 변경하는 것이 나아보입니다.

 

4) PlayerAnimationTrigger

public class PlayerAnimationTrigger : MonoBehaviour
{
	.
	.
	.

    private void AttackAnimationTrigger()
    {
        Collider2D[] colliders = Physics2D.OverlapCircleAll(_controller._attackCheck.position, _controller._attackCheckRadius);

        foreach (Collider2D hit in colliders)
        { 
            if(hit.GetComponent<EnemyController>() != null)
            {
                hit.GetComponent<EnemyController>().DoGetDamage();
            }
        }
    }
}

 

Player의 공격 애니메이션에서 Animation Event로 추가할 AttackAnimationTrigger 함수입니다.

마찬가지로, 원 형태로 Overlap을 수행하여 오버랩된 대상을 colliders에 저장합니다.

 

colliders를 순회하면서 Enemy인지 판단하고

Enemy라면, DoGetDamage 함수를 호출하게 합니다.

 

5) 추가 설정

코드 작업이 마무리되었으므로, 애니메이션의 올바른 프레임에 Animation Event를 설정합니다.

또, 플레이어와 몬스터간에 서로 밀고 당기지 못하게 만들기 위해

상단 Project Settings - Physics 2D에서 Player 레이어와 Enemy 레이어의 충돌을 막아놓았습니다.

 

피격 시 머티리얼 효과를 주기 위해 머티리얼을 생성한 후 Shader - GUI - Text Shader를 생성하였습니다.

생성한 머티리얼은 각각의 BaseEffectController에 설정하였습니다.

 

실제 결과물은 다음과 같습니다.

 

 

728x90
반응형