Posted in: Unity, 프로젝트 When the Spring comes

9. 플레이어 기본 근접 공격 수정 및 개선

저번에 만들었던 공격 기능에서 몬스터 체력을 깎는 부분이 잘 호출이 안되었다. 혹시나 리소스를 많이 잡아먹는 코드가 있어서 렉이 걸리는 건가 싶었지만 그런것도 아니었다. 이틀동안 문제를 못찾아 여기다 싶은 부분은 모두 콘솔에 출력을 해보았다. 그리고 문제를 찾았다.

최근에 구매한 키보드가 입력 세기를 감지(0.1 · 0.2 · 0.3 … 0.8 · 0.9 · 1)하는 키보드여서 Input.GetKeyDown 메소드가 감지가 안되었던 것이다. 키보드가 완전히 눌려지지 않으면 1을 반환하지 않고 0.5 이런 값을 반환하니까 GetKeyDown이 불리지 않았던 것이다.

정말 상상도 못한 에러였다… 이런 경우가 있구나ㅋㅋㅋ

private void OnTriggerStay(Collider other) {
    if(other.gameObject.layer == LayerMask.NameToLayer("Monster"))
    {
        FindVisibleTargets();
    }
}

그리고 에러를 찾는 과정에서 코드도 더 깔끔해졌다. 저번 글에서는 가장 처음에 만든 코드여서 좀 다르긴 하지만 원래 FixedUpdate에서 지속적으로 FindVisibleTargets을 호출하여 범위안에 들어온 몬스터를 알아내었다. 그 과정에서 OverlapSphere이 계속 호출되는데 아무래도 효율면에선 좋지 못하였다.

FindVisibleTargets를 몬스터 감지용으로 사용할 SphereCollider OnTriggerStay에서 호출되게 하였다. 그리고 스피어콜라이더의 반지름을 공격범위 거리와 같게 하였다.

public void FindVisibleTargets() {
    Collider[] targets = Physics.OverlapSphere(transform.position, distance, targetMask);

    for(int i = 0; i < targets.Length; ++i)
    {
        Vector3 dirToTarget = (targets[i].transform.position - transform.position).normalized;

        if(Vector3.Dot(GameManager.GetInstance().playerDirection.normalized, dirToTarget) > Mathf.Cos((viewAngle / 2) * Mathf.Deg2Rad))
        {
            monsterStat = targets[i].GetComponent<MonsterStat>();

            attack = true;
            Debug.DrawLine(transform.position, targets[i].transform.position, Color.red);
        }
        else
        {
            attack = false;
        }
    } 
}

몬스터가 SphereCollider와 Trigger되면 FindVisibleTargets 함수를 호출해 몬스터를 감지하고 플레이어 공격 범위안에 몬스터가 들어온다면 공격 가능하도록 바꾸었다.

매순간 OverlapSphere이 호출되는 것보다 훨씬 괜찮은 방법이라고 생각한다. 거기에다 Scene에서 직관적으로 범위를 확인할 수 있으니 마음에든다.

OnTriggerStay로 계속 호출하는 것보다 OnTriggerEnter와 OnTriggerExit로 더 효율적인 코드를 작성할 수 있을 것 같은데 몬스터가 공격 범위안에 들어온건지 체크하기가 복잡해질수도 있겠다고 생각한다. 다음에 건들여봐야겠다.

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Weapon_1 : MonoBehaviour
{
    public float viewAngle;
    public float distance;
    private float maxDistance;
    public LayerMask targetMask;

    public MonsterStat monsterStat;
    public PlayerStat playerStat;
    public bool attack;

    private void Start() {
        maxDistance = 10.0f;
        
        playerStat = GetComponent<PlayerStat>();
        GetComponent<SphereCollider>().radius = distance;
    }

    private void Update() {
        DrawView();
    }

    private void FixedUpdate() {
        //공격에 시간을 주어 광클이 안되도록 나중에 제어하자.
        if(Input.GetKey(KeyCode.K))
        {
            if(attack)
            {
                BasicAttack(monsterStat);
            }
        }
    }

    public Vector3 DirFromAngle(float angleInDegrees) {

        if(GameManager.GetInstance().playerDirection == new Vector3(-1.14f, 0, 1.14f))
            angleInDegrees += GameManager.GetInstance().attackAngle;
        if(GameManager.GetInstance().playerDirection == new Vector3(0, 0, 2.0f))
            angleInDegrees += GameManager.GetInstance().attackAngle;
        if(GameManager.GetInstance().playerDirection == new Vector3(1.14f, 0, 1.14f))
            angleInDegrees += GameManager.GetInstance().attackAngle;
        if(GameManager.GetInstance().playerDirection == new Vector3(-2.0f, 0, 0))
            angleInDegrees += GameManager.GetInstance().attackAngle;
        if(GameManager.GetInstance().playerDirection == new Vector3(2.0f, 0, 0))
            angleInDegrees += GameManager.GetInstance().attackAngle;
        if(GameManager.GetInstance().playerDirection == new Vector3(-1.14f, 0, -1.14f))
            angleInDegrees += GameManager.GetInstance().attackAngle;
        if(GameManager.GetInstance().playerDirection == new Vector3(0, 0, -2.0f))
            angleInDegrees += GameManager.GetInstance().attackAngle;
        if(GameManager.GetInstance().playerDirection == new Vector3(1.14f, 0, -1.14f))
            angleInDegrees += GameManager.GetInstance().attackAngle;

        return new Vector3(Mathf.Sin(angleInDegrees * Mathf.Deg2Rad), 0, Mathf.Cos(angleInDegrees * Mathf.Deg2Rad));
    }

    public void DrawView() {
        Vector3 leftBoundary = DirFromAngle(-viewAngle / 2);
        Vector3 rightBoundary = DirFromAngle(viewAngle / 2);

        Debug.DrawLine(transform.position, transform.position + leftBoundary * distance, Color.green);
        Debug.DrawLine(transform.position, transform.position + rightBoundary * distance, Color.green);
        Debug.DrawLine(transform.position + leftBoundary * distance, transform.position + rightBoundary * distance, Color.green);
    }

    public void FindVisibleTargets() {
        Collider[] targets = Physics.OverlapSphere(transform.position, distance, targetMask);

        for(int i = 0; i < targets.Length; ++i)
        {
            Vector3 dirToTarget = (targets[i].transform.position - transform.position).normalized;

            if(Vector3.Dot(GameManager.GetInstance().playerDirection.normalized, dirToTarget) > Mathf.Cos((viewAngle / 2) * Mathf.Deg2Rad))
            {
                monsterStat = targets[i].GetComponent<MonsterStat>();

                attack = true;
                Debug.DrawLine(transform.position, targets[i].transform.position, Color.red);
            }
            else
            {
                attack = false;
            }
        } 
    }

    void BasicAttack(MonsterStat monster) {
        monster.health -= playerStat.damage;
        Debug.Log("Basic Attack Call | " + monster.health);
    }

    private void OnTriggerStay(Collider other) {
        if(other.gameObject.layer == LayerMask.NameToLayer("Monster"))
        {
            FindVisibleTargets();
        }
    }
}

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 항목은 *(으)로 표시합니다