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

3D FPS - 10 / 적 생성 유지

by svcbn 2022. 5. 21.

 

 

적을 일정 시간마다 생성하는 코루틴 작성

 IEnumerator Start()
    {
        while(true)
        {
            if (count < maxCount)
            {

                // 적공장에서 적을 생성
                GameObject enemy = Instantiate(EnemyFactory);

                // 내 위치에 가져다 놓고싶다.
                enemy.transform.position = this.transform.position + new Vector3(Random.value * 2, 0, Random.value * 2);

                // 내 방향과 일치시키고 싶다.
                enemy.transform.rotation = transform.rotation;

                count++;

                // 생성시간동안 대기하고싶다.
                yield return new WaitForSeconds(createTime);
            }
        }

    }

 

 

 

 

 

 

 

 

최대 2개체까지만 생성되게 하며, 적 처치시 추가 생성하는 함수 호출을 위해 싱글톤으로 설정

public static EnemyManager instance;
    private void Awake()
    {
        instance = this;
    }

    public float createTime = 2;
    public GameObject EnemyFactory;
    public int maxCount = 2;

    int count;
    
    public int COUNT
    {
        get { return count; }
        set {
            count = value; 
            if (count < 0)
            {
                count = 0;
            }
            if (count > maxCount)
            {
                count = maxCount;
            }
        
        }
    }

maxCount와 count 사이의 생길 수 있는 수치상 오류를 방지하기 위해 COUNT 설정

 

 

 

EnemyManager 전문

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


// 일정시간마다 적공장에서 적을 생성해서 내 위치에 가져다놓고 싶다.
public class EnemyManager : MonoBehaviour
{
    public static EnemyManager instance;
    private void Awake()
    {
        instance = this;
    }

    public float createTime = 2;
    public GameObject EnemyFactory;
    public int maxCount = 2;

    int count;
    
    public int COUNT
    {
        get { return count; }
        set {
            count = value; 
            if (count < 0)
            {
                count = 0;
            }
            if (count > maxCount)
            {
                count = maxCount;
            }
        
        }
    }

    

    // Start is called before the first frame update
    IEnumerator Start()
    {
        while(true)
        {
            if (count < maxCount)
            {

                // 적공장에서 적을 생성
                GameObject enemy = Instantiate(EnemyFactory);

                // 내 위치에 가져다 놓고싶다.
                enemy.transform.position = this.transform.position + new Vector3(Random.value * 2, 0, Random.value * 2);

                // 내 방향과 일치시키고 싶다.
                enemy.transform.rotation = transform.rotation;

                count++;

                // 생성시간동안 대기하고싶다.
                yield return new WaitForSeconds(createTime);
            }
        }

    }

    // Update is called once per frame
    void Update()
    {
        
    }
}

 

 

 

 

 

파괴될 경우 count 감소를 위해 Enemy에서 COUNT호출

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

    }

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

 

 

 

 

Enemy 전문

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

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

    public float speed = 1;
    
    public float findDistance = 5;
    public float attackDistance = 1.5f;
    public float traceDistance = 15;

    
    enum State
    {
        Idle,
        Move,
        Attack,
        Die,
        Return
    }

    State state;
    PlayerHP php;

    GameObject target;

    float currentTime;
    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);

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

            agent.destination = target.transform.position;

        }

    }

    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;
        }

        
    }
}

'공부 > Unity 기초' 카테고리의 다른 글

3D FPS - 12 / 모델링 적용  (0) 2022.05.23
3D FPS - 11 / 적 시야각  (0) 2022.05.23
3D FPS - 9 / 네비게이션 알고리즘으로 길찾기  (0) 2022.05.21
3D FPS - 8 / 게임 오버 처리  (0) 2022.05.19
3D FPS - 7 / 피격 처리  (0) 2022.05.19