언리얼 엔진/25.01.23 2차프로젝트 기술

스폰될 때 에너미에게 정보를 전달하기

odkokdh 2025. 2. 12. 21:50

저번에 웨이브로 에너미들을 소환되게 만들었는데 구현을 수정해야하는 부분을 발견했다.

for문을 사용해서 맨 마지막 적에게만 마지막이라는 정보를 전달하고 해당 적이 죽으면 다음 웨이브 적이 소환되도록 생각하였는데 그러면

1(x),2(x),3(x),4(o) 적이 이렇게 남았다고 쳤을 때 마지막 적인 4번 적을 먼저 죽인다면 마지막 적이 죽었다는 정보를 받아 적이 남아있는데 다음 웨이브 적을 소환되게 될 것이다.

 

그래서 수정 방법을 생각했다. 다음은 스폰하여 에너미에게 어느 웨이브에 속해있는지 정보를 받고 죽을 경우 해당 웨이브의 카운트를 감소하는 과정을 간단하게 글로 작성해본 내용이다.

1. 배열에 담은 에너미들의 스폰을 발생하게 한다.

2. 스폰 함수를 부를 때 숫자를 넣어 숫자 값에 따라 에너미가 어느 배열에 속하는지 알려준다. 스폰하는 수 만큼 카운트를 늘려준다.

3. 에너미가 죽으면 죽었다는 정보를 스포너에 전달하고 해당 웨이브의 숫자를 관리하는 변수의 값을 하나 빼준다.

4. 죽었다는 정보를 전달했을 때 카운트가 일정 수량 이하이면 다음 웨이브가 스폰된다.

5. 위에 작업을 끝날 때까지 반복한다.

 

위에 순서를 참고하여 작업을 시작하였다.


1. 배열을 작성하여 몬스터 웨이브의 정보들을 저장하기

EnemySpanwer.h

//배열에 있는 에너미를 반복으로 생성하게 한다. WaveNum은 에너미가 어디 웨이브인지 알려줌.
void SpawnEnemyWave(TArray<TSubclassOf<AEnemyBase>> Wave, int32 WaveNum);

 

 

먼저 에너미를 스폰하는 함수를 수정하였다. 뒤에 int32 WaveNum을 붙여 int32의 값이 1,2,3에 따라 어디 웨이브에 속하는지 판단하기 위해 추가하였다.

EnemySpawner.h

//각각의 웨이브 에너미들의 총 마리 수를 저장하기 위한 변수들
int32 WaveAEnemys = 0;
int32 WaveBEnemys = 0;
int32 WaveCEnemys = 0;

 

해당 코드들은 각 웨이브의 에너미 수를 저장할 수 있도록 선언한 코드다. 배열의 총 크기는 언리얼 엔진에서 정하기 때문에 각 웨이브의 에너미 수를 확인할 수 있는 변수가 필요하다고 생각하여 만들었다.


2. 스폰될 때 웨이브의 정보를 에너미에게 전달하기

EnemySpanwer.cpp

void AEnemySpawner::SpawnEnemyWave(TArray<TSubclassOf<AEnemyBase>> Wave, int32 WaveNum)
{
	//한 웨이브의 총 몬스터 수를 계산해서 그 만큼 반복문이 돌아감
	for (int i = 0; i < Wave.Num(); ++i)
	{
		//반복으로 해당 웨이브안에 적들이 소환 >> 딜레이가 없기에 아마 한꺼번에 소환이 될 같음
		AEnemyBase* spawnEnemy = GetWorld()->SpawnActor<AEnemyBase>(Wave[i], FTransform(FVector(0)));

		switch (WaveNum)
		{
		case 1:	//WaveA에 속함
			//해당 웨이브 카운트를 1 증가
			WaveAEnemys += 1;
			//해당 적에게 자신이 어느 웨이브에 속하고 스포너 정보를 알려줌 
			spawnEnemy->SetWaveA();
			break;
		case 2:	//WaveB에 속함
			//해당 웨이브 카운트를 1 증가
			WaveBEnemys += 1;
			//해당 적에게 자신이 어느 웨이브에 속하고 스포너 정보를 알려줌 
			spawnEnemy->SetWaveB();
			break;
		case 3:	//WaveC에 속함
			//해당 웨이브 카운트를 1 증가
			WaveCEnemys += 1;
			//해당 적에게 자신이 어느 웨이브에 속하고 스포너 정보를 알려줌 
			spawnEnemy->SetWaveC();
			break;
		case 4:	//wave에 속하지 않을 경우 아무런 처리가 없음
			break;
		default:
			break;
		}
	}
}

 

함수 구현은 이렇게 바뀌었다. 배열의 크기만큼 반복문을 돌리는 것은 동일하다.

이후 int32 WaveNum의 값에 따라 switch를 사용하여 각 값에 따라 카운트를 늘려주게 만들었다. 그리고 스폰하는 에너미에게 자신이 어느 웨이브에 속하는지 각 SetWave() 함수를 불러서 세팅해준다.

EnemyBase.h

UPROPERTY(VisibleAnywhere,BlueprintReadOnly)
int32 SetWave = 4;

//WaveA 지정함수
void SetWaveA();

//WaveB 지정함수
void SetWaveB();

//WaveC 지정함수
void SetWaveC();

 

맨 위에 int32 선언은 int32의 값을 통해 해당 에너미가 어느 웨이브에 속하는지 알기위해 만든 변수다. 4는 웨이브가 없음을 나타내고 가장 기본적인 형태이다.

생성될때 각 함수가 불러오면 SetWave의 값을 변경하여 어느 웨이브에 속하는지 표시한다.

EnemyBase.cpp

void AEnemyBase::SetWaveA()
{
	SetWave = 1;
}

void AEnemyBase::SetWaveB()
{
	SetWave = 2;
}

void AEnemyBase::SetWaveC()
{
	SetWave = 3;
}

각 함수의 구현부분이다. 사실 간단하다. SetWave의 값만 바꿔서 어느 웨이브인지 확인하게 만들어주는거다.


3. 죽었을 때 스포너에게 죽었다는 정보를 전달하기

EnemyBase.cpp

void AEnemyBase::Die()
{
	//케이스를 나눠서 각각 사망하는 것을 다르게 처리해줌
	switch (SetWave)
	{
	case 1: //웨이브 A에 속할 경우
		//스포너에게 소속 웨이브와 사망 소식을 전달
		EnemySpawner->WaveEnemyCountDown(1);
		//사망처리를 해줌
		SetLifeSpan(3.0f);
		break;

	case 2:
		//스포너에게 소속 웨이브와 사망 소식을 전달
		EnemySpawner->WaveEnemyCountDown(2);
		//사망처리를 해줌
		SetLifeSpan(3.0f);
		break;

	case 3:
		//스포너에게 소속 웨이브와 사망 소식을 전달
		EnemySpawner->WaveEnemyCountDown(3);
		//사망처리를 해줌
		SetLifeSpan(3.0f);
		break;

	case 4: //웨이브에 속하지 않을 경우
		//그냥 사망 처리를 해줌
		SetLifeSpan(3.0f);
		break;
	default:
		break;
	}


}

 

이후 기존에 작성했던 Die() 함수를 수정하였다. 죽었을 때 스포너에게 죽었다는 정보와 함께 어느 웨이브에 속하는 지 알려준다. 이렇게 작성을 하니 스포너의 정보를 미리 받아야 작동하는 것을 확인하였다. 그래서 BeginPlay()에서 스포너의 정보를 저장되도록 코드를 작성하였다. WaveEnemyCountDown(SetWave) 함수는 뒤에서 설명하겠다.

EnemyBase.cpp
void AEnemyBase::BeginPlay()에 포함된 코드

//EnemySpawner에서 함수로 EnemySpawer의 정보 전달이 안되어서 생성될 때 레벨에 있는 EnemySpawner의 정보를 찾아 저장해둔다.
EnemySpawner = Cast<AEnemySpawner>(UGameplayStatics::GetActorOfClass(GetWorld(), AEnemySpawner::StaticClass()));

 

 

EnemySpawner.cpp

void AEnemySpawner::WaveEnemyCountDown(int32 WaveNum)
{
	switch (WaveNum)
	{
	case 1:	//WaveA에 속함

		//카운트를 하나 줄임
		WaveAEnemys -= 1;

		//일정 수량 이하일 경우
		if (WaveAEnemys < 3)
		{
			//다음 웨이브를 소환
			WaveAClera = true;
		}
		break;
	case 2:	//WaveB에 속함

		//카운트를 하나 줄임
		WaveBEnemys -= 1;

		//일정 수량 이하일 경우
		if (WaveBEnemys < 3)
		{
			//다음 웨이브를 소환
			WaveBClera = true;
		}

		break;
	case 3:	//WaveC에 속함

		//카운트를 하나 줄임
		WaveCEnemys -= 1;

		//일정 수량 이하일 경우
		if (WaveCEnemys == 0)
		{
			//다음 웨이브를 소환
			WaveCClera = true;
		}

		break;
	case 4:	//wave에 속하지 않을 경우 아무런 후처리가 없음
		break;
	default:
		break;
	}
}

WaveEnemyCountDown(SetWave)는 EnemySpawner에 함수를 정의해두었다.

해당 함수가 불렸을때 각 스폰 카운터를 1씩 차감시켜준다. 그리고 차감됐을 때 만약 카운트가 일정치 이하이면 다음 웨이브 소환 조건을 만족되게 바꿔준다.

 


4. 테스트

스포너에 각 Wave에 원하는 에너미들을 지정했다.

스포너에서 미리 각 웨이브에 어떤 에너미가 나올지 설정해주었다.

 

레벨에 스포너를 설치해둔다.

그후 미리 설정해둔 에너미를 레벨에 미리 설치해준다.

그 다음 실제로 작성한 대로 플레이가 되는지 확인을 해보았다.

스폰시켰을 때 에너미의 정보

그리고 실제로 스포너에서 에너미를 스폰시켜보았다. WaveA라 정상적으로 Set Wave에 숫자 1이 들어간 것을 볼 수 있다.

 

사망 정보 전달은 현재 총알과 에너미의 충돌처리가 안되어있기에 현시점에서 확인할 수가 없다. 이후 충돌처리를 만든 다음 사망 정보가 전달이 되는지 테스트할 예정이다.