メインコンテンツまでスキップ

マッチセッション作成 UI を追加する - ピアツーピアで参加可能なセッション - (Unreal Engine モジュール)

Last updated on June 12, 2024

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 P2P widget.

In this tutorial, the widget that you'll prepare is the Create Match Session P2P.

About the Create Match Session menu

The Create Match Session menu is a widget in Byte Wars used to select game mode and server type for the session creation. It is available in the Resources section and consists of two parts:

  • CreateMatchSessionWidget: The C++ class where most of our implementation will be.
    • Header file: \Source\AccelByteWars\TutorialModules\MatchSessionEssentials\UI\CreateMatchSessionWidget.h
    • CPP file: \Source\AccelByteWars\TutorialModules\MatchSessionEssentials\UI\CreateMatchSessionWidget.cpp
  • 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

The Create Match Session menu has four states:

  • Select Game Mode: showing a selection of game modes, Elimination and Team Deathmatch.
  • Select Network Type: showing a selection of network types. In this module, this will display P2P.
  • 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 Unreal Motion Graphics's Widget Switcher and the 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;
}

FocusTarget->SetUserFocus(GetOwningPlayer());
Ws_ContentOuter->SetActiveWidget(Target);
Btn_GameModeType_BackToCreateSession->SetVisibility(bShowBackButton ? ESlateVisibility::Visible : ESlateVisibility::Collapsed);
}

Select Game Mode state

Here, the player will see two buttons to select one of the offered game modes, Elimination or Team Deathmatch. Upon clicking the button, the widget will store the game mode the player selected.

Preview of the Select Game Mode state Unreal Byte Wars joinable sessions peer-to-peer

Those buttons are declared in the Header file.

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:

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 what you will attach your Create Match Session P2P widget to.

Preview of the Select Network Type state Unreal Byte Wars joinable sessions peer-to-peer

Loading state

This state is comprised of text that can be set to any message and a cancel button which can be enabled or 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;
}

Preview of the Loading state Unreal Byte Wars joinable sessions peer-to-peer

Error state

This state is comprised of text that can be set to any message and a retry button which can be enabled or 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;
}

Preview of the Error state Unreal Byte Wars joinable sessions peer-to-peer

About the Create Match Session P2P

The Create Match Session P2P widget has a button that will be spawned inside the Create Match Session menu. It is available in the Resources section and consists of two parts:

  • CreateMatchSessionP2PWidget_Starter: The C++ class where most of our implementation will be.
    • Header file: \Source\AccelByteWars\TutorialModules\Play\MatchSessionP2P\UI\CreateMatchSessionP2PWidget_Starter.h
    • CPP file: \Source\AccelByteWars\TutorialModules\MatchSessionP2P\UI\CreateMatchSessionP2PWidget_Starter.cpp
  • W_CreateMatchSession: A widget Blueprint class that was created and designed using Unreal Motion Graphics (UMG).
    • Widget Blueprint file: \Content\TutorialModules\MatchSessionP2P\UI\W_CreateMatchSession.uasset

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.

private:
UPROPERTY()
UAccelByteWarsOnlineSessionBase* OnlineSession;

UPROPERTY(BlueprintReadOnly, meta = (BindWidget, BlueprintProtected = true, AllowPrivateAccess = true))
UCommonButtonBase* Btn_StartMatchSessionP2P;

UPROPERTY()
UCreateMatchSessionWidget* W_Parent;

The codes that assigns the Online Session class and the parent widget is in the NativeOnActivated function.

void UCreateMatchSessionP2PWidget_Starter::NativeOnActivated()
{
Super::NativeOnActivated();

UOnlineSession* BaseOnlineSession = GetWorld()->GetGameInstance()->GetOnlineSession();
if (!ensure(BaseOnlineSession))
{
return;
}
OnlineSession = Cast<UAccelByteWarsOnlineSessionBase>(BaseOnlineSession);

W_Parent = GetFirstOccurenceOuter<UCreateMatchSessionWidget>();
if (!ensure(W_Parent))
{
return;
}

...
}

Here is a preview of the Create Match Session P2P widget:

Preview of the Create Match Session P2P widget Unreal Byte Wars joinable sessions peer-to-peer

Ready the Create Match Session P2P widget

The functionality that we need for the Create Match Session P2P widget is the session creation and the cancel joining session. You are going to prepare the widget for those functionalities.

  1. Open the CreateMatchSessionP2PWidget_Starter Header file and add the following function declarations:

    protected:
    UFUNCTION()
    void CreateSession() const;

    void OnCreateSessionComplete(FName SessionName, bool bSucceeded) const;
  2. Open the CreateMatchSessionP2PWidget_Starter CPP file and add the implementations below. In the CreateSession, you're changing the Create Match Session menu widget state to its Loading state with the cancel button disabled since we can't cancel while the request is being sent. For the OnCreateSessionComplete, if the request succeeds, we change the menu widget state to Loading with the cancel button enabled. If not, we change the menu widget state to Error.

    void UCreateMatchSessionP2PWidget_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);

    // TODO: Call the session creation through Online Session
    }

    void UCreateMatchSessionP2PWidget_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);
    }
    }
  3. Add code for the cancel joining functionality. Go back to the CreateMatchSessionP2PWidget_Starter Header file and add the following function declarations:

    protected:
    UFUNCTION()
    void CancelJoiningSession() const;

    void OnCancelJoiningSessionComplete(FName SessionName, bool bSucceeded) const;
  4. Open the CreateMatchSessionP2PWidget_Starter CPP file and add the function implementations below. For the CancelJoiningSession, you're changing the Create Match Session menu widget state to Loading with the cancel button disabled. For the OnCancelJoiningSessionComplete, if successful, you're transitioning back to the Select Game Mode state. Otherwise, it transitions to the Error state.

    void UCreateMatchSessionP2PWidget_Starter::CancelJoiningSession() const
    {
    W_Parent->SetLoadingMessage(TEXT_LEAVING_SESSION, false);
    W_Parent->SwitchContent(UCreateMatchSessionWidget::EContentType::LOADING);

    // TODO: Call the cancel joining functionality through Online Session
    }

    void UCreateMatchSessionP2PWidget_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);
    }
    }
  5. Let the player know when the server is ready or when there's an error. In P2P, ideally, clients will be able to join the server directly after OnJoinComplete was called, but you are implementing this as a safety net. Go back to the CreateMatchSessionP2PWidget_Starter Header file and add this function declaration:

    protected:
    void OnSessionServerUpdateReceived(
    const FName SessionName,
    const FOnlineError& Error,
    const bool bHasClientTravelTriggered) const;
  6. Open the CreateMatchSessionP2PWidget_Starter CPP file and add the function implementation below. For this function, if successful, you simply change the loading message. Otherwise, show the error.

    void UCreateMatchSessionP2PWidget_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)
    {
    // waiting for further update
    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);
    }
    }
  7. Now that you have all the functions declared and implemented, bind the widget components to those functions. In the CreateMatchSessionP2PWidget_Starter CPP file, navigate to the NativeOnActivated function and add the highlighted lines in the following code:

    void UCreateMatchSessionP2PWidget_Starter::NativeOnActivated()
    {
    ...

    // TODO: Add your Online Session delegates setup here

    Btn_StartMatchSessionP2P->OnClicked().AddUObject(this, &ThisClass::CreateSession);
    W_Parent->GetProcessingWidgetComponent()->OnCancelClicked.AddUObject(this, &ThisClass::CancelJoiningSession);
    W_Parent->GetProcessingWidgetComponent()->OnRetryClicked.AddUObject(this, &ThisClass::CreateSession);
    }
  8. 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 highlighted lines in the following code:

    void UCreateMatchSessionP2PWidget_Starter::NativeOnDeactivated()
    {
    ...

    // TODO: Add your Online Session delegates cleanup here

    Btn_StartMatchSessionP2P->OnClicked().RemoveAll(this);
    W_Parent->GetProcessingWidgetComponent()->OnRetryClicked.RemoveAll(this);
    W_Parent->GetProcessingWidgetComponent()->OnCancelClicked.RemoveAll(this);
    }
  9. Build the project and open it in the Unreal Editor.

  10. In the Unreal Editor, from the Content Browser, navigate to Content\TutorialModules\Play\MatchSessionP2P\UI\ and open W_CreateMatchSessionP2P_Starter. Make sure that all widgets are bound properly in the Bind Widgets tab and the Parent class is set to CreateMatchSessionP2PWidget_Starter.

    Bind widgets tab Unreal Byte Wars joinable sessions peer-to-peer

  11. Open Content\TutorialModules\Play\MatchSessionP2P\DA_MatchSessionP2PEssentials.uasset and enable the Is Starter Mode Active. Save the Data Asset.

    Data Asset changes preview Unreal Byte Wars joinable sessions peer-to-peer

  12. Play the game in the editor. Navigate to Online Play > Play Online > Create Match Session. The starter UI will be shown if implemented correctly.

Resources