Skip to content

develop Enemy Spawn, Movement

Jeon-YJ1004 edited this page Mar 10, 2023 · 11 revisions

로직

  1. 적 sprite 준비. 에셋 스토어 1-1. 추가로 박쥐 sprite를 만들었습니다.
  2. 적 애니메이션 설정하기.
  3. 씬에 프리펩들을 저장할 배열 변수 선언 => PoolManager 생성
  4. 씬에 플레이어나 맵, 몬스터 재배치를 위한 스크립트인 GameManager 생성. PoolManager를 게임 전체에 사용할 수 있게 GameManager에 선언.
  5. --Enemies--에 소환 스크립트인 Enemy Spawner 추가

PoolManager

Instantiate와 Destroy는 생성, 삭제하면서 조각난 메모리가 계속해서 쌓이는데, 게임 도중 Garbage Collector가 실행되면 렉이 많이 걸린다. 이를 최적화하기 위해 오브젝트 풀링 사용한다.
미리 생성해둔 풀에서 활성화/비활성화로 사용.

  1. PoolManager 스크립트와 오브젝트를 생성

  2. 프리펩을 생성하여 저장할 배열 변수를 생성함. 한번에 등장가능한 개수를 고려해서 배열 할당(여기서는 몬스터 종류)
    image

  3. Instantiate를 사용해 생성한 인스턴스를 배열에 저장해 준다.

  4. 프리펩으로 객체 생성하고 pools[]에 저장

  5. 생성하면 좌표 0,0,0에 모두 생성되므로 모두 비활성화

  6. pools에서 사용하고 싶으면 GameManager.instance.pool.Get()을 사용하여 하나 꺼내오기.

  7. 설정들 활성화, 초기화 해주기

[PoolManager.cs]
using System.Collections.Generic;
using UnityEngine;

public class PoolManager : MonoBehaviour
{
    public GameObject[] prefabs; // 프리펩들을 보관할 변수.
    List<GameObject>[] pools; // 풀 담당을 하는 리스트들

    void Awake()
    {
        pools = new List<GameObject>[prefabs.Length];
        // 인스펙터에서 초기화
        for (int index = 0; index < pools.Length; index++)
            pools[index] = new List<GameObject>();
    }
    public GameObject Get(int index) //게임 오브젝트 반환 함수
    {
        GameObject select = null;
        //선택한 풀의 비활성화 된 게임 오브젝트 접근.
        // 발견하면 select 변수에 할당// 생성된 적이 죽을경우 
        
        foreach (GameObject item in pools[index])
        {
            if (!item.activeSelf)
            {
                select = item;
                select.SetActive(true);
                break;
            }
        }
        // 못찾으면 새롭게 생성후 할당// 모든 적이 죽지 않고 살아있음
        if (!select)
        {
            select = Instantiate(prefabs[index], transform);
            pools[index].Add(select);
        }

        return select;
    }
    public void Clear(int index)
    {
        foreach (GameObject item in pools[index])
            item.SetActive(false);
    }

    public void ClearAll()
    {
        for (int index = 0; index < pools.Length; index++)
            foreach (GameObject item in pools[index])
                item.SetActive(false);
    }
}

적 만들기

  1. 플레이어 추적 로직
  • 타겟 위치 - 나의 위치 = 위치 차이
  • 위치 차이의 정규화 = 방향
  • 프레임의 영향으로 결과가 달라지지 않도록 FixedDeltaTime 사용
  • 플레이어의 키 입력 값을 더한 이동 = 적의 방향 값을 더한 이동
 void FixedUpdate()
    {
        //몬스터가 살아 있을 때만 움직이도록 
        if (!isLive) return;

        Vector2 direction = (target.position - rb.position).normalized;
        Vector2 nextVec = direction * speed * Time.fixedDeltaTime; ;

        //플레이어의 키입력 값을 더한 이동=몬스터의 방향 값을 더한 이동
        rb.MovePosition(rb.position + nextVec);

        //물리 속도가 이동에 영향을 주지 않도록 속도 제거
        rb.velocity = Vector2.zero;
    }
  1. 적 스폰하기
  • 플레이어의 위치 값에 랜덤 값을 더해 적 스폰 지점 생성
 private void Spawn()
    {
        //player의 위치 값에 랜덤 pos를 더해 스폰 지점 설정
        Vector3 position = GenerateRandomPos();
        position += player.transform.position;

        GameObject newEnemy= GameManager.instance.pool.Get(0);
        newEnemy.transform.position = position;
        newEnemy.transform.parent = transform;
        newEnemy.GetComponent<Enemy>().Init(spawnData[level]);
    }

    private Vector3 GenerateRandomPos()
    {
        Vector3 position = new Vector3();
        float f = UnityEngine.Random.value > 0.5f ? -1f : 1f;
        if (UnityEngine.Random.value > 0.5f)
        {
            position.x = UnityEngine.Random.Range(-spawnArea.x, spawnArea.x);
            position.y = spawnArea.y * f;
        }
        else
        {
            position.y = UnityEngine.Random.Range(-spawnArea.y, spawnArea.y);
            position.x = spawnArea.x * f;
        }


        position.z = 0;
        return position;
    }
}
  1. 소환 레벨 적용하기
  • GameManager에 게임 시간과 최대게임 시간 변수 선언
 private void Update()
    {

        gameTime += Time.deltaTime;
        if (gameTime >maxGameTime)
        {
            gameTime = maxGameTime;
        }
    }
  • EnemySpawner에 시간에 맞춰 레벨이 올라가도록 작성
  • 적의 타입 클래스 생성 후 직력화하여 --Enemy-- 인스펙터에서 초기화

image

  • Enemy 스크립트에 RuntimeAnimatorController 배열 선언하여 각각의 애니메이션 선택
  • Enemy 스크립트의 Init 함수 생성하여 애니네이션과 SpawnData에서 사용할 변수 초기화
[System.Serializable]
public void Init(SpawnData data)  //각각의 몬스터 데이터 설정 함수
    {
        anim.runtimeAnimatorController = animcon[data.spriteType];
        speed = data.speed;
        maxHealth = data.health;
        health = data.health;
    }
  • EnemySpawner 스크립트에 timer 변수 생성, spawnTime를 이용해 레벨에 따라 소환 시간 바꾸게끔 설정
        timer += Time.deltaTime;
        //float형 시간에 따라 int형 레벨 설정
        level =Mathf.Min(Mathf.FloorToInt( GameManager.instance.gameTime / 10f),spawnData.Length-1);

        //레벨을 활용해 몬스터 각각의 소환 타이밍 변경하기
        if (timer >(spawnData[level].spawnTime))
        {
            Spawn();
            timer = 0;
        }
Clone this wiki locally