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

20. 근접형 기본 몬스터 공격 범위 수정

벌써 3번째 수정인가. 기존 공격 범위는 몬스터가 바라보는 방향으로 원뿔형이었다. 하지만 플레이어가 가까이 붙어있다면 공격 기능이 안 불리는 불상사가 벌어졌다. 원뿔형이다 보니 가까이 붙을수록 범위가 작아지는 것이다. 그래서 공격 범위만 직사각형으로 바꾸었다.

void Attack()
{
    Collider[] targets = Physics.OverlapBox(transform.position + monsterMove.directionVec.normalized,
                                            new Vector3(1f, 0.5f, 1f),
                                            Quaternion.Euler(monsterMove.attackAngle),
                                            targetMask);
    
    for(int i = 0; i < targets.Length; ++i)
    {
        playerStat = targets[i].GetComponent<PlayerStat>();

        playerStat.health -= monsterStat.damage;
    }

    attacking = false;
}

공격 함수가 호출될 때 OverlapBox를 사용하여 공격 기능을 만들었다. 중요한 건 OverlapBox의 위치와 각도이다. 몬스터가 바라보는 방향으로 적절하게 각도가 바뀌어야 공격 범위가 일정하기 때문.

각도는 몬스터의 이동 방향을 결정짓는 함수에서 각 방향마다 attackAngle 변수에 적절한 값을 대입해 결정지었다. 이렇게 하니 문제없이 해결되었다.

위에 보이는 노란색 네모 직사각형이 공격 범위이다. Scene에서 공격 범위를 보이게 하고싶었는데 저렇게 보이게 하는 방법을 몰랐었다.

private void OnDrawGizmos() {
    Gizmos.color = Color.yellow;
    if(!monsterMove) return;
    else Gizmos.DrawWireCube(transform.position + monsterMove.directionVec.normalized, new Vector3(1f, 0.5f, 1f));
}

Gizmos.DrawWireCube를 사용하면 위 사진처럼 상자 모양의 범위를 Scene에서 띄울 수 있다. 하지만 단점이 있는데 각도 설정은 못하기 때문에 OverlapBox와 완벽히 똑같게 그려지지는 않는다.

Gizmos.DrawWireMesh 사용하면 가능할지도 모르겠는데 일단은 보이니까 나중에 알아보기로 하자.

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

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

    private PlayerStat playerStat;
    private MonsterStat monsterStat;
    private MonsterMove monsterMove;
    private SphereCollider sphereCollider;
    private bool attack;
    public bool attackReadyAnime;

    public float attackReady = 0.5f;

    public float attackDelay = 3.0f;

    private float checkTime;

    private bool attacking;
    public bool attackAnime;

    private Rigidbody rigidbody;

    public float attackForce;

    public float reMove;


    private void Start() {
        maxDistance = 10.0f;

        monsterStat = GetComponent<MonsterStat>();
        sphereCollider = transform.Find("BasicAttack").GetComponent<SphereCollider>();
        monsterMove = GetComponent<MonsterMove>();
        rigidbody = GetComponent<Rigidbody>();
    }

    private void Update() {
        sphereCollider.radius = distance;

        DrawView();

        checkTime += Time.deltaTime;
    }

    private void FixedUpdate() {
        if(attack)
        {
            if(checkTime >= attackDelay)
            {
                transform.GetComponent<MonsterMove>().enabled = false;

                attackReadyAnime = true;

                StartCoroutine(BasicAttackCoroutine());

                attack = false;

                checkTime = 0;
            }
        }

        if(attacking && checkTime >= reMove)
        {
            if(!playerStat.isUnBeatTime)
                Attack();
            else if(playerStat.isUnBeatTime)
                Debug.Log("플레이어 무적 시간..");
        }
    }

    Vector3 DirFromAngle(float angleInDegrees)
    {
        if(monsterMove.directionVec == new Vector3(-1.14f, 0, 1.14f))//UpLeft
            angleInDegrees += 315.0f;
        if(monsterMove.directionVec == new Vector3(0, 0, 2.0f))//Up
            angleInDegrees += 0.0f;
        if(monsterMove.directionVec == new Vector3(1.14f, 0, 1.14f))//UpRight
            angleInDegrees += 45.0f;
        if(monsterMove.directionVec == new Vector3(-2.0f, 0, 0))//Left
            angleInDegrees += 270.0f;
        if(monsterMove.directionVec == new Vector3(2.0f, 0, 0))//Right
            angleInDegrees += 90.0f;
        if(monsterMove.directionVec == new Vector3(-1.14f, 0, -1.14f))//DownLeft
            angleInDegrees += 225.0f;
        if(monsterMove.directionVec == new Vector3(0, 0, -2.0f))//Down
            angleInDegrees += 180.0f;
        if(monsterMove.directionVec == new Vector3(1.14f, 0, -1.14f))//DownRight
            angleInDegrees += 135.0f;

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

    public void DrawView()
    {
        //경계선(Boundary)
        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(monsterMove.directionVec.normalized, dirToTarget) > Mathf.Cos((viewAngle / 2) * Mathf.Deg2Rad))
            {
                playerStat = targets[i].GetComponent<PlayerStat>();

                attack = true;
            }
        }
        
        if(targets.Length <= 0)
        {
            attack = false;
        }

    }

    void Attack()
    {
        Collider[] targets = Physics.OverlapBox(transform.position + monsterMove.directionVec.normalized,
                                                new Vector3(1f, 0.5f, 1f),
                                                Quaternion.Euler(monsterMove.attackAngle),
                                                targetMask);
        
        for(int i = 0; i < targets.Length; ++i)
        {
            playerStat = targets[i].GetComponent<PlayerStat>();

            playerStat.health -= monsterStat.damage;
        }

        attacking = false;

    }

    private void OnDrawGizmos() {
        Gizmos.color = Color.yellow;
        if(!monsterMove) return;
        else Gizmos.DrawWireCube(transform.position + monsterMove.directionVec.normalized, new Vector3(1f, 0.5f, 1f));
    }

    IEnumerator BasicAttackCoroutine()
    {
        yield return new WaitForSeconds(attackReady);

        attackReadyAnime = false;

        attacking = true;

        attackAnime = true;

        rigidbody.AddForce(monsterMove.directionVec.normalized * attackForce, ForceMode.Impulse);

        StartCoroutine(ReMove());
    }

    IEnumerator ReMove()
    {
        yield return new WaitForSeconds(reMove);

        attacking = false;
        checkTime = 0;

        attackAnime = false;

        transform.GetComponent<MonsterMove>().enabled = true;

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

        if(targets.Length > 0)
        {
            Vector3 temp = transform.position - targets[0].transform.position;
            float direction = Mathf.Atan2(temp.z, temp.x) * Mathf.Rad2Deg;

            monsterMove.Direction(direction);

            attack = true;
        }
    }

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

답글 남기기

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