2 분 소요

Intro

이번 포스팅은 플레이어의 공격 애니메이션에 맞게 공격판정을 구현해보았습니다.

공격 판정을 구현하는 방법은 여러가지가있습니다.

  1. Raycast 활용
  2. Collider 활용
  3. Overlap 활용
  4. Projectile 활용

전략패턴으로 무기를 만들어놓았으니, 각 무기마다 적절한 방법을 골라서 적용할 수 있습니다.

Sword

검은 Collider를 활용하여 공격판정을 구현해보았습니다.

Sword 클래스에서 공격판정을 위한 함수를 구현합니다.

/*
                    Sword : 무기(검) 클래스

            - BoxCollider를 사용해서 공격판정 구현
            
 */
public class Sword : Weapon
{
    private BoxCollider hitBox;                 // 공격 판정

    private void Awake()
    {
        // 무기 타입 설정
        type = WeaponType.Sword;

        hitBox = GetComponent<BoxCollider>();
    }

    // 공격 데미지 등
    public override void Attack()
    {
        Debug.Log("몬스터 공격성공");
    }

    protected override void OnTriggerEnter(Collider other)
    {
        if(other.CompareTag("Monster"))
        {
            Attack();
        }
    }
    public override void SetHitBox(bool isEnabled)
    {
        hitBox.enabled = isEnabled;
    }
}


  • 콜라이더의 Trigger 옵션을 통해 공격의 성공여부를 판단할것이므로 OntriggerEnter를 사용했습니다.

  • SetHitBox()는 애니메이션 이벤트를 사용하기위한 함수로, PlayerController 클래스에서 호출합니다.

image

Staff

스태프 공격은 범위내의 모든 적을 공격하는 범위공격으로 만들어보았습니다.

RayCast를 활용한 방법입니다.

/*
                    Staff : 무기(스태프) 클래스

            - RayCast를 사용해서 공격판정 구현
 */

public class Staff : Weapon
{
    private float attackRange = 3f;             // 공격 범위
    private float attackRadius = 3f;            // 공격 반경 

    private Vector3 attackOrigin;
    private Vector3 attackDir;

    private List<RaycastResult> rrList;

    private void Awake()
    {
        type = WeaponType.Staff;
    }

    // 공격 구현
    public override void Attack()
    {
        attackOrigin = GameManager.Instance.player.transform.position + GameManager.Instance.player.transform.forward * 2f;
        attackDir = GameManager.Instance.player.transform.forward;
        
        // 공격범위
        RaycastHit[] hits = Physics.SphereCastAll(
            attackOrigin,
            attackRadius,
            attackDir,
            attackRange,
            LayerMask.GetMask("Monster")
        );

        // 공격범위에 몬스터가 있을경우
        foreach(RaycastHit hit in hits)
        {
            Monster monster = hit.collider.GetComponent<Monster>();
            if(monster != null)
            {
                // .. 몬스터에게 데미지
                Debug.Log("몬스터를 맞추었음.");
            }
        }
    }

    public override void SetHitBox(bool isEnabled)
    {
        
    }

    // 공격범위 시각화
    void OnDrawGizmos()
    {
        Gizmos.color = Color.red;
        Gizmos.DrawWireSphere(attackOrigin, attackRadius);
        Gizmos.color = Color.yellow;
        Gizmos.DrawLine(attackOrigin, attackOrigin + attackDir * attackRange);
        Gizmos.color = Color.green;
        Gizmos.DrawWireSphere(attackOrigin + attackDir * attackRange, attackRadius);
    }
}

image

  • 구형(Sphere) Raycast를 사용하여 범위내 적을 공격합니다.

  • OnDrawGizmos는 Raycast 하려는 모양을 시각화해주는 함수입니다.
    • 빨간색 부분은 공격의 시작점, 초록색 부분은 공격이 끝나는지점으로 눈으로 확인할 수 있도록 했습니다.
  • 마찬가지로 플레이어의 애니메이션 이벤트를 통해 공격함수를 호출합니다.

PlayerController

공격 애니메이션은 플레이어 애니메이터 존재하고, 각 무기에 공격함수가 달려있으므로

애니메이션 이벤트를 사용하기위해서는 플레이어 오브젝트에 무기 클래스가 붙어있어야합니다.

하지만 무기클래스는 각 무기에 달려있으므로 애니메이션 이벤트에서 무기클래스에 접근하기위해서는

WeaponManager를 통해 간접적으로 접근해야합니다.

    // 플레이어 공격
    private void Attack()
    {
        if (isAttackKeyDown && !isAttacking)
        {
            anim.SetTrigger(hashAttackTrigger);
        }
    }

    #region ** Animation Events **
    private void IsAttacking() => isAttacking = !isAttacking;

    private void EnableAttackHitbox() => WeaponManager.Instance.currentWeapon.SetHitBox(true);
    
    private void DisableAttackHitbox() => WeaponManager.Instance.currentWeapon.SetHitBox(false);

    private void TriggerAttack() => WeaponManager.Instance.currentWeapon.Attack();
    #endregion
  • 플레이어가 공격중에는 움직이지 못하도록하였습니다.
    • bool형 변수 isAttacking 를 통해 플레이어가 공격중인지 판별합니다.
    • IsAttacking()는 애니메이션이 시작될때와 끝날 때 호출하여 플레이어가 공격중인지를 변경합니다.
  • EnableAttackingHitBox()DisableAttackHitbox() 는 Collider를 활용한 공격에 사용될 메서드입니다.
  • TriggerAttack()는 ‘Weapon’ 클래스의 Attack() 함수를 호출하기위한 메서드입니다.

댓글남기기