With our HUD
in place, we can now implement the functionality to finish the Bounty Dash game loop. We need to be able to end the game when the player reaches the same kill point that destroys the objects that are moving down the game field. Upon reaching this kill point, we should pause the game and present the player with game over text. Upon game over, the player should also be able to press R to reset the game. To achieve this, we are going to have to make edits to the ABountyDashGameMode
class to support game over and pause functionality, to the ABountyDashCharacter
class to support restarting, and to the ABountyDashHUD
class so that we can draw the game over text.
Instead of having the game suddenly pause when the player reaches the kill point, it would be more user-friendly to have the character fall for a short while before we pause the game and report that the game is over. To do this, we are going to have to set up a timer within the ABountyDashGameMode
class and a few Boolean flags we can use to represent the different game over states. Navigate to BountyDashGameMode.h
and add the following public functions to the class definition now:
UFUNCTION() bool GetGameOver(); UFUNCTION() void GameOver(); UFUNCTION() void SetGamePaused(bool gamePaused);
The first will be used to query whether the game is in fact over, and the second will be called when the character detects that it has reached the kill point. This will start the game over timer. Once the timer has reached a specified time, we will set a game over flag to true. The last one will be used to pause the game once the game over timer has run its course. We now need to add a few protected members to the class definition:
UPROPERTY() bool bGameOver; UPROPERTY() bool startGameOverCount; UPROPERTY() float timeTillGameOver; UPROPERTY() float gameOverTimer;
The first is a Boolean flag that represents if the game is in fact over. The next Boolean flag is used to gauge when to start the game over timer. The next two floating point values are used for the timer itself. The first will be used to hold how long it will take between the character reaching the KillPoint
and the game over screen being shown. The second is the accumulated time since startGameOverCount
was set to true.
Navigate to BountyDashGameMode.cpp
now. We need to add some default values for the new protected members we just created; let's do that now by adding the following code to the constructor:
RunTime = 0.0f; bGameOver = false; startGameOverCount = false; timeTillGameOver = 2.0f; gameOverTimer = 0.0f;
The game over timer will run for two seconds before we pause the game. We have also added some safe defaults for the Boolean flags and the RunTime
counter we made earlier.
Next, we need to adjust the tick function so that it includes the new game over timer. Modify ABountyDashGameMode::Tick()
so that it appears as follows:
if (!startGameOverCount) { RunTime += DeltaSeconds; } else { gameOverTimer += DeltaSeconds; if (gameOverTimer >= timeTillGameOver) bGameOver = true; } }
Here, we are checking whether we have begun the game over timer. If not, we need to increment the runtime as the game is still in progress. Otherwise, we need to increment the game over timer until it is greater than or equal to timeTillGameOver
. If so, we will change the bGameOver
flag to true. Next, we need to add the following simple functions to the .cpp
:
bool ABountyDashGameMode::GetGameOver() { return bGameOver; } void ABountyDashGameMode::GameOver() { startGameOverCount = true; }
The first simply returns the bGameOver
flag and the second is used to start the game over timer.
Now we can write our pause function. Pausing a game from C++ is very easy; all we need is a handle to a player controller. In our case, this will be the first player controller. We can then call SetPause
on this controller to pause the game. Define the ABountyDashGameMode::SetGamePaused
method as follows:
void ABountyDashGameMode::SetGamePaused(bool gamePaused) { APlayerController* myPlayer = GetWorld()->GetFirstPlayerController(); if (myPlayer != nullptr) { myPlayer->SetPause(gamePaused); } }
Here, we are simply getting the first player controller from the world context, ensuring the controller is valid, and then calling SetPause
on the controller.
We now need to allow the player to restart the game somehow while the game is paused. We can do this by binding another input action to the ABountyDashCharacter
. We are then going to inform that action to be allowed to execute even when the game is paused. The first thing we need to do is create a new action mapping in the editor. Do this now. Navigate to Edit->ProjectSettings->Engine->
Input and add the following action mapping:
We need to bind this mapping to a new function in ABountyDashCharacter
. Navigate to BountyDashCharacter.h
and add the following protected method to the class definition:
UFUNCTION() void Reset();
This function will be bound to the input event and defined so that the game level is reset. Navigate to ABountyDashCharacter::SetupPlayerInputComponent()
in BountyDashCharacter.cpp
. We need to add one more binding to the definition of this function. Add the following line of code:
InputComponent->BindAction("Reset", IE_Pressed, this, &ABountyDashCharacter::Reset).bExecuteWhenPaused = true;
What we are doing here is binding the Reset()
method we just created to the Reset input action mapping pressed state. The BindAction
function returns a reference to a newly created FInputActionBinding
. We are then setting the bExecutWhenPaused
member of this new binding to true. Whenever you want an input action to be carried out, even when a game state is paused, you must set this flag appropriately.
Now we can define the Reset()
method. Add the following code to the .cpp
now:
void ABountyDashCharacter::Reset() { UGameplayStatics::OpenLevel(GetWorld(), TEXT("BountyDashMap")); }
This code utilizes the UGameplayStatistics
namespace function OpenLevel
. This function will open the level specified, given a game world context. Reloading this level will perform the same action as resetting the level.
We need to do one last thing before we are done editing ABountyDashCharacter
. We need to add some code to the Tick
function so that when the player reaches the killPoint
of the floor object, the game mode will be informed to start the game over counter.
First, we need to retrieve and save the kill point somewhere. Add the following protected member to the ABountyDashCharacter
class definition:
// Kill Point float Killpoint; Now modify ABountyDashCharacter::BeginPlay in BountyDashCharacter.cpp to include the following code: for (TActorIterator<AFloor> TargetIter(GetWorld()); TargetIter; ++TargetIter) { Killpoint = TargetIter->GetKillPoint(); }
This code will retrieve the floor actor in the level and save the killPoint
into the value we just declared. As we just reference AFloor
in this .cpp
, ensure to add the appropriate include to the include list. Finally, we can add the following code to ABountyDashCharacter::Tick()
:
if (GetActorLocation().X < Killpoint) { GetCustomGameMode<ABountyDashGameMode>(GetWorld())-> GameOver(); }
Simply, if the character reaches the killPoint
, we need to inform the game to start the game over counter.
We are nearly done! All we need to do now is draw the game over text within the HUD
when ABountyDashGameMode::bGameOver
has been set to true. Navigate to ABountyDashHUD::DrawHUD()
in BountyDashHUD.cpp
and add the following code:
if (DashGameMode->GetGameOver()) { FVector2D GameOverSize; GetTextSize(TEXT("GAME OVER!!! Press R to Restart!"), GameOverSize.X, GameOverSize.Y, HUDFont); DrawText(TEXT("GAME OVER!!! Press R to Restart!"), FColor::Yellow, (ScreenDimensions.X - GameOverSize.X) / 2.0f, (ScreenDimensions.Y - GameOverSize.Y) / 2.0f, HUDFont); DashGameMode->SetGamePaused(true); }
This section of code is checking whether the game is over. If so, we need to draw GAME OVER!!! Press R to Restart!
in the middle of the screen. We can do this by using the same draw text function we created earlier; however, this time, instead of parsing hard-coded values (such as 50), we are simply calculating the position of the text. We have used the GetTextSize()
method to retrieve how much space the previous sentence will take up in screen space when using the font we made earlier. We are then using these text size dimensions to place the game over text in the middle of the screen. We do this by subtracting the appropriate text dimension from the corresponding screen dimension, then dividing the resulting value by 2.0f
. This will place the text in the middle of the screen while compensating for screen text size. Then, finally, we have set the game to pause!
Great! We are now done with the Bounty Dash game loop! Compile, build, and run the code. Let the player be pushed off the back of the floor object, wait two seconds and you should be presented with this:
Now when you press R, the game will restart and you can try beating your previous score!
3.135.184.239