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

マッチブラウザメニューを追加する - ピアツーピアで参加可能なセッション - (Unreal Engine モジュール)

Last updated on February 4, 2026

注釈:本資料はAI技術を用いて翻訳されています。

マッチセッション作成UIと同様に、両方のサーバーモード(ピアツーピア(P2P)と専用サーバー(DS))を同時にサポートするために、メニューウィジェットと、メニューウィジェット内に表示されるブラウズセッション機能に実際に接続するウィジェットを分離します。これらのウィジェットは、Browse MatchウィジェットとBrowse Match P2Pウィジェットです。

準備するウィジェットは、Browse Match P2Pです。

Browse Matchメニューについて

Browse Matchメニューは、Byte WarsでBrowse Matchメニューから作成されたすべてのセッションを表示し、それらに参加する方法を提供するウィジェットです。リソースセクションで利用可能で、2つの部分で構成されています:

  • BrowseMatchWidget: 実装の大部分が行われるC++クラスです。
    • ヘッダーファイル: \Source\AccelByteWars\TutorialModules\MatchSessionEssentials\UI\BrowseMatchWidget.h
    • CPPファイル: \Source\AccelByteWars\TutorialModules\MatchSessionEssentials\UI\BrowseMatchWidget.cpp
  • W_BrowseMatch: Unreal Motion Graphics(UMG)を使用して作成および設計されたウィジェットブループリントクラスです。
    • ウィジェットブループリントファイル: \Content\TutorialModules\MatchSessionEssentials\UI\W_BrowseMatch.uasset

Browse Matchメニューには6つの状態があります:

  • Browse Loading: ブラウズセッションの読み込み状態を表示します。
  • Browse Empty: 現在利用可能なセッションがないことを示すテキストを表示します。
  • Browse Not Empty: 利用可能なすべてのセッションのリストと、それぞれの参加ボタンを表示します。
  • Browse Error: ブラウズセッションがエラーを返したときのエラーテキストを表示します。
  • Join Loading: プレイヤーが参加ボタンをクリックしたときの現在の参加状態を表示します。
  • Join Error: 参加セッションがエラーを返したときのエラーテキストを表示します。

状態の変更は、Unreal Motion GraphicsのWidget SwitcherAccelByteWars Widget Switcherの組み合わせを使用して可能になります。AccelByteWars Widget Switcherは、empty、loading、error、successという事前定義された状態を持つカスタムWidget Switcherです。以下は、ヘッダーファイルで言及されているコンポーネントの宣言です:

private:
// ...
UPROPERTY(BlueprintReadOnly, meta = (BindWidget, BlueprintProtected = true, AllowPrivateAccess = true))
UWidgetSwitcher* Ws_Root;
// ...
UPROPERTY(BlueprintReadOnly, meta = (BindWidget, BlueprintProtected = true, AllowPrivateAccess = true))
UAccelByteWarsWidgetSwitcher* Ws_Browse_Content;

UPROPERTY(BlueprintReadOnly, meta = (BindWidget, BlueprintProtected = true, AllowPrivateAccess = true))
UAccelByteWarsWidgetSwitcher* Ws_Joining;

状態を切り替えるために、Browse Matchウィジェットには次の関数があります:

void UBrowseMatchWidget::SwitchContent(const EContentType Type)
{
bool bBrowseMenu = false;
EAccelByteWarsWidgetSwitcherState State = EAccelByteWarsWidgetSwitcherState::Loading;

bool bJoin_ShowBackButton = false;

UWidget* FocusTarget = Btn_Back;

switch (Type)
{
case EContentType::BROWSE_LOADING:
bBrowseMenu = true;
State = EAccelByteWarsWidgetSwitcherState::Loading;
CameraTargetY = 600.0f;
FocusTarget = Btn_Back;
break;
case EContentType::BROWSE_EMPTY:
bBrowseMenu = true;
State = EAccelByteWarsWidgetSwitcherState::Empty;
CameraTargetY = 600.0f;
FocusTarget = Btn_Back;
break;
case EContentType::BROWSE_NOT_EMPTY:
bBrowseMenu = true;
State = EAccelByteWarsWidgetSwitcherState::Not_Empty;
CameraTargetY = 600.0f;
FocusTarget = Lv_Sessions;
break;
case EContentType::BROWSE_ERROR:
bBrowseMenu = true;
State = EAccelByteWarsWidgetSwitcherState::Error;
CameraTargetY = 600.0f;
FocusTarget = W_ActionButtonsOuter->HasAnyChildren() ? W_ActionButtonsOuter->GetChildAt(0) : Btn_Back;
break;
case EContentType::JOIN_LOADING:
bBrowseMenu = false;
State = EAccelByteWarsWidgetSwitcherState::Loading;
bJoin_ShowBackButton = false;
CameraTargetY = 750.0f;
FocusTarget = Ws_Joining;
break;
case EContentType::JOIN_ERROR:
bBrowseMenu = false;
State = EAccelByteWarsWidgetSwitcherState::Error;
bJoin_ShowBackButton = true;
CameraTargetY = 750.0f;
FocusTarget = Btn_Joining_Back;
break;
}

// Display the target widget. If the last switcher is different, enable force state to directly display the correct initial state.
UWidget* TargetWidget = bBrowseMenu ? W_Browse_Outer : W_Joining_Outer;
const bool bForceState = Ws_Root->GetActiveWidget() != TargetWidget;
Ws_Root->SetActiveWidget(TargetWidget);
UAccelByteWarsWidgetSwitcher* TargetWidgetSwitcher = bBrowseMenu ? Ws_Browse_Content : Ws_Joining;
if (bBrowseMenu)
{
Ws_Browse_Content->SetWidgetState(State, bForceState);
}
else
{
Btn_Joining_Back->SetVisibility(bJoin_ShowBackButton ? ESlateVisibility::Visible : ESlateVisibility::Collapsed);
Ws_Joining->SetWidgetState(State, bForceState);
}

// Refresh widget focus.
DesiredFocusTargetButton = FocusTarget;
FocusTarget->SetUserFocus(GetOwningPlayer());
RequestRefreshFocus();

// Set FTUE
if (Type == EContentType::BROWSE_EMPTY || Type == EContentType::BROWSE_NOT_EMPTY)
{
InitializeFTUEDialogues(true);
}
else
{
DeinitializeFTUEDialogues();
}
}

BrowseMatchWidgetヘッダーファイルを見てください。W_ActionButtonsOuterという変数が表示されます。これは、このモジュールで後で準備するBrowse Match P2Pウィジェットを格納するPanel Widgetです。

private:
// ...
UPROPERTY(BlueprintReadOnly, meta = (BindWidget, BlueprintProtected = true, AllowPrivateAccess = true))
UPanelWidget* W_ActionButtonsOuter;

Browse Loading状態

この状態は、任意のメッセージに設定できるテキストで構成されています。これを行うには、SwitchContent(EContentType::BROWSE_LOADING)を呼び出す直前に、const bool bBrowseパラメータをtrueとしてSetLoadingMessageを呼び出します。

void UBrowseMatchWidget::SetLoadingMessage(const FText& Text, const bool bBrowse, const bool bEnableCancelButton) const
{
if (bBrowse)
{
Ws_Browse_Content->LoadingMessage = Text;
}
else
{
Ws_Joining->LoadingMessage = Text;
Ws_Joining->bEnableCancelButton = bEnableCancelButton;
}
}

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

Browse Empty状態

これは、現在利用可能なセッションがないことを示す静的テキストで構成されています。

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

Browse Not Empty状態

ここでは、プレイヤーは利用可能なすべてのセッションのリストを表示し、そのうちの1つに参加できます。これは、GetListViewWidgetComponentを使用して取得できるポインタのList Viewのみで構成されています。AccelByteWarsOnlineSessionModels.hにあるUMatchSessionDataというUObjectが提供されており、List Viewのエントリウィジェットにデータを渡すために使用できます。

public:
// ...
UListView* GetListViewWidgetComponent() const { return Lv_Sessions; }

Preview of the Browse Not Empty state Unreal Byte Wars joinable sessions peer-to-peer

Browse Error状態

任意のメッセージに設定できるエラーメッセージで構成されています。これを行うには、SwitchContent(EContentType::BROWSE_ERROR)を呼び出す直前に、const bool bBrowseパラメータをtrueとしてSetErrorMessageを呼び出します。

void UBrowseMatchWidget::SetErrorMessage(const FText& Text, const bool bBrowse) const
{
if (bBrowse)
{
Ws_Browse_Content->ErrorMessage = Text;
}
else
{
Ws_Joining->ErrorMessage = Text;
}
}

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

Join Loading状態

この状態は、キャンセルボタンと任意のメッセージに設定できるテキストで構成されています。これを行うには、Browse Loading状態と同じ方法でSetLoadingMessageを呼び出しますが、SwitchContent(EContentType::BROWSE_LOADING)を呼び出す直前にconst bool bBrowseパラメータをfalseに設定します。

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

Join Error状態

これは、任意のメッセージに設定できるエラーテキストで構成されています。これを行うには、SwitchContent(EContentType::JOIN_ERROR)を呼び出す直前に、const bool bBrowseパラメータをfalseとしてSetErrorMessageを呼び出します。

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

Browse Match P2Pについて

Browse Match P2Pウィジェットは、Browse Matchメニュー内に生成されるボタンで構成されています。このウィジェットは、Browse Matchメニューウィジェットを制御する責任を持ちます。リソースセクションで利用可能で、2つの部分で構成されています:

  • BrowseMatchP2PWidget_Starter: 実装の大部分が行われるC++です。
    • ヘッダーファイル: \Source\AccelByteWars\TutorialModules\Play\MatchSessionP2P\UI\BrowseMatchP2PWidget_Starter.h
    • CPPファイル: \Source\AccelByteWars\TutorialModules\MatchSessionP2P\UI\BrowseMatchP2PWidget_Starter.cpp
  • W_BrowseMatchP2P_Starter: Unreal Motion Graphics(UMG)を使用して作成および設計されたウィジェットブループリントクラスです。
    • ウィジェットブループリントファイル: \Content\TutorialModules\MatchSessionP2P\UI\W_BrowseMatchP2P_Starter.uasset

ヘッダーファイルには、セッション機能へのゲートウェイであるOnlineSession、ボタン自体、および親ウィジェットへのポインタが表示されます。

protected:
// ...
UPROPERTY()
UAccelByteWarsOnlineSessionBase* OnlineSession;
private:
UPROPERTY(BlueprintReadOnly, meta = (BindWidget, BlueprintProtected = true, AllowPrivateAccess = true))
UCommonButtonBase* Btn_Refresh;

UPROPERTY()
UBrowseMatchWidget* W_Parent;

Online Sessionクラスと親ウィジェットを割り当てるコードは、NativeOnActivated()関数で確認できます。

void UBrowseMatchP2PWidget_Starter::NativeOnActivated()
{
// ...
// Get online session
UOnlineSession* BaseOnlineSession = GetWorld()->GetGameInstance()->GetOnlineSession();
if (!ensure(BaseOnlineSession))
{
return;
}
OnlineSession = Cast<UAccelByteWarsOnlineSessionBase>(BaseOnlineSession);
ensure(OnlineSession);

// Get parent menu widget
W_Parent = GetFirstOccurenceOuter<UBrowseMatchWidget>();
if (!ensure(W_Parent))
{
return;
}
// ...
}

以下は、Browse Match P2Pウィジェットのプレビューです:

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

Browse Match P2Pウィジェットを準備する

Browse Match P2Pウィジェットに必要な機能は、ブラウズセッション、参加セッションのキャンセル、および参加セッションです。これらの機能のためにウィジェットを準備します。

  1. BrowseMatchP2PWidget_Starterヘッダーファイルを開き、次の関数宣言を追加します:

    protected:
    void FindSessions(const bool bForce) const;
    void OnFindSessionComplete(const TArray<FMatchSessionEssentialInfo> SessionEssentialsInfo, bool bSucceeded);
  2. BrowseMatchP2PWidget_Starter CPPファイルを開き、以下の実装を追加します。FindSessions()では、Browse Matchメニューウィジェットの状態をBrowse Loading状態に変更します。OnCreateSessionComplete()では、リクエストが成功し、SessionEssentialsInfo配列が空でない場合、メニューウィジェットの状態をBrowse Not Emptyに遷移し、親ウィジェットのList Viewにデータを入力します。成功したがSessionEssentialsInfoが空の場合は、Browse Empty状態に遷移します。それ以外の場合は、Browse Error状態に遷移します。

    void UBrowseMatchP2PWidget_Starter::FindSessions(const bool bForce) const
    {
    W_Parent->SetLoadingMessage(TEXT_LOADING_DATA, true, false);
    W_Parent->SwitchContent(UBrowseMatchWidget::EContentType::BROWSE_LOADING);
    Btn_Refresh->SetIsEnabled(false);
    // ...
    }
    void UBrowseMatchP2PWidget_Starter::OnFindSessionComplete(
    const TArray<FMatchSessionEssentialInfo> SessionEssentialsInfo,
    bool bSucceeded)
    {
    Btn_Refresh->SetIsEnabled(true);

    if (bSucceeded)
    {
    if (SessionEssentialsInfo.IsEmpty())
    {
    W_Parent->SwitchContent(UBrowseMatchWidget::EContentType::BROWSE_EMPTY);
    }
    else
    {
    TArray<UMatchSessionData*> MatchSessionDatas;
    for (const FMatchSessionEssentialInfo& SessionEssentialInfo : SessionEssentialsInfo)
    {
    UMatchSessionData* MatchSessionData = NewObject<UMatchSessionData>();
    MatchSessionData->SessionEssentialInfo = SessionEssentialInfo;
    MatchSessionData->OnJoinButtonClicked.BindUObject(this, &ThisClass::JoinSession);
    MatchSessionDatas.Add(MatchSessionData);
    }

    W_Parent->GetListViewWidgetComponent()->ClearListItems();
    W_Parent->GetListViewWidgetComponent()->SetListItems<UMatchSessionData*>(MatchSessionDatas);
    W_Parent->SwitchContent(UBrowseMatchWidget::EContentType::BROWSE_NOT_EMPTY);
    }
    }
    else
    {
    W_Parent->SetErrorMessage(TEXT_FAILED_TO_RETRIEVE_DATA, true);
    W_Parent->SwitchContent(UBrowseMatchWidget::EContentType::BROWSE_ERROR);
    }
    }
  3. 参加キャンセル機能に進みます。BrowseMatchP2PWidget_Starterヘッダーファイルに戻り、次の関数宣言を追加します:

    protected:
    // ...
    void CancelJoining() const;
    void OnCancelJoiningComplete(FName SessionName, bool bSucceeded) const;
  4. BrowseMatchP2PWidget_Starter CPPファイルを開き、以下の関数実装を追加します。CancelJoiningSession()では、キャンセルボタンを無効にしてメニュー状態をJoin Loadingに変更します。OnCancelJoiningSessionComplete()では、成功した場合はBrowse Not Empty状態に戻ります。それ以外の場合は、Join Error状態に遷移します。

    void UBrowseMatchP2PWidget_Starter::CancelJoining() const
    {
    W_Parent->SetLoadingMessage(TEXT_LEAVING_SESSION, false, false);
    W_Parent->SwitchContent(UBrowseMatchWidget::EContentType::JOIN_LOADING);
    // ...
    }
    void UBrowseMatchP2PWidget_Starter::OnCancelJoiningComplete(FName SessionName, bool bSucceeded) const
    {
    // Abort if not a game session.
    if (!SessionName.IsEqual(OnlineSession->GetPredefinedSessionNameFromType(EAccelByteV2SessionType::GameSession)))
    {
    return;
    }

    if (bSucceeded)
    {
    W_Parent->SwitchContent(UBrowseMatchWidget::EContentType::BROWSE_NOT_EMPTY);
    }
    else
    {
    W_Parent->SetErrorMessage(TEXT_FAILED_TO_LEAVE_SESSION, false);
    W_Parent->SwitchContent(UBrowseMatchWidget::EContentType::JOIN_ERROR);
    }
    }
  5. 参加セッションのUIロジックを実装します。BrowseMatchP2PWidget_Starterヘッダーファイルに戻り、次の関数宣言を追加します:

    protected:
    // ...
    void JoinSession(const FOnlineSessionSearchResult&SessionSearchResult) const;
    void OnJoinSessionComplete(FName SessionName, EOnJoinSessionCompleteResult::Type CompletionType) const;
  6. BrowseMatchP2PWidget_Starter CPPファイルを開き、以下の実装を追加します。JoinSession()は、メニューウィジェットをJoin Loading状態に遷移します。OnJoinSessionComplete()は、成功した場合は何もしません。それ以外の場合は、Join Errorに遷移し、エラーメッセージにエラータイプを表示します。

    void UBrowseMatchP2PWidget_Starter::JoinSession(const FOnlineSessionSearchResult& SessionSearchResult) const
    {
    if (OnlineSession->ValidateToJoinSession.IsBound() &&
    !OnlineSession->ValidateToJoinSession.Execute(SessionSearchResult))
    {
    return;
    }

    W_Parent->SetLoadingMessage(TEXT_JOINING_SESSION, false, false);
    W_Parent->SwitchContent(UBrowseMatchWidget::EContentType::JOIN_LOADING);
    // ...
    }
    void UBrowseMatchP2PWidget_Starter::OnJoinSessionComplete(
    FName SessionName,
    EOnJoinSessionCompleteResult::Type CompletionType) const
    {
    // Abort if not a game session.
    if (!SessionName.IsEqual(OnlineSession->GetPredefinedSessionNameFromType(EAccelByteV2SessionType::GameSession)))
    {
    return;
    }

    bool bSucceeded;
    FText ErrorMessage;

    switch (CompletionType)
    {
    case EOnJoinSessionCompleteResult::Success:
    bSucceeded = true;
    ErrorMessage = FText();
    break;
    case EOnJoinSessionCompleteResult::SessionIsFull:
    bSucceeded = false;
    ErrorMessage = TEXT_FAILED_SESSION_FULL;
    break;
    case EOnJoinSessionCompleteResult::SessionDoesNotExist:
    bSucceeded = false;
    ErrorMessage = TEXT_FAILED_SESSION_NULL;
    break;
    case EOnJoinSessionCompleteResult::CouldNotRetrieveAddress:
    bSucceeded = false;
    ErrorMessage = TEXT_FAILED_TO_JOIN_SESSION;
    break;
    case EOnJoinSessionCompleteResult::AlreadyInSession:
    bSucceeded = false;
    ErrorMessage = TEXT_FAILED_ALREADY_IN_SESSION;
    break;
    case EOnJoinSessionCompleteResult::UnknownError:
    bSucceeded = false;
    ErrorMessage = TEXT_FAILED_TO_JOIN_SESSION;
    break;
    default:
    bSucceeded = true;
    ErrorMessage = FText();
    }

    W_Parent->SetErrorMessage(ErrorMessage, false);
    W_Parent->SetLoadingMessage(TEXT_JOINING_SESSION, false, true);
    W_Parent->SwitchContent(bSucceeded ?
    UBrowseMatchWidget::EContentType::JOIN_LOADING :
    UBrowseMatchWidget::EContentType::JOIN_ERROR);
    }
  7. サーバーの準備ができたとき、またはエラーが発生したときにプレイヤーに知らせます。BrowseMatchP2PWidget_Starterヘッダーファイルに戻り、この関数宣言を追加します:

    protected:
    // ...
    void OnSessionServerUpdateReceived(
    const FName SessionName,
    const FOnlineError& Error,
    const bool bHasClientTravelTriggered) const;
  8. BrowseMatchP2PWidget_Starter CPPファイルを開き、以下の関数実装を追加します。この関数では、成功した場合は単に読み込みメッセージを変更します。それ以外の場合は、エラーを表示します。

    void UBrowseMatchP2PWidget_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 P2P host.
    W_Parent->SetLoadingMessage(TEXT_JOINING_SESSION, false, false);
    W_Parent->SwitchContent(UBrowseMatchWidget::EContentType::JOIN_LOADING);
    }
    else if (!bHasClientTravelTriggered && !Error.bSucceeded)
    {
    W_Parent->SetErrorMessage(TEXT_FAILED_TO_JOIN_SESSION, false);
    W_Parent->SwitchContent(UBrowseMatchWidget::EContentType::JOIN_ERROR);
    }
    }
  9. すべての関数が宣言および実装されたので、ウィジェットコンポーネントをこれらの関数にバインドします。BrowseMatchP2PWidget_Starter CPPファイルで、NativeOnActivated()関数に移動し、以下のコードを追加します。この関数でFindSessions()も呼び出していることに注意してください。これにより、プレイヤーがメニューウィジェットを開くたびにその関数がトリガーされます。

    void UBrowseMatchP2PWidget_Starter::NativeOnActivated()
    {
    // ...
    Btn_Refresh->OnClicked().AddUObject(this, &ThisClass::FindSessions, true);
    W_Parent->GetJoiningWidgetComponent()->OnCancelClicked.AddUObject(this, &ThisClass::CancelJoining);

    FindSessions(false);
    }
  10. バインディングが完了したら、ウィジェットが使用されなくなったときにバインドを解除します。CPPファイルで、NativeOnDeactivatedに移動し、次のコードを追加します:

    void UBrowseMatchP2PWidget_Starter::NativeOnDeactivated()
    {
    // ...
    Btn_Refresh->OnClicked().RemoveAll(this);
    W_Parent->GetJoiningWidgetComponent()->OnCancelClicked.RemoveAll(this);
    }
  11. プロジェクトをビルドし、Unreal Editorで開きます。

  12. Unreal Editorで、Content BrowserからContent\TutorialModules\Play\MatchSessionP2P\UI\に移動し、W_BrowseMatchP2P_Starterを開きます。Bind Widgetsタブですべてのウィジェットが適切にバインドされており、Parent classBrowseMatchP2PWidget_Starterに設定されていることを確認してください。

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

  13. Content\TutorialModules\Play\MatchSessionP2P\DA_MatchSessionP2PEssentials.uassetを開き、Is Starter Mode Activeを有効にします。Data Assetを保存します。

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

  14. エディターでゲームをプレイします。Online Play > Play Online > Browse Matchに移動します。正しく実装されている場合、スターターUIが表示されます。

リソース