Unity 자동 공격 포탑(Automatic Attack Turret) 구현

Unity Version : 2021.3.5f1 

나중에 모델과 이펙트까지 붙이면 될 것 같다.

적에게 피해를 입히는 방법은 어떤식으로 구현하면 좋을지 생각해봐야 할 듯

추가적으로 이 방법은 범위에 들어온 적 1기만 공격할 수 있지만

여러명을 공격하는 포탑에는 적용시킬 수 없을 듯하다. (Physics.OverlapSphere 사용을 생각 중)

사용한 방법은

콜라이더를 이용하여 주변에 적을 식별(layer로 필터)하고 타겟팅합니다.

물체와 자신과의 거리를 계산하여 특정 거리 이상으로 멀어지면 타겟팅을 해제하고 다시 대기모드로 변경합니다.

구현하면서 나온 문제는 중심거리는 멀어져서 타겟팅이 해제되지만 충돌범위에 속하여 오류가 발생합니다.

-> 해결 방법은 포탑의 탐색길이(파랑)와 타겟팅된 물체의 콜라이더 길이(노랑)의 합보다 중심거리가(빨강) 길면 해결됩니다.

뒤 생각없이 구현한 포탑 코드

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

public enum eTurretState
{
    Nono = -1,
    idle,
    attack
}

public class turret : MonoBehaviour
{
    [SerializeField]
    public GameObject m_TurretTarget;

    [SerializeField]
    private eTurretState m_eTurretState = eTurretState.Nono;

    [SerializeField]
    public float m_AttackRange;
    
    [SerializeField]
    public float m_TurnSpeed;

    [SerializeField]
    private float m_AttackSpeed;

    [SerializeField]
    private float m_AttackCoolDown;

    public GameObject testHead;
    public GameObject RangeSqhere;


    private void OnTriggerEnter(Collider other)
    {
        if (other.gameObject.layer == 20)
        {
            this.m_TurretTarget = other.gameObject; // 터렛의 공격 대상 오브젝트에 추가
            // 공격 범위 콜라이더를 OFF;
            SphereColliderActive(false);
            StartCoroutine(this.TargettingCoroutine());
            this.m_eTurretState = eTurretState.attack;
        }

    }

    /// <summary>
    /// 터렛의 적을 식별하는 콜라이더의 크기를 변경합니다.
    /// </summary>
    /// <param name="state"></param>
    private void SphereColliderActive(bool state)
    {
        if (state) { this.RangeSqhere.transform.localScale = new Vector3(this.m_AttackRange * 2, 0.0001f, this.m_AttackRange * 2); }
        else { this.RangeSqhere.transform.localScale = new Vector3(0.001f, 0.0001f, 0.001f); }
    }

    /// <summary>
    /// 터렛이 지속적으로 적을 타겟팅하는 코르틴
    /// </summary>
    /// <returns></returns>
    private IEnumerator TargettingCoroutine()
    {
        while (true)
        {
            // 만약 터렛의 타겟이 Null이거나 터렛과 
            if (m_TurretTarget == null || this.m_TurretTarget.gameObject.layer != 20 || Vector3.Distance(this.m_TurretTarget.transform.position, this.transform.position) > this.m_AttackRange + 2.0f)
            {
                this.m_eTurretState = eTurretState.idle;
                SphereColliderActive(true);
                break;
            }
            else
            {
                this.testHead.transform.rotation = Quaternion.Lerp(this.testHead.transform.rotation, Quaternion.LookRotation(this.m_TurretTarget.transform.position - this.transform.position), Time.deltaTime * this.m_TurnSpeed);
            }

            yield return null;
        }
    }

    public void SearchEnemy()
    {

    }



    public void AttackEnemy()
    {
        Debug.Log("Attack");
        //m_TurretTarget.GetComponent<TestEnemy>().BeDamaged(this.Damage);

    }

    private void Start()
    {
        // 샘플

        this.RangeSqhere.transform.localScale = new Vector3(this.m_AttackRange * 2, 0.0001f, this.m_AttackRange * 2);

    }
    private void Update()
    {
        if (this.m_AttackCoolDown > 0f) {
            this.m_AttackCoolDown -= Time.deltaTime; }

        if (m_eTurretState == eTurretState.attack && this.m_AttackCoolDown <= 0f && this.m_TurretTarget != null)
        {
            this.m_AttackCoolDown = this.m_AttackSpeed;
            AttackEnemy();
        }
    }
}

댓글 달기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다

위로 스크롤