루트슈터 프로젝트

블루아카이브 루트슈터 팬게임 프로젝트(18) - 적 스폰 시스템 & 웨이브 게임모드(3)

1인겜덕부 2024. 1. 20. 01:02

안녕하세요. 오늘은 웨이브, 남은 적 정보를 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);
}

 

그리고 웨이브가 시작할때에도 위젯을 새로고침 해줍니다. 

 

 

 

웨이브 위젯의 블루프린트 입니다. 웨이브와 남은 적들의 수를 표시해주는 간단한 위젯입니다. 

 

 

 

 

 

결과물입니다. 적을 처치하면 남은 적이 줄어드는 것을 확인할 수 있습니다. 웨이브 모드 만들기는 이것으로 일단락 된 것 같고.. 이제 게임 메인 메뉴 레벨을 만들어서 게임 시작 버튼을 누를 수 있게 하고 플레이어 상태를 저장할 수 있도록 하고싶습니다. 이거는 좀 더 방법을 찾아보아야 할 것 같습니다.