게임 끝내기

정재훈·2022년 5월 6일
0

1.Character가 죽었는 지 확인

ShooterCharacter.cpp


bool AShooterCharacter::isDead() const
{
	return Health <= 0;
}


float AShooterCharacter::TakeDamage(float DamageAmount, FDamageEvent const& DamageEvent, AController* EventInstigator, AActor* DamageCauser)
{
	float DamagetoApply = Super::TakeDamage(DamageAmount, DamageEvent, EventInstigator, DamageCauser);
	DamagetoApply = FMath::Min(Health, DamagetoApply);  //남은 Health와 Damage중 작은값을 구해
	Health -= DamagetoApply;							//Health에서 빼준다 ->Health가 음수값이 되는것을 방지한다
	UE_LOG(LogTemp, Warning, TEXT("health left %f"), Health);

	if (isDead()) 
	{
		ASimpleShooterGameModeBase* GameMode = GetWorld()->GetAuthGameMode<ASimpleShooterGameModeBase>();  //end the game
		if (GameMode != nullptr)
		{
			GameMode->PawnKilled(this);
		}
		DetachFromControllerPendingDestroy();	//delete capsule when dead
		GetCapsuleComponent()->SetCollisionEnabled(ECollisionEnabled::NoCollision);
	}

	return DamagetoApply;
}

ShooterCharacter 클래스에서 전에 override method로 구현한 takeDamage가 호출될때마다 HP가 0보다 많은지 확인하는 IsDead 메소드를 통해 캐릭터가 죽었는지 살았는지 확인한다.

만약 캐릭터가 죽었다면 현재 GameMode 클래스에 Pawnkilled 메소드에 this를 인자로넣어 이 Character가 죽었다는것을 알린다.
Pawnkilled 메소드는 밑에서 살펴보겠다.

그리고 character가 죽으면 DetachFromControllerPendingDestroy를 통해 캡슐을 삭제하고 SetCollisionEnabled 를 호출해 collision도 없에준다.

캡슐을 삭제해주지 않으면 시체가 player를 따라다니는 공포스러운 경험을 할수있다.

2. Character가 죽었을때 게임을 끝낼지 Condition 확인

PawnKilled 메소드는 GameModeBase에 구현하지 않고 비어있는 virtual 메소드로 생성한다음 subclass 인GameMode를 만들고 그곳에서 PawnKilled 메소드를 override해 구현한다.

그 이유는 GameModeBase를 상속받는 여러가지 GameMode가 있을수 있기때문이다 예를들어 우리는 지금 모든 적이 죽으면 게임이 끝나는 GameMode를 구현 하지만 특정 몇명만 죽이면 게임이 끝나는 GameMode도 있을수 있기 때문이다.

여기서는 사용하지 않았지만 멀티플레이로 만든다면 GameMode에 게임 상태를 모니터링하는 GameState를 사용하기도 한다
UE4 Doc about GameMode and GameState

SimpleShooterGameModeBase.ccp

void ASimpleShooterGameModeBase::PawnKilled(APawn* PawnKilled)
{

}

KillEmAllGameMode.ccp

void AKillEmAllGameMode::PawnKilled(APawn* PawnKilled)
{
	Super::PawnKilled(PawnKilled);
	
	UE_LOG(LogTemp, Warning, TEXT("A pawn was killed"));

	APlayerController* PlayerController = Cast<APlayerController>(PawnKilled->GetController());
	if (PlayerController != nullptr)	//player가 죽었는지 확인
	{
		EndGame(false);	//player lose and end game
	}

	for (AShooterAIController* Controller : TActorRange<AShooterAIController>(GetWorld()))	//Ai controller를 순회하며 살아있는 AI가 있는지 확인
	{
		if (!Controller->IsDead())
		{
			return;	//살아있는 AI가 있으면 메소드 종료
		}
	}
	EndGame(true);	//살아있는 AI가 없으면 EndGame에 false로 호출
}

PawnKilled가 호출되면 같이 인자로 받은 Pawn이 player인지 확인한다
Player라면 EndGame(false) 호출
아니라면 world에 있는 AI Controller를 순회하며 살아있는 AI가 있는지 확인한다.
살아있는 AI가 있으면 메소드 종료
살아있는 AI가 없으면 EndGame(true) 호출

3. End Game

void AKillEmAllGameMode::EndGame(bool bIsPlayerWinner)
{
	for (AController* Controller : TActorRange<AController>(GetWorld()))	//iterate over all actors
	{
		bool bIsWinner=Controller->IsPlayerController()==bIsPlayerWinner; //check if it's player and check if who is winner ->if both of them are true return true
		Controller->GameHasEnded(Controller->GetPawn(), bIsWinner);	
	}
}

bool bIsWinner=Controller->IsPlayerController()==bIsPlayerWinner
모든 actor을 돌며 Controller가 player의 컨트롤러일때 그리고 모든 AI를 죽여 true를 반환받았을때 두가지 조건이 모두 True일때만 bIsWinner는 True값을 가진다.

게임 결과 UI는 멀티 플레이를 생각하면 GameMode가 아닌 Character Controller에서 띄워줘야하기 때문에 ShooterPlayerController에 구현된 GameHasEnded override 메소드를 호출해준다.

ShooterPlayerController.cpp

void AShooterPlayerController::GameHasEnded(class AActor* EndGameFocus, bool bIsWinner)
{
	Super::GameHasEnded(EndGameFocus,bIsWinner);

	HUD->RemoveFromViewport();
	if (bIsWinner)
	{
		UUserWidget* WinScreen = CreateWidget(this, WinScreenClass);
		if (WinScreen != nullptr)
		{
			WinScreen->AddToViewport();
		}
	}
	else
	{
		UUserWidget* LoseScreen = CreateWidget(this, LoseScreenClass);
		if (LoseScreen != nullptr)
		{
			LoseScreen->AddToViewport();
		}
	}
	GetWorldTimerManager().SetTimer(RestartTimer, this, &APlayerController::RestartLevel, RestartDelay);
}

게임 결과에 따라 UI를 띄워주고 SetTimer메소드로 몇초후에 Level을 재시작한다

profile
게임 개발 공부중!

0개의 댓글