지난번에 이어 게임을 만들어보자.
이번에 참고할 강의는 아래 강의이다.
https://www.youtube.com/watch?v=ajwRvAGKl_k&list=PLctzObGsrjfyevwpeEVQ9pxGVwZtS7gZK&index=6
이번 강의에서는 적 스폰(소환)시스템을 만들어본다.
시작해보자.
적을 소환하기 위한 스크립트와 빈 게임 오브젝트 Spawner 를 만들자.
Spawner 오브젝트의 위치를 중앙으로 설정하고 Spawner 스크립트를 할당해주자.
| Spawner 스크립트
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Spawner : MonoBehaviour
{
public Wave[] waves; // 웨이브들을 저장할 배열 생성
public Enemy enemy; // 스폰할 적 레퍼런스
Wave currentWave; // 현재 웨이브 레퍼런스
int currentWaveNumber; // 현재 웨이브 번호
int enemiesRemainingToSpawn; // 남아있는 스폰 할 적 수
float nextSpawnTime; // 다음 스폰까지의 시간
void Start()
{
NextWave(); // 시작 시 웨이브 실행
}
void Update()
{
if(enemiesRemainingToSpawn > 0 && Time.time >= nextSpawnTime)
// 적 수가 남아있고 현재 시간이 다음 스폰 시간보다 큰 경우
{
enemiesRemainingToSpawn--; // 적 수를 하나 줄임
nextSpawnTime = Time.time + currentWave.timeBetweenSpawns;
// 다음 스폰 시간을 현재시간 + 스폰 간격 으로 저장
Enemy spawnedEnemy = Instantiate(enemy, Vector3.zero, Quaternion.identity) as Enemy;
// 적을 인스턴스화를 통해 생성, 맵 중앙에 회전값 없이 배치
}
}
void NextWave() // 다음 웨이브 실행
{
currentWaveNumber++; // 웨이브 숫자 증가
currentWave = waves[currentWaveNumber - 1];
// 현재 웨이브 레퍼런스 참조
// (웨이브 숫자는 1부터 시작 할 것이므로 -1 하여 배열의 인덱스에 맞게 참조)
enemiesRemainingToSpawn = currentWave.enemyCount;
// 이번 웨이브의 적 수 저장
}
[System.Serializable]
/* 스크립트 직렬화, 직렬화를 통해 객체, 변수 등을 선언 할 때의
접근 제한(private 등)은 유지되지만 인스펙터에서 값을 변경 가능하게 함 */
public class Wave
// 적의 주기, 스폰 주기 등 웨이브 정보를 저장 할 클래스 생성
{
public int enemyCount; // 적의 수
public float timeBetweenSpawns; // 적의 스폰 주기
}
}
위와 같이 작성한다.
위 코드에서와 같이 직렬화하면 인스펙터에서 변수 값을 조정할 수 있다.
웨이브 수, 각 웨이브마다 적 수, 적 스폰 시간을 설정하자.
그리고 Enemy 오브젝트를 프리팹으로 만들고 Spawner 스크립트의 Enemy 에 할당해준다.
이제 Enemy 오브젝트를 씬(계층)에서 지운다.
이제 한번 실행해보자.
적이 중앙에서 스폰되고 총알을 맞추면 죽게된다.
아직 다음 웨이브로 넘어가지지 않는다.
적을 모두 잡으면 다음 웨이브로 넘어가도록 하자.
이때 저번에 적 오브젝트가 죽을 때 호출되는 메소드 Die() 메소드를 수정하여
Spawner 객체를 가져와 참조하여 사용하는 방법도 있겠지만..
이번에는 이벤트를 사용하여 적이 죽을 때 체크할 수 있도록 해볼 것이다.
이를 위해 LivingEntity 스크립트에 이벤트 부분을 추가하자.
| LivingEntity 스크립트
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class LivingEntity : MonoBehaviour, IDamaneable // 인터페이스 상속
{
public float startingHealth; // 시작 체력
protected float health; // 체력
/* protected 를 통해 상속 관계가 없는 클래스에서 사용할 수 없게 한다.
인스펙터에서도 보이지 않음. */
protected bool dead; // 캐릭터가 죽었는지
public event System.Action OnDeath;
/* System.Action 은 델리게이트 메소드이다.
여기서 델리게이트란 C++ 의 함수 포인터와 유사하게
메소드의 위치를 가르키고 불러올 수 있는 타입이다.
void 를 리턴하고 입력을 받지 않는다. */
protected virtual void Start()
// 상속 받는 클래스의 같은 메소드를 재정의 할 수 있도록 virtual 로 선언.
{
health = startingHealth; // 시작 체력 설정
}
public void TakeHit(float damage, RaycastHit hit) // 데미지 받는 메소드 구현/
{
health -= damage; // 체력에서 데미지만큼 감소
if(health <= 0 && !dead) // 체력이 0 이하고 죽은 경우
{
Die(); // 죽음 메소드 호출
}
}
protected void Die() // 죽음 메소드
{
dead = true; // 죽은 판정으로 변경.
if(OnDeath != null) // 이벤트가 있는 경우
{
OnDeath(); // 메소드를 호출
}
GameObject.Destroy(gameObject); // 현재 오브젝트 파괴
}
}
Spawner 스크립트에도 이벤트 부분을 추가해준다.
| Spawner 스크립트
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Spawner : MonoBehaviour
{
public Wave[] waves; // 웨이브들을 저장할 배열 생성
public Enemy enemy; // 스폰할 적 레퍼런스
Wave currentWave; // 현재 웨이브 레퍼런스
int currentWaveNumber; // 현재 웨이브 번호
int enemiesRemainingToSpawn; // 남아있는 스폰 할 적 수
int enemiesRemainingAlive; // 살아있는 적의 수
float nextSpawnTime; // 다음 스폰까지의 시간
void Start()
{
NextWave(); // 시작 시 웨이브 실행
}
void Update()
{
if(enemiesRemainingToSpawn > 0 && Time.time >= nextSpawnTime)
// 적 수가 남아있고 현재 시간이 다음 스폰 시간보다 큰 경우
{
enemiesRemainingToSpawn--; // 적 수를 하나 줄임
nextSpawnTime = Time.time + currentWave.timeBetweenSpawns;
// 다음 스폰 시간을 현재시간 + 스폰 간격 으로 저장
Enemy spawnedEnemy = Instantiate(enemy, Vector3.zero, Quaternion.identity) as Enemy;
// 적을 인스턴스화를 통해 생성, 맵 중앙에 회전값 없이 배치
spawnedEnemy.OnDeath += OnEnemyDeath;
// 적이 죽을 때 위 메소드를 추가
}
}
void OnEnemyDeath()
/* 적이 죽을 때 처리하는 메소드
적이 죽으면 LivingEntity 에서 OnDeath 를 호출하고,
OnDeath 는 이 메소드를 호출해서 적이 죽을 때 알려준다. */
{
enemiesRemainingAlive--; // 적의 수를 1 줄임
if(enemiesRemainingAlive == 0) // 적의 수가 0이 되면
{
NextWave(); // 다음 웨이브 실행
}
}
void NextWave() // 다음 웨이브 실행 메소드
{
currentWaveNumber++; // 웨이브 숫자 증가
print("Wave : " + currentWaveNumber); // 현재 웨이브 번호 출력
if(currentWaveNumber - 1 < waves.Length) { // 배열 인덱스 예외 없도록 처리
currentWave = waves[currentWaveNumber - 1];
/* 현재 웨이브 레퍼런스 참조
(웨이브 숫자는 1부터 시작 할 것이므로 -1 하여 배열의 인덱스에 맞게 참조) */
enemiesRemainingToSpawn = currentWave.enemyCount;
// 이번 웨이브의 적 수 저장
enemiesRemainingAlive = enemiesRemainingToSpawn;
// 살아있는 적의 수를 스폰 할 적의 수로 저장
}
}
[System.Serializable]
/* 스크립트 직렬화, 직렬화를 통해 객체, 변수 등을 선언 할 때의
접근 제한(private 등)은 유지되지만 인스펙터에서 값을 변경 가능하게 함 */
public class Wave
// 적의 주기, 스폰 주기 등 웨이브 정보를 저장 할 클래스 생성
{
public int enemyCount; // 적의 수
public float timeBetweenSpawns; // 적의 스폰 주기
}
}
위와 같이 작성했다면
이제 실행해보자!
콘솔 창에 웨이브 번호가 출력된다.
각 웨이브마다 설정한 만큼 적들이 설정한 시간 간격을 두고 스폰된다.
웨이브에 스폰된 적을 모두 잡으면 다음 웨이브로 넘어간다.
다음 시간에는 적의 공격을 구현해보자.
'Unity > 게임개발' 카테고리의 다른 글
[Unity] 게임 개발 일지 | 탑다운 슈팅 7 (0) | 2023.11.29 |
---|---|
[Unity] 게임 개발 일지 | 탑다운 슈팅 6 (0) | 2023.11.24 |
[Unity] 게임 개발 일지 | 탑다운 슈팅 4 (0) | 2023.11.22 |
[Unity] 게임 개발 일지 | 탑다운 슈팅 3 (1) | 2023.11.22 |
[Unity] 게임 개발 일지 | 탑다운 슈팅 2 (0) | 2023.11.17 |