Drawing the HUD and preparing the lobby

Ok, the last thing we need to do is provide each player with an accurate readout of their own score/deaths! We will then also set up a pseudo multiplayer lobby by showing who is connected to the game, and that we are waiting on the server to initiate travel to the main multiplayer map. This, however, is going to require that we have information from the GameMode replicated to all clients, namely when the game has started. We already have a static bool present in the game mode that we can use, but we now need to replicate the value of this bool to all clients. The only way we can do that is with a GameState.

Creating the GameState

In a very similar way to PlayerStates of the PlayerController, we have GameStates for game modes. They are replicated objects that are replicated on all connected clients. We can use this game state to store variables pertaining to the game world within this state. Game states, by default, contain information on all of the currently connected players via player states, how long the game has been running, and other game state variables.

We are going to be creating a very simple game state that contains a single Boolean. Open the C++ class wizard and create an object that inherits from GameState. Call this object NSGameState. Once the code for this object has been compiled, navigate to NSGameState.h. Modify the class declaration to match the following:

UCLASS()
class NS_API ANSGameState : public AGameState
{
    GENERATED_BODY()

public:
    ANSGameState();

UPROPERTY(Replicated)
    bool bInMenu;
};

Here we are simply declaring a default class constructor and a Boolean flag that will inform the player's HUD if the game is still in the menu, and yet to be run by the server. Navigate to NSGameState.cpp now and add the following code:

ANSGameState::ANSGameState()
{
    bInMenu = false;
}

void ANSGameState::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
{
    Super::GetLifetimeReplicatedProps(OutLifetimeProps);

    DOREPLIFETIME(ANSGameState, bInMenu);
}

Ensure that you also add #include "Net/UnrealNetwork.h" to your .cpp include list. The previous code will simply set that, by default, the game is not in the menu and then ensures that we replicate that variable over its lifetime whenever it is changed. Now navigate to your NSGameMode.cpp and address the constructor. We need to inform the game mode of its new game state class. We can do this by adding the following line to the ANSGameMode constructor:

GameStateClass = ANSGameState::StaticClass();

Be sure to also include #include "NSGameState.h" in the .cpp include list.

Drawing in the HUD

Ok, now we can do some basic information drawing in the HUD! We are going to be using basic text and colors to create our HUD readout. Alright, let's start by modifying the already generated ANSHUD class. Navigate to NSHUD.cpp now. We are not going to be removing any functionality from the DrawHUD() method, we are simply going to be appending to it. Before we begin, be sure to modify the include list of this .cpp to include the following:

#include "NSCharacter.h"
#include "NSGameState.h"
#include "NSGameMode.h"
#include "NSPlayerState.h"

The first thing we need to do is query the ANSGameState we just created if the game is in fact still in the menu. If so, we want to be able to draw the name of each connected client underneath their respective teams:

ANSGameState* thisGameState = Cast<ANSGameState>(GetWorld()->
GetGameState());

    if (thisGameState != nullptr && thisGameState->bInMenu)
    {
        int BlueScreenPos = 50;
        int RedScreenPos = Center.Y + 50;
        int nameSpacing = 25;
        int NumBlueteam = 1;
        int NumReadTeam = 1;

The first thing we do is get our game state from the game world. We have to use the game world here as the game mode itself is not replicated. If we successfully get the game state, we then check whether the Boolean value is contained within. If this resolves to true, we now know we must draw the in-menu HUD. We then declare all of the logic variables we are going to need. We have specified where the blue team list will start and where the red team list will start. Each name will be 25 units apart and we have then declared two counters that will be used to increment where we draw team names.

Following this, add:

FString thisString = "BLUE TEAM:";
DrawText(thisString, FColor::Cyan, 50, BlueScreenPos);

thisString = "RED TEAM:";
DrawText(thisString, FColor::Red, 50, RedScreenPos);
for (auto player : thisGameState->PlayerArray)
{
    ANSPlayerState* thisPS = Cast<ANSPlayerState>(player);
    
    if (thisPS)
    {
        if (thisPS->Team == ETeam::BLUE_TEAM)
        {
            thisString = FString::Printf(TEXT("%s"), &(thisPS->
            PlayerName[0]));

             DrawText(thisString, FColor::Cyan, 50,
             BlueScreenPos + nameSpacing * NumBlueteam);

            NumBlueteam++;
        }
        else
        {
            thisString = FString::Printf(TEXT("%s"), &(thisPS->
PlayerName[0]));
            
DrawText(thisString, FColor::Red, 50, RedScreenPos + nameSpacing * NumReadTeam);

             NumReadTeam++;
        }
     }
}

The previous code starts by drawing either BLUE TEAM: or RED TEAM: at the previously mentioned screen positions. We then iterate over all of the player states that exist in the GameState base class. This array will contain the player states of all connected clients. This means we can use this container as a go-to when wanting information from every connected client. Then we check which team the player state is associated with. We then draw the name of that player state by getting the player name member and drawing it, in the right color at the team screen position, plus the 25-unit offset multiplied by the number of names already drawn. The final thing we need to do for this is inform the clients that they are waiting on the server to start the game, and inform the server to press R if they wish to travel:

if (GetWorld()->GetAuthGameMode())
{
    thisString = "Press R to start game";
    DrawText(thisString, FColor::Yellow, Center.X, Center.Y);

}
else
{
    thisString = "Waiting on Server!!";
    DrawText(thisString, FColor::Yellow, Center.X, Center.Y);
}
} <-- closes if (thisGameState != nullptr && thisGameState->bInMenu)

Instead of checking authority, we can check whether a given HUD object is not the server by seeing whether we can get access to the Game Mode! As only the server will have a valid game mode, this will return null for all other net roles. Here we check whether the return is valid. If so, we draw the server message; if not, we draw the client message. This closes the functionality for when the clients are still in-menu.

Now we must write the accompanying else for the if statement:

else
{
ANSCharacter* ThisChar = Cast<ANSCharacter>(UGameplayStatics::GetPlayerPawn(GetWorld(), 0));

    if (ThisChar != nullptr)
    {
        if (ThisChar->GetNSPlayerState())
        {
            FString HUDString = FString::Printf(TEXT("Health: %f, Score: %d, Deaths: %d"), ThisChar->GetNSPlayerState()->
            Health, ThisChar->GetNSPlayerState()->Score, ThisChar->
            GetNSPlayerState()->Deaths);

            DrawText(HUDString, FColor::Yellow, 50, 50);
        }
    }
}

Here we simply get the player state from the player and draw the health, score, and deaths to the screen in yellow in the top left-hand corner.

Now navigate back to NSGameMode.cpp, we have to add this line:

Cast<ANSGameState>(GameState)->bInMenu = bInGameMenu;

in NSGameMode::BeginPlay() within the true scope for the role authority check, and after we have traveled the server in ANSGameMode::Tick().

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.145.191.214