-
Notifications
You must be signed in to change notification settings - Fork 2
develop Enemy Spawn, Movement
Jeon-YJ1004 edited this page Mar 10, 2023
·
11 revisions
- 적 sprite 준비. 에셋 스토어 1-1. 추가로 박쥐 sprite를 만들었습니다.
- 적 애니메이션 설정하기.
- 씬에 프리펩들을 저장할 배열 변수 선언 => PoolManager 생성
- 씬에 플레이어나 맵, 몬스터 재배치를 위한 스크립트인 GameManager 생성. PoolManager를 게임 전체에 사용할 수 있게 GameManager에 선언.
- --Enemies--에 소환 스크립트인 Enemy Spawner 추가
Instantiate와 Destroy는 생성, 삭제하면서 조각난 메모리가 계속해서 쌓이는데, 게임 도중 Garbage Collector가 실행되면 렉이 많이 걸린다. 이를 최적화하기 위해 오브젝트 풀링 사용한다.
미리 생성해둔 풀에서 활성화/비활성화로 사용.
-
PoolManager 스크립트와 오브젝트를 생성
-
프리펩을 생성하여 저장할 배열 변수를 생성함. 한번에 등장가능한 개수를 고려해서 배열 할당(여기서는 몬스터 종류)
-
Instantiate를 사용해 생성한 인스턴스를 배열에 저장해 준다.
-
프리펩으로 객체 생성하고 pools[]에 저장
-
생성하면 좌표 0,0,0에 모두 생성되므로 모두 비활성화
-
pools에서 사용하고 싶으면 GameManager.instance.pool.Get()을 사용하여 하나 꺼내오기.
-
설정들 활성화, 초기화 해주기
[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);
}
}
- 플레이어 추적 로직
- 타겟 위치 - 나의 위치 = 위치 차이
- 위치 차이의 정규화 = 방향
- 프레임의 영향으로 결과가 달라지지 않도록 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;
}
- 적 스폰하기
- 플레이어의 위치 값에 랜덤 값을 더해 적 스폰 지점 생성
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;
}
}
- 소환 레벨 적용하기
- GameManager에 게임 시간과 최대게임 시간 변수 선언
private void Update()
{
gameTime += Time.deltaTime;
if (gameTime >maxGameTime)
{
gameTime = maxGameTime;
}
}
- EnemySpawner에 시간에 맞춰 레벨이 올라가도록 작성
- 적의 타입 클래스 생성 후 직력화하여 --Enemy-- 인스펙터에서 초기화
- 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;
}