K-digital traning/Final Project

Gazzlers 개발일지 - Enemy생성

내꺼블로그 2023. 12. 4. 16:28

원작에 의하면 한 화면에서 Enemy가 최대 2마리 생성됨을 확인해볼 수 있다.

한 마리씩 텀을 두고 생성되며 enemy가 일정수량(wave 당 생성되는 enemy 수) 생성될 때까지 2마리씩 화면에 나타난다.

 

먼저 게임이 시작될 때 텀을 두고 enemy 2마리가 생성되는 부분을 구현해보쟈.

 

 

TestEnemy1MoveMain의 Enemy1Move 필드를 arr로 바꾸어 2마리를 관리하도록 한다.

 

 

Start method에서는 arrEnemys를 순회하면서 각각의 enemy에게 접근하여 onChangeTarget 대리자를 선언한다.

그 다음 처음 시작할 때 enemy들의 spawn 위치를 정해주는 CoStartSpawnEnemy를 호출한다.

 

 

 

 

CoSpawnEnemy는 5초 간격으로 각 enemy마다 스폰 위치와 타겟을 정해준다.

 

 

 

 

??????? 욕해도 되나,,,,

 

 

 

ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ

 

요 안에 있는 map 게임오브젝트는 어떤아이냐면요,,,,

 

 

 

요 아이랍니다~ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ

TestMap에서 main역할을 하던 아이였습니댜!

어쩐지 rail도 늘어나고 player도 enemy쪽으로 오더라닠ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ map을 실수로 enemy안에다가 생성해버렸나봅니댴ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ(미쳐가는중)

 

 

 

 

 

 

map을 없애고 다시 실행

 

 

 

휴,,, 훨 낫네요,,,,,,

아직 속도 변속도 없거니와 지들끼리 겹쳐지는 경우도 있지만 우선 구현하려는 부분은 구현이 되었습니다!!!짝짝짝!!!!!!!!

 

 

 

 

 

 

enemy를 생성하고 사라질(죽을) 때마다도 등장하게 하기.

 

먼저 enemypool 생성

 

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

    public class EnemyPoolManager : MonoBehaviour
    {
        public static EnemyPoolManager Instance;

        [SerializeField] private GameObject enemyPrefab;
        private List<Enemy1Move> enemyPool = new List<Enemy1Move>();
        // Start is called before the first frame update
        void Awake()
        {
            Instance = this;
        }

        private void Start()
        {
            this.GenerateEnemy();
        }

        private void GenerateEnemy()
        {
            for(int i = 0; i < 2; i++)
            {
                GameObject go = Instantiate(enemyPrefab, this.transform);
                go.SetActive(false);
                enemyPool.Add(go.GetComponent<Enemy1Move>());
            }
        }

        public Enemy1Move EnableEnemy()
        {
            Enemy1Move result = null;
            for(int i=0;i<enemyPool.Count;i++) {
                if (enemyPool[i].gameObject.activeSelf == false)
                {
                    result = enemyPool[i];
                    result.gameObject.SetActive(true);
                    result.transform.SetParent(null);
                    break;
                }
            }
            return result;
        }

        public void DisableEnemy(Enemy1Move enemy)
        {
            enemy.gameObject.SetActive(false);
            enemy.transform.SetParent(this.transform);
        }
    }

 

 

 

 

TestEnemy1MoveMain에서 enemy 생성(pool에서 갖고오기)

 

enemy(Enemy1Move)를 관리할 listEnemys와 enemy들의 수를 정해주는 EnemyCount를 선언.

 

 

기존 foreach로 enemy 순회했던 부분 제거

 

 

 

기존 코드에서 foreach로 순회하면서 onChangeTarget 선언했던 부분을 CoStartSpawnEnemy에서 선언.

 

 

 

 

enemy가 동적으로 나타난다. 그러나 위의 영상에서 enemy가 생성될 때 이미 있는 enemy자리 근처에 생성되는 것이 확인되었다.

enemy가 서로 겹치도록 생성되는 것을 막고자 array를 구현해놓아야 할 듯 싶다.

적어도 x좌표는 겹치지 않는 편이 미관상으로도, 코드 구현으로도 좋으니 x좌표를 기준으로 array를 선언하여 구현해보쟈.

 

 

 

 

 

x좌표 단위로 (-3, -1), (-1, 1), (1, 3)으로 나누어 각각의 위치에 enemy가 있는지 없는지 파악. enemy가 존재할 경우 isFilled[idx]를 true로 변경.

 

 

 

isFilled가 false인 idx 위주로 listIndex에 삽입.

Random.Range를 사용하여 listIndex로부터 arrTargetPos로 접근할 index를 랜덤하게 추출.

enemy의 position.x는 arrTargetPos[idx]의 position.x를 사용, enemy의 position.z는 10부터 20까지의 수 중 랜덤하게 추출.

위의 값들을 활용하여 player의 위치에 대한 상대적인 위치로 pos를 계산한 뒤 enemy에게 부여.

 

 

 

결과

 

 

 

 

 

 

+ SpawnEnemy 수정 => overlapsphere 사용해보기

 

UpdateFillEnemy 제거

 

Random.Range를 사용하여 임의의 좌표값을 설정.

OverlapShpereNonAlloc를 사용하여 물리적 충돌 발생 갯수를 num에 저장.

num==0이면 즉, enemy를 생성시키고자 하는 위치 주변으로 충돌되는 물체가 존재하지 않으면 break, 그렇지 않으면 계속해서 while문을 돌리면서 적절한 위치 탐색.

while문을 빠져나오면 그 안에서 얻은 값을 토대로 enemy의 위치 지정.

설정한 layer들은 차례로 monster(enemy), car, obstacle이다.

 

 

 

 

 

 

 

enemy와 tree에게 각각 Monster, Obstacle 레이어를 할당.

 

 

 

 

 

결과

 

 

 

 

앞의 장애물 피하는 부분을 아직 구현을 못해서,,, 어떻게든 해보아야지,,,,,,,,,,,,

 

 

 

 

코드

using System.Collections;
using System.Collections.Generic;
using Unity.VisualScripting;
using UnityEngine;


    public class TestEnemy1MoveMain : MonoBehaviour
    {
        [SerializeField] private Transform[] arrTargetPos;
        private List<Enemy1Move> listEnemys = new List<Enemy1Move>();
        private List<bool> isTargeted = new List<bool>();
        private int EnemyCount = 2;
        // Start is called before the first frame update
        void Start()
        {
            for(int i = 0; i < arrTargetPos.Length; i++)
            {
                isTargeted.Add(false);
            }
            //foreach (Enemy1Move enemy in listEnemys)
            //{
            //    enemy.onChangeTarget = (idx) =>
            //    {
            //        Debug.Log("target change!");
            //        Debug.LogFormat("<color=yellow>{0} preTargetPos: {1}</color>", enemy.name, arrTargetPos[idx]);
            //        this.SetTargetPos(enemy);
            //        this.isTargeted[idx] = false;
            //    };
            //}
            this.StartCoroutine(this.CoStartSpawnEnemy());
        }

        private IEnumerator CoStartSpawnEnemy()
        {
            //foreach (Enemy1Move enemy in listEnemys)
            //{
            //    yield return new WaitForSeconds(5f);
            //    this.SpawnEnemy(enemy);
            //    this.SetTargetPos(enemy);
            //}
            for(int i = 0; i < this.EnemyCount; i++)
            {
                yield return new WaitForSeconds(5f);
                Enemy1Move enemy = EnemyPoolManager.Instance.EnableEnemy();
                this.listEnemys.Add(enemy);
                enemy.onChangeTarget = (idx) =>
                {
                    Debug.Log("target change!");
                    Debug.LogFormat("<color=yellow>{0} preTargetPos: {1}</color>", enemy.name, arrTargetPos[idx]);
                    this.SetTargetPos(enemy);
                    this.isTargeted[idx] = false;
                };
                this.SpawnEnemy(enemy);
                this.SetTargetPos(enemy);
            }
        }

        private void SpawnEnemy(Enemy1Move enemy)
        {
            float x = Random.Range(-3f, 3f);
            float z = Random.Range(10f, 20f);
            while (true)
            {
                int layer = 1 << 3 | 1 << 6 | 1 << 7;
                //Debug.Log(layer.ToBinaryString());
                //Debug.Log(enemy.gameObject.layer.ToString());
                Collider[] hit = new Collider[10];
                int num = Physics.OverlapSphereNonAlloc(this.arrTargetPos[2].position + Vector3.right * x
                    + Vector3.forward * z, 2f, hit, layer);
                if (num == 0)
                    break;
                x = Random.Range(-3f, 3f);
                z = Random.Range(10f, 20f);
            }
            
            Vector3 pos = this.arrTargetPos[2].position + Vector3.right * x + Vector3.forward * z;
            enemy.Init(pos);
        }

        private void SetTargetPos(Enemy1Move enemy)
        {
            List<int> listIndex = new List<int>();
            for(int i = 0; i < this.isTargeted.Count; i++)
            {
                if (!isTargeted[i])
                {
                    listIndex.Add(i);
                }
            }
            int rand = Random.Range(0, listIndex.Count);
            int idx = listIndex[rand];
            enemy.UpdateTargetPos(idx, this.arrTargetPos[idx]);
            this.isTargeted[idx] = true;
            Debug.LogFormat("<color=magenta>{0} curTargetPos: {1}</color>", enemy.name, arrTargetPos[idx]);
        }
    }