Project Status | Completed |
Softwares Used | Unreal Engine |
Languages Used | C++, Blueprint |
I made the in-game chat part of multiplayer games simply using Unreal Engine. You can chat with other players by texting. This project is built on C++ Third Person Sample. Since the point I want to focus on is the messaging system, not the character controls, I built on the sample project.
You can review and download this project on Github!
There are 3 widgets blueprinted from C++.
We have 3 classes that derive from UserWidget for sending and reading messages. These are: UMCChatMessage, UMCChatWidget and UMCMainWidget.
When a player enters the game, a widget from the UMCMainWidget class is created and added to the screen. This class contains the UMCChatWidget.
void AMultiplayerChatCharacter::BeginPlay()
{
Super::BeginPlay();
if(IsLocallyControlled() && IsValid(MainWidgetClass))
{
MainWidget = CreateWidget(GetWorld(), MainWidgetClass);
if(MainWidget)
{
MainWidget->AddToViewport();
}
}
}
When the player presses the enter key, the StartTyping method inside this widget is called. The function in question calls methods that enable typing, such as setting the input mode as UI, darkening the background, focusing input text box etc.
void UMCMainWidget::StartTyping(APlayerController* PlayerController)
{
if(ChatWidget)
{
ChatWidget->SetVisibility(ESlateVisibility::Visible);
const FLinearColor SemiTransparentBlackColor = FLinearColor(0.f, 0.f, 0.f, 0.5f);
GetChatWidget()->BackgroundBorder->SetBrushColor(SemiTransparentBlackColor);
UWidgetBlueprintLibrary::SetInputMode_UIOnlyEx(PlayerController, ChatWidget->EnterTextWidget, EMouseLockMode::LockAlways);
ChatWidget->EnterTextWidget->SetFocus();
PlayerController->SetShowMouseCursor(false);
}
}
If the chat box is active and enter is pressed, this time my function tied to the OnTextCommitted event of the EditableText runs. This function broadcasts the delegate to which the MainWidget is connected and sends the message and the name of the person who wrote the message as a parameter.
void UMCChatWidget::EnterTextCommitted(const FText& Text, ETextCommit::Type CommitMethod)
{
if(CommitMethod == ETextCommit::OnEnter)
{
if(Text.IsEmpty())
{
HideChatWidget();
}
else
{
const FString Sender = GetOwningPlayer()->GetPlayerState()->GetPlayerName();
const FChatMessageData MessageData(Sender, Text);
OnMultiplayerChatMessageSent.Broadcast(MessageData);
EnterTextWidget->SetText(FText::GetEmpty());
HideChatWidget();
}
}
}
The MainWidget calls the AddChatText function on the character it belongs to with the GetOwningPawn function. This function calls the Server_AddChatText function running on the server, and it calls the multicast MC_AddChatText function. In other words, the messaging system I built is a multicast-based system. It works on server and all clients and ensures that a new widget is created and added to the widget on everyone's screen.
void AMultiplayerChatCharacter::AddChatText(const FChatMessageData& MessageData)
{
Server_AddChatText(MessageData);
}
void AMultiplayerChatCharacter::Server_AddChatText_Implementation(const FChatMessageData& MessageData)
{
MC_AddChatText(MessageData);
}
void AMultiplayerChatCharacter::MC_AddChatText_Implementation(const FChatMessageData& MessageData)
{
TArray FoundedWidgets;
UWidgetBlueprintLibrary::GetAllWidgetsOfClass(this, FoundedWidgets, MainWidgetClass);
TArray MainWidgets;
for(UUserWidget* NextWidget : FoundedWidgets)
{
if(UMCMainWidget* MainWidgetToAdd = Cast(NextWidget))
{
MainWidgets.Add(MainWidgetToAdd);
}
}
UMCChatMessage* ChatMessage = CreateWidget(GetWorld(), ChatMessageClass);
if(ChatMessage)
{
ChatMessage->SenderNameText->SetText(FText::FromString(MessageData.SenderName));
ChatMessage->SenderMessage->SetText(MessageData.ChatMessage);
}
for(UMCMainWidget* NextMainWidget : MainWidgets)
{
NextMainWidget->GetChatWidget()->ChatMessages->AddChild(ChatMessage);
NextMainWidget->GetChatWidget()->SetVisibility(ESlateVisibility::Visible);
NextMainWidget->GetChatWidget()->ChatMessages->ScrollToEnd();
}
}