본문 바로가기
공부/Unity 기초

3D FPS - 11 / 적 시야각

by svcbn 2022. 5. 23.

Enemy가 대기(Idle) 상태일 때 플레이어를 감지하는 시야각을 전방의 좌우 각각 45도로 제한하는 기능을 구현.

 

 

bool 형으로 각도를 확인하는 함수를 만들기

bool CheckPlayerAngle(Vector3 position)
    {
        // 체크하려는 대상을 바라보는 벡터를 구한다.
        Vector3 direction = position - transform.position;

        // 나의 정면 벡터와 앞에서 구한 벡터를 비교해서 사잇각을 구한다.
        float checkDegree = Vector3.Angle(transform.forward, direction);

        // 구해진 각도가 45도 이내라면 true, 45도 밖이면 false라고 반환한다.
        if (checkDegree <= sightDegree)
        {
            return true;
        }
        else
        {
            return false;
        }

    }

 

 

 

 

 

 

UpdateIdle에서 CheckPlayerAngle을 호출한다.

private void UpdateIdle()
    {
        // 나와 target의 거리를 구해서
        float distance = Vector3.Distance(transform.position, target.transform.position);

        bool isInSight = CheckPlayerAngle(target.transform.position);

        // 만약 그 거리가 감지거리보다 작으면
        if (distance < findDistance && isInSight)
        {
            // Move상태로 전이하고싶다.
            state = State.Move;

            agent.destination = target.transform.position;

        }

    }

 

 

 

 

 

Enemy 전문

더보기
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.AI;

// FSM으로 상태를 제어하고싶다.
// 정지, 이동, 공격, 죽음
public class Enemy : MonoBehaviour
{

    public float speed = 1;
    public float findDistance = 5;
    public float attackDistance = 1.5f;
    public float traceDistance = 15;
    public float sightDegree = 45;
    
    public enum State
    {
        Idle,
        Move,
        Attack,
        Die,
        Return
    }

    NavMeshAgent agent;
    public State state;
    PlayerHP php;

    GameObject target;

    float currentTime = 0;
    float attackTime = 1;

    Vector3 originPosition;

    // Start is called before the first frame update
    void Start()
    {
        agent = GetComponent<NavMeshAgent>();
        
        state = State.Idle;

        target = GameObject.Find("Player");

        php = target.GetComponent<PlayerHP>();

        // 최초 위치를 originPosition 변수에 저장한다.
        originPosition = transform.position;


    }

    // Update is called once per frame
    void Update()
    {
        if (state == State.Idle)
        {
            UpdateIdle();
        }

        else if (state == State.Move)
        {
            UpdateMove();
        }

        else if (state == State.Attack)
        {
            UpdateAttack();
        }

        else if (state == State.Return)
        {
            UpdateReturn();
        }
    }

    
    private void UpdateIdle()
    {
        // 나와 target의 거리를 구해서
        float distance = Vector3.Distance(transform.position, target.transform.position);

        bool isInSight = CheckPlayerAngle(target.transform.position);

        // 만약 그 거리가 감지거리보다 작으면
        if (distance < findDistance && isInSight)
        {
            // Move상태로 전이하고싶다.
            state = State.Move;

            agent.destination = target.transform.position;

        }

    }

    bool CheckPlayerAngle(Vector3 position)
    {
        // 체크하려는 대상을 바라보는 벡터를 구한다.
        Vector3 direction = position - transform.position;

        // 나의 정면 벡터와 앞에서 구한 벡터를 비교해서 사잇각을 구한다.
        float checkDegree = Vector3.Angle(transform.forward, direction);

        // 구해진 각도가 45도 이내라면 true, 45도 밖이면 false라고 반환한다.
        if (checkDegree <= sightDegree)
        {
            return true;
        }
        else
        {
            return false;
        }

    }

    
    private void UpdateMove()
    {
        // 현재 위치가 최대 추적 가능 거리를 넘어갔는지 여부를 판단한다.
        float trace = Vector3.Distance(originPosition, transform.position);

        // 만일, 최대 추적 가능 거리를 넘어갔다면 상태를 복귀 상태로 전환한다.
        if (trace > traceDistance)
        {
            state = State.Return;
        }


        // target방향으로 이동하다가
        //Vector3 dir = target.transform.position - transform.position;
        //dir.Normalize();
        //transform.position += dir * speed * Time.deltaTime;
        agent.destination = target.transform.position;

        // 나와 target의 거리를 구해서
        float distance = Vector3.Distance(transform.position, target.transform.position);

        // 만약 그 거리가 공격거리보다 작으면
        if (distance < attackDistance)
        {
            // Attack으로 전이하고싶다.
            state = State.Attack;
            agent.isStopped = true;
        }


    }

    private void UpdateAttack()
    {
        // 시간이 흐르다가
        currentTime += Time.deltaTime;

        // 만약 현재시간이 공격시간이 되면
        if (currentTime > attackTime)
        {
            // 현재시간을 초기화하고
            currentTime = 0;

            

            // 만약 target이 공격거리 밖에 있으면 Move상태로 전이하고싶다.
            // 나와 target의 거리를 구해서
            float distance = Vector3.Distance(transform.position, target.transform.position);

            // 만약 그 거리가 공격거리보다 크다면
            if (distance > attackDistance)
            {
                // Move상태로 전이하고싶다.
                state = State.Move;
                agent.isStopped = false;

            }

            // 공격 성공
            else
            {
                // 플레이어를 공격하고
                php.AddDamage();
                

            }


        }

    }

    
    public void AddDamage (int damage)
    {
        agent.isStopped = true;
        Destroy(gameObject);

    }

    private void OnDestroy()
    {
        EnemyManager.instance.COUNT--;
    }

    void UpdateReturn()
    {
        // 만일, 나의 현재 위치가 originPosition 위치에서 10센티미터 이상이라면
        Vector3 dir = originPosition - transform.position;

        if (dir.magnitude > 0.1f)
        {
            // originPosition 방향으로 이동한다.
            transform.position += dir.normalized * speed * Time.deltaTime;
        }


        // 그렇지 않다면, 나의 위치를 originPosition으로 설정한다.
        else
        {
            transform.position = originPosition;

            // 상태를 다시 대기 상태로 전환한다.
            state = State.Idle;

        }

        // 나와 target의 거리를 구해서
        //float distance = Vector3.Distance(transform.position, target.transform.position);

        // 복귀 중 감지거리 안으로 Player가 들어오면
        //if (distance < findDistance)
        {
            // 상태를 Move 상태로 전이한다.
            //state = State.Move;
        }

        
    }
}