Match session creation UI - Joinable sessions with dedicated servers - (Unreal Engine module)
To support both server modes at once (peer-to-peer (P2P) and dedicated server (DS)), separate the menu widget and the widget that connects to the session creation functionality that will be shown inside the menu widget. Those widgets are the Create Match Session widget and Create Match Session DS widget.
In this tutorial, the widget that you'll prepare is the Create Match Session DS.
About the Create Match Session menu
The Create Match Session menu is a widget in Byte Wars used to select the game mode and server type for the session creation. It has been provided for you in the Resources section and consists of two parts:
CreateMatchSessionWidget
: A C++ class where most of the implementation will be.- Header file:
\Source\AccelByteWars\TutorialModules\MatchSessionEssentials\UI\CreateMatchSessionWidget.h
- CPP file:
\Source\AccelByteWars\TutorialModules\MatchSessionEssentials\UI\CreateMatchSessionWidget.cpp
- Header file:
W_CreateMatchSession
: A widget Blueprint class that was created and designed using Unreal Motion Graphics (UMG).- Widget Blueprint file:
\Content\TutorialModules\MatchSessionEssentials\UI\W_CreateMatchSession.uasset
- Widget Blueprint file:
The Create Match Session menu has four states:
- Select Game Mode : showing a selection of game modes, which are Elimination and Team Deathmatch.
- Select Network Type : showing a selection of network types. In this module, this will display Dedicated Server.
- Loading : showing the creation status and a cancel button.
- Error : showing a retry and back button.
The state changes are possible using a combination of the Unreal Motion Graphics's Widget Switcher and our AccelByteWars Widget Switcher. The AccelByteWars Widget Switcher is a custom Widget Switcher with predefined states: empty, loading, error, and success. Here are the declarations of the mentioned components in the Header file.
private:
// ...
UPROPERTY(BlueprintReadOnly, meta = (BindWidget, BlueprintProtected = true, AllowPrivateAccess = true))
UWidgetSwitcher* Ws_ContentOuter;
// ...
UPROPERTY(BlueprintReadOnly, meta = (BindWidget, BlueprintProtected = true, AllowPrivateAccess = true))
UAccelByteWarsWidgetSwitcher* Ws_Processing;
To switch between states, the Create Match Session widget has the following function:
void UCreateMatchSessionWidget::SwitchContent(const EContentType ContentType)
{
UWidget* Target = nullptr;
UWidget* FocusTarget = Btn_GameModeType_BackToCreateSession;
bool bShowBackButton = true;
switch (ContentType)
{
case EContentType::SELECT_GAMEMODE:
Target = W_SelectGameModeType;
CameraTargetY = 600.0f;
FocusTarget = Btn_Elimination;
break;
case EContentType::SELECT_NETWORKTYPE:
Target = W_SelectGameModeNetworkType;
CameraTargetY = 750.0f;
FocusTarget = W_SelectGameModeNetworkTypeButtonOuter->HasAnyChildren() ?
W_SelectGameModeNetworkTypeButtonOuter->GetChildAt(0) :
Btn_ServerType_BackToCreateSession;
break;
case EContentType::LOADING:
Target = W_Processing;
CameraTargetY = 825.0f;
Ws_Processing->SetWidgetState(EAccelByteWarsWidgetSwitcherState::Loading);
FocusTarget = Ws_Processing;
bShowBackButton = false;
break;
case EContentType::ERROR:
Target = W_Processing;
Ws_Processing->SetWidgetState(EAccelByteWarsWidgetSwitcherState::Error);
CameraTargetY = 825.0f;
FocusTarget = Ws_Processing;
bShowBackButton = false;
break;
}
DesiredFocusTargetButton = FocusTarget;
FocusTarget->SetUserFocus(GetOwningPlayer());
Ws_ContentOuter->SetActiveWidget(Target);
Btn_GameModeType_BackToCreateSession->SetVisibility(bShowBackButton ? ESlateVisibility::Visible : ESlateVisibility::Collapsed);
RequestRefreshFocus();
}
Select Game Mode state
Here, players will see two buttons to select one of the offered game modes, Elimination or Team Deathmatch. Upon clicking the button, the widget will then store what game mode the player have selected.
Those buttons are declared in the Header file:
private:
// ...
UPROPERTY(BlueprintReadOnly, meta = (BindWidget, BlueprintProtected = true, AllowPrivateAccess = true))
UCommonButtonBase* Btn_Elimination;
UPROPERTY(BlueprintReadOnly, meta = (BindWidget, BlueprintProtected = true, AllowPrivateAccess = true))
UCommonButtonBase* Btn_TeamDeathMatch;
The stored selected game mode can be accessed via this function:
public:
// ...
EGameModeType GetSelectedGameModeType() const { return SelectedGameModeType; }
Select Network Type state
This state is where the player can select which network type the session will use. By default, this state is empty. This is where you will attach your Create Match Session DS widget to.
Loading state
This state is comprised of a loading message, which can be set to any message and a cancel button that can be enabled and disabled. To do so, call SetLoadingMessage
just before calling SwitchContent(EContentType::LOADING)
:
void UCreateMatchSessionWidget::SetLoadingMessage(const FText& Message, const bool bEnableCancelButton) const
{
Ws_Processing->LoadingMessage = Message;
Ws_Processing->bEnableCancelButton = bEnableCancelButton;
}
Error state
This state is comprised of an error message, which can be set to any message and a retry button that can be enabled and disabled. To do so, call SetErrorMessage
just before calling SwitchContent(EContentType::Error)
:
void UCreateMatchSessionWidget::SetErrorMessage(const FText& Message, const bool bShowRetryButton) const
{
Ws_Processing->ErrorMessage = Message;
Ws_Processing->bShowRetryButtonOnError = bShowRetryButton;
}
About the Create Match Session DS
The Create Match Session DS widget consists of only one button that will be spawned inside the Create Match Session menu. It is available in the Resources section and is made of two parts:
CreateMatchSessionDSWidget_Starter
: A C++ class where most of our implementation will be.- Header file:
\Source\AccelByteWars\TutorialModules\Play\MatchSessionDS\UI\CreateMatchSessionDSWidget_Starter.h
- CPP file:
\Source\AccelByteWars\TutorialModules\MatchSessionDS\UI\CreateMatchSessionDSWidget_Starter.cpp
- Header file:
W_CreateMatchSession
: A widget Blueprint class that was created and designed using Unreal Motion Graphics (UMG).- Widget Blueprint file:
\Content\TutorialModules\MatchSessionDS\UI\W_CreateMatchSession.uasset
- Widget Blueprint file:
In the Header file, you will see OnlineSession
, which is your gateway to the session functionalities, the button itself, and a pointer to the parent widget.
protected:
// ...
UPROPERTY()
UAccelByteWarsOnlineSessionBase* OnlineSession;
private:
UPROPERTY(BlueprintReadOnly, meta = (BindWidget, BlueprintProtected = true, AllowPrivateAccess = true))
UCommonButtonBase* Btn_StartMatchSessionDS;
UPROPERTY()
UCreateMatchSessionWidget* W_Parent;
The code that assigns the Online Session class and the parent widget is seen on the NativeOnActivated
function.
void UCreateMatchSessionDSWidget_Starter::NativeOnActivated()
{
// ...
// Get online session
UOnlineSession* BaseOnlineSession = GetWorld()->GetGameInstance()->GetOnlineSession();
if (!ensure(BaseOnlineSession))
{
return;
}
OnlineSession = Cast<UAccelByteWarsOnlineSessionBase>(BaseOnlineSession);
// Get parent menu widget
W_Parent = GetFirstOccurenceOuter<UCreateMatchSessionWidget>();
if (!ensure(W_Parent))
{
return;
}
// ...
}
Here is a preview of the Create Match Session DS widget.
Ready the Create Match Session DS widget
The functionalities needed for the Create Match Session DS widget are the Create Session and the Cancel Joining Session. You are going to prepare the widget for those functionalities.
Open the
CreateMatchSessionDSWidget_Starter
Header file and add the following function declarations:protected:
UFUNCTION()
void CreateSession() const;
void OnCreateSessionComplete(FName SessionName, bool bSucceeded) const;Open the
CreateMatchSessionDSWidget_Starter
CPP file and add the implementations below. In theCreateSession
, change the Create Match Session menu widget state to its "Loading" state with the cancel button disabled since the request can't be canceled while it's being sent. For theOnCreateSessionComplete
, if the request succeeds, change the menu widget state to Loading with the cancel button enabled. If not, change the menu widget state to Error.void UCreateMatchSessionDSWidget_Starter::CreateSession() const
{
if (OnlineSession->ValidateToStartSession.IsBound() &&
!OnlineSession->ValidateToStartSession.Execute())
{
return;
}
// ...
W_Parent->SetLoadingMessage(TEXT_REQUESTING_SESSION_CREATION, false);
W_Parent->SwitchContent(UCreateMatchSessionWidget::EContentType::LOADING);
// ...
}void UCreateMatchSessionDSWidget_Starter::OnCreateSessionComplete(FName SessionName, bool bSucceeded) const
{
// Abort if not a game session.
if (!SessionName.IsEqual(OnlineSession->GetPredefinedSessionNameFromType(EAccelByteV2SessionType::GameSession)))
{
return;
}
if (bSucceeded)
{
W_Parent->SetLoadingMessage(TEXT_JOINING_SESSION, true);
W_Parent->SwitchContent(UCreateMatchSessionWidget::EContentType::LOADING);
}
else
{
W_Parent->SetErrorMessage(TEXT_FAILED_TO_CREATE_SESSION, true);
W_Parent->SwitchContent(UCreateMatchSessionWidget::EContentType::ERROR);
}
}Write code for the cancel joining functionality. Go back to
CreateMatchSessionDSWidget_Starter
Header file and add the following function declarations:protected:
// ...
UFUNCTION()
void CancelJoiningSession() const;
void OnCancelJoiningSessionComplete(FName SessionName, bool bSucceeded) const;Open the
CreateMatchSessionDSWidget_Starter
CPP file and add the function implementations below. For theCancelJoiningSession
, change the Create Match Session menu widget state to Loading with the cancel button disabled. If theOnCancelJoiningSessionComplete
succeeds, transition back to the Select Game Mode state. Otherwise, transition to the Error state.void UCreateMatchSessionDSWidget_Starter::CancelJoiningSession() const
{
W_Parent->SetLoadingMessage(TEXT_LEAVING_SESSION, false);
W_Parent->SwitchContent(UCreateMatchSessionWidget::EContentType::LOADING);
// ...
}void UCreateMatchSessionDSWidget_Starter::OnCancelJoiningSessionComplete(FName SessionName, bool bSucceeded) const
{
// Abort if not a game session.
if (!SessionName.IsEqual(OnlineSession->GetPredefinedSessionNameFromType(EAccelByteV2SessionType::GameSession)))
{
return;
}
if (bSucceeded)
{
W_Parent->SwitchContent(UCreateMatchSessionWidget::EContentType::SELECT_GAMEMODE);
}
else
{
W_Parent->SetErrorMessage(TEXT_FAILED_TO_LEAVE_SESSION, false);
W_Parent->SwitchContent(UCreateMatchSessionWidget::EContentType::ERROR);
}
}You can also let the player know when the dedicated server is ready or when there's an error. Go back to the
CreateMatchSessionDSWidget_Starter
Header file and add this function declaration:protected:
// ...
void OnSessionServerUpdateReceived(
const FName SessionName,
const FOnlineError& Error,
const bool bHasClientTravelTriggered) const;Open the
CreateMatchSessionDSWidget_Starter
CPP file and add this function implementation. If this function succeeds, change the loading message. Otherwise, show the error.void UCreateMatchSessionDSWidget_Starter::OnSessionServerUpdateReceived(
const FName SessionName,
const FOnlineError& Error,
const bool bHasClientTravelTriggered) const
{
// Abort if not a game session.
if (!SessionName.IsEqual(OnlineSession->GetPredefinedSessionNameFromType(EAccelByteV2SessionType::GameSession)))
{
return;
}
if (Error.bSucceeded && !bHasClientTravelTriggered)
{
// Keep showing the loading state until the client travels to the server.
W_Parent->SetLoadingMessage(TEXT_JOINING_SESSION, true);
W_Parent->SwitchContent(UCreateMatchSessionWidget::EContentType::LOADING);
}
else if (!bHasClientTravelTriggered && !Error.bSucceeded)
{
W_Parent->SetErrorMessage(TEXT_FAILED_TO_JOIN_SESSION, false);
W_Parent->SwitchContent(UCreateMatchSessionWidget::EContentType::ERROR);
}
}Now that you have all the functions declared and implemented, you can bind the widget components to those functions. In
CreateMatchSessionDSWidget_Starter
CPP file, navigate to theNativeOnActivated
function and add the following code:void UCreateMatchSessionDSWidget_Starter::NativeOnActivated()
{
// ...
Btn_StartMatchSessionDS->OnClicked().AddUObject(this, &ThisClass::CreateSession);
}With the binding done, implement code to unbind it when the widget is no longer in use. Still in the CPP file, navigate to
NativeOnDeactivated
and add the following code:void UCreateMatchSessionDSWidget_Starter::NativeOnDeactivated()
{
// ...
Btn_StartMatchSessionDS->OnClicked().RemoveAll(this);
W_Parent->GetProcessingWidgetComponent()->OnRetryClicked.RemoveAll(this);
W_Parent->GetProcessingWidgetComponent()->OnCancelClicked.RemoveAll(this);
}Build the project and open it with the Unreal Editor once it's done.
In the Unreal Editor, from the Content Browser, navigate to
Content\TutorialModules\Play\MatchSessionDS\UI\
and openW_CreateMatchSessionDS_Starter
. Make sure that all widgets are bound properly in the Bind Widgets tab and the Parent class is set toCreateMatchSessionDSWidget_Starter
.Open
Content\TutorialModules\Play\MatchSessionDS\DA_MatchSessionDSEssentials.uasset
and enable theIs Starter Mode Active
. Save the Data Asset.Click Play in the editor. Go to Online Play > Play Online > Create Match Session. The UI will appear if implemented correctly.
Resources
- The files used in this tutorial section are available in the Byte Wars GitHub repository.
- AccelByteWars/Content/TutorialModules/Play/MatchSessionDS/UI/W_CreateMatchSessionDS_Starter.uasset
- AccelByteWars/Source/AccelByteWars/TutorialModules/Play/MatchSessionDS/UI/CreateMatchSessionDSWidget_Starter.h
- AccelByteWars/Source/AccelByteWars/TutorialModules/Play/MatchSessionDS/UI/CreateMatchSessionDSWidget_Starter.cpp
- AccelByteWars/Content/TutorialModules/Play/MatchSessionDS/UI/DA_MatchSessionDSEssentials.uasset