블루아카이브 루트슈터 팬게임 프로젝트(18) - 적 스폰 시스템 & 웨이브 게임모드(3)
안녕하세요. 오늘은 웨이브, 남은 적 정보를 UI로 보여주고 웨이브 간 시간차를 설정해서 모든 적을 처치하면 5초 후 다음 웨이브가 시작되도록 하겠습니다.
WaveWidget.h
UCLASS()
class PROJECTBLUELANDS_API UWaveWidget : public UUserWidget
{
GENERATED_BODY()
public:
UPROPERTY(BlueprintReadWrite, meta=(BindWidget))
TObjectPtr<class UTextBlock> WaveRound;
UPROPERTY(BlueprintReadWrite, meta=(BindWidget))
TObjectPtr<class UTextBlock> EnemyLeft;
void RefreshWidget(int Wave, int enemyLeft);
};
웨이브 정보들을 보여주는 위젯 클래스를 하나 생성해줍니다. WaveRound는 몇웨이브인지 알려주는 텍스트 블록 이고 EnemyLeft는 남은 적들을 보여주는 텍스트 블록입니다.
WaveWidget.h
void UWaveWidget::RefreshWidget(int Wave, int enemyLeft)
{
WaveRound->SetText(FText::FromString(FString::FromInt(Wave)));
EnemyLeft->SetText(FText::FromString(FString::FromInt(enemyLeft)));
}
위젯의 정보를 초기화하는 함수입니다. 이 함수는 1. 새로운 웨이브가 시작될 때, 2. 적을 처치해서 남은 적이 줄어들었을때 호출됩니다.
WaveGameMode.h
private:
FTimerHandle WaveStartTimerHandle;
게임 모드 클래스입니다. 웨이브가 5초의 딜레이를 가지고 시작할 수 있도록 타임 핸들러 변수를 하나 선언해줍니다.
WaveGameMode.cpp
void ABluelandsGameWaveMode::BeginPlay()
{
Super::BeginPlay();
WaveWidget = Cast<UWaveWidget>(CreateWidget(GetWorld(), WaveWidgetClass));
if(WaveWidget)
{
WaveWidget->AddToViewport();
}
waveRound = 1;
SpawnEnemy();
}
void ABluelandsGameWaveMode::ActorDied(AActor *DeadActor)
{
Super::ActorDied(DeadActor);
if(Cast<AEnemyCharacter>(DeadActor))
{
enemyLeft--;
UE_LOG(LogTemp, Error, TEXT("%d enemy left!"), enemyLeft);
if(WaveWidget) WaveWidget->RefreshWidget(waveRound, enemyLeft);
}
if(enemyLeft == 0)
{
UE_LOG(LogTemp, Error, TEXT("Next Wave!!"));
waveRound++;
if(waveRound < 8) GetWorldTimerManager().SetTimer(WaveStartTimerHandle,this, &ABluelandsGameWaveMode::SpawnEnemy, 5.0, false, 5.0);
}
}
게임 시작시 위젯을 불러와 뷰포트에 추가하고, 새로운 웨이브가 시작될 때(남은 적이 0이 됐을때) 5초의 텀을 가지고 웨이브가 시작되도록 타이머를 설정해줍니다. 그리고 적이 죽을때에는 웨이브 정보 위젯을 새로고침 해줍니다.
void ABluelandsGameWaveMode::SpawnEnemy() //각 라운드 시작시 발동됨
{
int normal, middle, boss;
normal = 5, middle = 2, boss = 2;
enemyLeft = 0;
TArray<AActor*> spawners;
UGameplayStatics::GetAllActorsOfClass(GetWorld(), spawnerClass, spawners);
for(int i=0; i<(normal + middle + boss); i++)
{
AEnemySpawner* spawner = Cast<AEnemySpawner>(spawners[i]);
if(spawner && player)
{
FEnemyTableRow* enemyRow = nullptr;
if(i < normal)
{
enemyRow = arNormalEnemyDataTable->FindRow<FEnemyTableRow>(FName(*(FString::FormatAsNumber(player->GetCharacterLevel()))), FString(""));
} else if(i < normal + middle) enemyRow = arMiddleEnemyDataTable->FindRow<FEnemyTableRow>(FName(*(FString::FormatAsNumber(player->GetCharacterLevel()))), FString(""));
else enemyRow = arBossEnemyDataTable->FindRow<FEnemyTableRow>(FName(*(FString::FormatAsNumber(player->GetCharacterLevel()))), FString(""));
if(enemyRow == nullptr) continue;
spawner->SpawnEnemy((*enemyRow).enemyLevel, (*enemyRow).itemDropRate, (*enemyRow).legendDropRate,
(*enemyRow).rareDropRate, (*enemyRow).uncommonDropRate, (*enemyRow).enemyHP, (*enemyRow).enemyDamage, (*enemyRow).enemyEXP);
enemyLeft++;
}
}
if(WaveWidget) WaveWidget->RefreshWidget(waveRound, enemyLeft);
}
그리고 웨이브가 시작할때에도 위젯을 새로고침 해줍니다.
웨이브 위젯의 블루프린트 입니다. 웨이브와 남은 적들의 수를 표시해주는 간단한 위젯입니다.
결과물입니다. 적을 처치하면 남은 적이 줄어드는 것을 확인할 수 있습니다. 웨이브 모드 만들기는 이것으로 일단락 된 것 같고.. 이제 게임 메인 메뉴 레벨을 만들어서 게임 시작 버튼을 누를 수 있게 하고 플레이어 상태를 저장할 수 있도록 하고싶습니다. 이거는 좀 더 방법을 찾아보아야 할 것 같습니다.