Download

Getting A message to appear on all players screens

I know the subject is a little vague, but the problem I have is this: in developing the code for my game, I want to utilize a message function I created in my HUD class that prints a message to the screen. I have the function working and callable from other classes, however, it only prints to one player vs. both players when I test it out. It is a simple message that says whatever team the player is currently on.

HUD Code:
MyGameHUD.h
public:
AGloom2HUD();

/** Primary draw call for the HUD */
virtual void DrawHUD() override;

FString GetUIMessage();

void SetMessage(FString Message);

FString UIMessage;

private:
/** Crosshair asset pointer /
class UTexture2D
CrosshairTex;

class UFont* DisplayFont;

//void Tick(float DeltaTime) override;

FTimerHandle FTimerMessageRefresh;

FString DisplayText;

void DisplayMessage(FString DisplayText, UFont* DisplayFont, FVector2D DisplayMessagePosition);

};

void AGloom2HUD::DrawHUD()
{
Super::DrawHUD();

// find center of the Canvas
const FVector2D Center(Canvas->ClipX * 0.5f, Canvas->ClipY * 0.5f);

// offset by half the texture's dimensions so that the center of the texture aligns with the center of the Canvas
const FVector2D CrosshairDrawPosition( (Center.X - (CrosshairTex->GetSurfaceWidth() * 0.5f)),
									   (Center.Y - (CrosshairTex->GetSurfaceHeight() * 0.5f)) );

const FVector2D DisplayMessagePosition((Center.X * 0.5f), (Center.Y * 0.5f));

// draw the crosshair
FCanvasTileItem TileItem( CrosshairDrawPosition, CrosshairTex->Resource, FLinearColor::White);
TileItem.BlendMode = SE_BLEND_Translucent;
Canvas->DrawItem( TileItem );

// Display a message to the screen above the crosshair
DisplayMessage(DisplayText, DisplayFont, DisplayMessagePosition);

// Display Frags

// Display Health and Armor

// Display Ammo

}

void AGloom2HUD::SetMessage(FString UIMessage)
{

DisplayText = UIMessage;

}

FString AGloom2HUD::GetUIMessage()
{
return UIMessage;
}

void AGloom2HUD::DisplayMessage(FString DisplayText, UFont* DisplayFont, FVector2D DisplayMessagePosition)
{
Canvas->DrawText(DisplayFont, (DisplayText), DisplayMessagePosition.X, DisplayMessagePosition.Y, 2.0f, 2.0f, FFontRenderInfo());

}

That all works perfectly fine as far as I can tell. However the issue comes with this code in my GameMode file.
void AGloom2GameMode::PostLogin(APlayerController * NewPlayer)
{
Super::PostLogin(NewPlayer);

AGloom2HUD * MsgHUD = Cast <AGloom2HUD>(NewPlayer->GetHUD());
if (NewPlayer)
{
	AGloom2HUD * MsgHUD = Cast <AGloom2HUD>(NewPlayer->GetHUD());
	AGloom2PlayerState * PS = Cast<AGloom2PlayerState>(NewPlayer->PlayerState);
	if (PS && GameState)
	{
		uint8 NumTeamA = 0; // Number of Alien Players
		uint8 NumTeamH = 0; // Number of Human Players
		
		for (APlayerState * It : GameState->PlayerArray)
		{
			AGloom2PlayerState * OtherPS = Cast<AGloom2PlayerState>(It);
			if (OtherPS)
			{
				if (OtherPS->bHuman)
				{
					NumTeamH++; // Adds 1 to Human Count
					if (MsgHUD)
					{
						MsgHUD->SetMessage("You have joined the Human team.");
						
					}
					
				}
				else
				{
					NumTeamA++; // Adds 1 to Alien Count
					if (MsgHUD)
					{
						MsgHUD->SetMessage("You have joined the Alien team.");
					}
					
				}
			}
		}
		if (NumTeamA > NumTeamH)
		{
			PS->bHuman = true;
		}
	}
}

}

I have it sending the “You have joined the Alien team.” message fine to the other player, but the main player or server player gets nothing. I do know that player state can be NULL for the first few frames before the game really gets going, I have tried putting a timing delay up before the message for the human player to see if that works, however, it does not. I am thinking it is something to do with replication? Should any of those functions be a replicated UFunction()? I am still learning all that I need to know with the networking information, but I do know quite a bit already. Any help will be appreciated.

I have a question, have you only tested this in the editor? The reason I’m asking, is because in my experience testing mp stuff in editor can sometimes be unreliable/confusing. The first thing I noticed is that you’re trying to call hud stuff directly in the game mode class. That’s very bad because the game mode class is server side only. You need to make a client function for your player controller class, and call the hud stuff from there.



// player controller header
UFUNCTION(client, reliable)
virtual void ClientSetMessage(const FString &_message);

// player controller cpp
void YourPlayerController::ClientSetMessage_Implementation(const FString &_message)
{
	AGloom2HUD *gloomHUD = Cast<AGloom2HUD>(MyHUD);
	if (gloomHUD)
	{
		gloomHUD->SetMessage(_message);
	}
}


also, I fixed your game mode function a bit



// game mode cpp
void AGloom2GameMode::PostLogin(APlayerController * NewPlayer)
{
	Super::PostLogin(NewPlayer);

	// these need to be made members of your game mode class
	uint8 NumTeamA = 0;
	uint8 NumTeamH = 0;


	for (FConstPlayerControllerIterator Iterator = GetWorld()->GetPlayerControllerIterator(); Iterator; ++Iterator)
	{
		YourPlayerController *currentPC = Cast<YourPlayerController>(*Iterator);
		if (currentPC && currentPC != NewPlayer)
		{
			AGloom2PlayerState *currentPS = Cast<AGloom2PlayerState>(currentPC->PlayerState);
			if (currentPS)
			{
				if(currentPS->bHuman)
					NumTeamH++;
				else
					NumTeamA++;
			}
		}
	}


	if (NewPlayer)
	{
		AGloom2PlayerState *yourPS = Cast<AGloom2PlayerState>(NewPlayer->PlayerState);
		if (yourPS)
		{
			if (yourPS->bHuman)
			{
				NumTeamH++; // Adds 1 to Human Count
				NewPlayer->ClientSetMessage("You have joined the Human team.");
			}
			else
			{
				NumTeamA++; // Adds 1 to Alien Count
				NewPlayer->ClientSetMessage("You have joined the Alien team.");
			}

			if (NumTeamA > NumTeamH)
			{
				yourPS->bHuman = true;
				NewPlayer->ClientSetMessage("You have joined the Human team.");
			}
		}
	}
}


Another question I had is, is everyone currently playing supposed to see the message, or just the person joining?
If so, you can just loop through all the player controllers and call your ClientSetMessage from there. For example, instead of NewPlayer->ClientSetMessage(“You have joined the Human team.”);, you can replace that with the following



for (FConstPlayerControllerIterator Iterator = GetWorld()->GetPlayerControllerIterator(); Iterator; ++Iterator)
{
	YourPlayerController *currentPC = Cast<YourPlayerController>(*Iterator);
	if (currentPC)
	{
		AGloom2PlayerState *currentPS = Cast<AGloom2PlayerState>(currentPC->PlayerState);
		if (currentPS)
		{
			FString plrJoin = currentPS->PlayerName + FString(" has joined the Human team.");
			currentPC->ClientSetMessage(plrJoin);
		}
	}
}