LoginSignup
7
2

More than 3 years have passed since last update.

【UE4】OnlineBeacon使ってみた!(サンプルあり)

Last updated at Posted at 2021-03-10

はじめに

今回の記事では、「同一ワールドに合流せずにホストとクライアントで通信するための仕組み」であるOnlineBeaconについて解説します。

OnlineBeacon@UnrealEngine公式ドキュメント

元々はGearsOfWarのマッチングをスムーズに行うために、素のTCP通信で席の予約やロビーのようなシステムを行うための仕組みだったものをUE4のRPC(遠隔関数呼び出し)で実装できるようにしたものだそうです。
通常のJoinSessionでの接続とは違いレベルの移動などの動作を含まずに通信できるため、高速にセッションのホストと通信して確実な状態になってから改めてJoinSessionしたりすることができます。
通信を行う主体はActorなのでもちろんブループリントを用いて実装を行うことも可能です!

◆ 関連クラス説明

公式ドキュメントと被る部分も多々ありますが、まずクラスの構成を解説します。
関連クラスはOnlineSubsystemUtilsモジュールに配置されています。
いくつかのクラスは基底クラスだけが実装されていてタイトルのニーズに合わせて追加の実装が必要な部分があります。
ブループリントを用いて実装したい場合は、主要な関数はブループリントに露出していないのでc++で基底となるクラスを実装する必要があります。

OnlineBeacon_ Lucidchart - Google Chrome 2021-03-0 (1).png

▼ AOnlineBeacon

オンラインビーコンの基底クラス。BeaconNetDriverを介して通信を行う機能を持ちます。

▼ AOnlineBeaconHost

ホスト側のプロセス内に作成されて、登録されたOnlineBeaconHostObjectを保持します。
クライアントから送られてきた通信を適切にBeaconHostObjectに振り分けます。

▼ AOnlineBeaconHostObject

ビーコン名とどのBeaconClientを使うかを紐づけるための情報を持ちOnlineBeaconClientと対になるようにタイトルに合わせた実装を行うことになります。注意点としてこれがBeaconClientと直接通信を行うわけではないことを覚えておいてください。

通常のUnrealEngineのワールドの仕組みの中で言うとゲームモードに近い立ち位置になります。
一番重要な仕事はクライアントからの要求に応じて対応するBeaconClientを作成することです。
ホスト側はセッションを作成した後(直接IPでやりとり場合はSessionは不要)、このアクターとOnlineBeaconHostの両方をスポーンし、
自作のBeaconHostObjectをOnlineBeaconHostに登録します。

    //OnlineBeaconHostを作成する
    AOnlineBeaconHost* BeaconHost = GetWorld()->SpawnActor<AOnlineBeaconHost>(AOnlineBeaconHost::StaticClass()); 
    if (BeaconHost && BeaconHost->InitHost())
    {
        //OnlineBeaconHostObjectを作成する
        AOnlineBeaconHostObject* BeaconHostObject = GetWorld()->SpawnActor<AOnlineBeaconHostObject>(ATestBeaconHost::StaticClass());
        if (BeaconHostObject)
        {
            //BeaconHostObject->Init();
            BeaconHost->RegisterHost(BeaconHostObject); //登録する
            Beacon = BeaconHost;
            BeaconHost->PauseBeaconRequests(false);
        }
    }

▼ AOnlineBeaconClient

OnlineBeaconの仕組みの中でアプリケーションに必要な通信をUnrealのRPCと同じインターフェイスを用いて直接行うことができるアクターです。
一つの通信を行うときクライアントに一つ、それに対応する形でサーバー側に一つ生成されます。
サーバー側で複数のクライアントから要求があった場合はサーバー側では複数のクライアントが作成されることになります。

OnlineBeacon_ Lucidchart - Google Chrome 2021-03-0 (6).png

◆ OnlineBeacon有効化方法

前準備としていくつか設定が必要な部分があります。

DefaultEngine.iniファイル

DefaultEngine.iniファイル中のEngine.GameEngineセクションにBeaconNetDriverの定義が必要です。

DefaultEngine.ini
[/Script/Engine.GameEngine]
+NetDriverDefinitions=(DefName="BeaconNetDriver",DriverClassName="OnlineSubsystemUtils.IpNetDriver",DriverClassNameFallback="OnlineSubsystemUtils.IpNetDriver")

上記定義が無い場合、

LogNet: CreateNamedNetDriver failed to create driver from definition BeaconNetDriver

というエラーが出力されOnlineBeaconは通信をはじめることが出来なくなります。

Beacon自体は例えばIPアドレスの直接指定でも動作可能ですが、通常はセッションなどと組み合わせて使うので、
DefaultEngine.iniにて適切なオンラインサブシステムを有効化しておく必要があります。以下の例ではOnlineSubsystemNullを有効化しています。

DefaultEngine.ini
[OnlineSubsystem]
DefaultPlatformService=Null

◆ 処理の流れ

▼ 1. 通信前の準備

OnlineBeacon_ Lucidchart - Google Chrome 2021-03-0 (3).png

ホスト側

ホスト側で自作したAMyOnlineBeaconHost(AOnlineBeaconHostObjectを継承)とAOnlineBeaconHostを作成し、前者を後者に登録します。
成功するとBeaconNetDriverが作成され動作を始めます。

またセッション検索用にCreateSessionを行っておきます。

クライアント側

AMyBeaconClient(AOnlineBeaconClientを継承)を通常のアクターと同じようにスポーンさせておきます。

▼ 2. 接続

OnlineBeacon_ Lucidchart - Google Chrome 2021-03-0 (4).png

クライアント側

FindSessionを実行し、ホストのセッションを見つけます。この後JoinSessionせずに SessionInterface::GetResolvedConnectString() を用いて接続先アドレスを取得し、それを引数にAOnlineBeaconClient::InitClient(FURL)を呼び出します。

MyOnlineBeaconClientBase.cpp
bool AMyOnlineBeaconClientBase::ConnectToBeaconHost(const FBlueprintSessionResult& InSessionSearchResult)
{
    const FOnlineSessionSearchResult& SearchResult = InSessionSearchResult.OnlineResult;
    // Make this stuff common?
    bool bSuccess = false;
    IOnlineSubsystem* OnlineSub = Online::GetSubsystem(GetWorld());
    if (OnlineSub == nullptr) { return bSuccess; }

    IOnlineSessionPtr SessionInt = OnlineSub->GetSessionInterface();
    if (SessionInt.IsValid() == false) { return bSuccess; }

    FString ConnectInfo;
    if (SessionInt->GetResolvedConnectString(SearchResult, NAME_BeaconPort, ConnectInfo))
    {
        FURL ConnectURL(NULL, *ConnectInfo, TRAVEL_Absolute);
        if (this->InitClient(ConnectURL) && SearchResult.Session.SessionInfo.IsValid())
        {
            //          DestSessionId = DesiredHost.Session.SessionInfo->GetSessionId().ToString();
            bSuccess = true;
        }
        else
        {
            UE_LOG(LogBeaconSample, Warning, TEXT("ConnectToBeaconHost: Failure to init client beacon with %s."), *ConnectURL.ToString());
        }
    }
    return bSuccess;
}

この関数によってクライアント側にBeaconNetDriverが作成され、ホストへの接続を試みてくれます。

ホスト側

クライアントからの要求に応じてMyBeaconHost内に登録されているMyBeaconClientがホスト側にも作成されます。
こうして作成されたクライアントとホスト側のMyBeaconClientに実装されたRPCによって通信が可能になります。

▼ 3. 切断

サーバー側もクライアントも不要になったアクター(MyBeaconHost/MyBeaconClient)は手動でDestroyActorする必要があります。

◆ 実装サンプル

エンジンに含まれるサンプルとしてはATestBeaconHost/Client、ASpectatorBeaconHost/Client、ALobbyBeaconHost/ClientがOnlineSubstemUtilsモジュール内にあります。
実装の前にこれらのコードをじっくり読んでみることをお勧めします。

ホスト/クライアント/レプリケート用の情報保持アクター(State)をブループリントで実装してみたサンプル

最低限の実装しかありませんが、ブループリントでタイトルにあった実装を加えるサンプルになっています。
接続の削除や複数回接続する際の実装などは考慮されておりません。ご了承ください。
サンプルプロジェクト@Github

以下サンプル中のブループリントのスクリーンショット

BP_BeaconHost 2021-03-09 23.29.54 (1).png

BP_BeaconClient 2021-03-09 23.29.13 (1).png

BP_BeaconState 2021-03-09 23.31.09 (1).png

OnlineBeaconによる接続を試みているところ

BP_JoinBeacon 2021-03-09 23.36.10 (1).png

◆ Advanced

レプリケーションについて

OnlineBeaconはClient同士の通信の他HostからClientへのアクターのレプリケーションにも対応しています。
詳しくはALobbyBeaconHost::SetupLobbyState()、LobbyBeaconState.hにあるALobbyBeaconStateを参照してください。
基本的には通常のレプリケートに対応したアクターを作成したあと、SetNetDriverNameを呼ぶだけです。
クライアントからの接続に成功するとサーバー側で指定されているアクターがクライアントにレプリケートされます。

7
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
2