3 분 소요

Intro

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

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

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

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

Punch

주먹은 Raycast를 활용해서 공격판정을 구현했습니다.

주먹공격은 단일 공격으로, BoxCastAll을 활용하여 cast된 첫번째 몬스터에게만 데미지를 가하도록 했습니다.

/*
                        Punch : 무기(주먹) 클래스

                - RayCast를 사용해서 공격판정 구현
*/
public class Punch : Weapon
{
    private float attackRange = 1f;
    private Vector3 boxSize = new Vector3(0.8f, 2f, 0.8f);
    private Vector3 attackOrigin;                             
    private Vector3 attackDir;
    
    private void Awake()
    {
        // 무기 타입 설정
        type = WeaponType.None;
    }

    public override void Attack()
    {
        attackOrigin = GameManager.Instance.player.transform.position + GameManager.Instance.player.transform.up;
        attackDir = GameManager.Instance.player.transform.forward;
        

        RaycastHit[] hits = Physics.BoxCastAll(
            attackOrigin,                       // 중심위치 : 플레이어
            boxSize,                            // 박스크기
            attackDir,                          // 공격방향     
            Quaternion.identity,                // 회전X
            attackRange,                        // 공격최대거리
            LayerMask.GetMask("Monster")
            );
        
        // 한마리만 공격
        if(hits.Length > 0)
        {
            Monster monster = hits[0].collider.GetComponent<Monster>();
            if (monster != null)
            {
                monster.GetDamaged(DataManager.Instance.GetPlayerData().Damage);
            }
        }
    }

    private  void OnTriggerEnter(Collider other)
    {
        
    }

    public override void SetHitBox(bool isEnabled)
    {
        
    }
    
    // 공격범위 시각화
    void OnDrawGizmos()
    {
        Gizmos.color = Color.red;
        Gizmos.DrawWireCube(attackOrigin, boxSize);

        Gizmos.color = Color.yellow;
        Gizmos.DrawLine(attackOrigin, attackOrigin + attackDir * attackRange);

        Gizmos.color = Color.green;
        Gizmos.DrawWireCube(attackOrigin + attackDir * attackRange, boxSize);
    }
}
  • 박스(Box) Raycast를 사용하여 범위내 적을 공격합니다.

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

image

Sword

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

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

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

            - Collider를 사용해서 공격판정 구현
            - SetHitBox() : Collider On/Off - 애니메이션 이벤트에 사용
 */
public class Sword : Weapon
{
    private BoxCollider hitBox;                 // 공격 판정

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

        hitBox = GetComponent<BoxCollider>();
    }

    // 공격 데미지 등
    public override void Attack()
    {
       
    }

    private void OnTriggerEnter(Collider other)
    {
        if(other.CompareTag("Monster"))
        {
            Monster monster = other.GetComponent<Monster>();
            monster.GetDamaged(DataManager.Instance.GetPlayerData().Damage);
        }
    }

    public override void SetHitBox(bool isEnabled)
    {
        hitBox.enabled = isEnabled;
    }
}


  • 콜라이더의 통해 공격의 성공여부를 판단하기위해 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 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)
            {
                // .. 몬스터에게 데미지
                monster.GetDamaged(DataManager.Instance.GetPlayerData().Damage);
            }
        }
    }

    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() 함수를 호출하기위한 메서드입니다.

Monster

몬스터가 데미지를 받는 함수를 추가했습니다.

// 공격받음
    public void GetDamaged(float damage)
    {
        float minDamage = damage * 0.8f;
        float maxDamage = damage * 1.2f;
        float randomDamage = Random.Range(minDamage, maxDamage);
        Debug.Log((int)randomDamage + "만큼의 데미지");
        curHp -= randomDamage;
    }

받은 데미지의 -20% ~ 20% 의 데미지를 받도록 했습니다.

댓글남기기