UE4でRPCを利用して全クライアントの特定widgetを表示する
はじめに
今回はUE4上でRPCを用いて全クライアントでUMGを表示させてみようと思います。
RPCとは
Remote Procedure Callの略です。
自分のローカル環境にあるプログラムをA、ネットワークを介して別の場所にあるプログラムをBとすると、AがBに対して「そちら側にあるこの関数を実行してください!」とコールしたり、逆にBがAに対して「そっちにあるこの関数を実行してくれるかしら?」とコールしたりすることをRPCといいます。他の環境にイベントを送るような感じです。
UE4ではエンジンにこのRPCを利用する方法が用意されているので、Blueprint/C++両方から簡単に利用することができます。
↓公式ドキュメントはここです
では早速やっていきましょう。
プレイヤーと送るUMGの設定
PlayerControllerと表示用のWidgetを用意しておきます。
レプリケート設定
プレイヤーコントローラーとキャラクターをレプリケート設定しておきましょう。
RPCを利用してメッセージを送る
ではRPCを使っていきます。
先程添付した公式ドキュメントの中にある"表"を見ながら進めていきましょう。
インプット設定
メッセージ送信のためのイベントを飛ばす入力を設定します。
サーバーにお願いをする
サーバーに「全クライアントで指定のUMGを表示して」とおねがいします。
厳密に言うと、クライアントでUMGを表示するという「関数(イベント)」をサーバーから呼び出すようお願いします。
さて、それではサーバーにお願いしてみましょう。
プレイヤーコントローラーにカスタムイベントを作成し
そのカスタムイベントはサーバーで実行されるRPCだよという設定をし
そのカスタムイベントを呼びます
では先程の表を見ながら2のレプリケートのオプションの選択について整理しましょう。
今回はクライアントが サーバーに対してServerSendMessageを実行してねというお願いをしたいので、↓こうなります ここでの"呼び出し側クライアント所有のアクタ"はクライアントにレプリケートされている、実際に入力を受け取ったプレイヤーコントローラーのインスタンスです。このインスタンスの所有権はクライアントにあります。(レプリケート元のサーバー側にあるプレイヤーコントローラーのインスタンスの所有者はサーバーです)
クライアントにUMGを表示せよと指令を出す
👆の続きです。
今度はサーバーからクライアントに対してUIを表示せよと司令を出します。
なので例の表でいうと↓こうなります。
なのでClientShowUMGというイベントのレプリケートのオプションはRun On Owning Clientに設定しておきます。
あとはCreateWidgetなどをしてUIを表示させる流れを組んでおけば完了です。
動かしてみましょう。
サーバーはDedicatedサーバーを利用し、3人でプレイします。
3人全員に表示されました!
Dedicatedサーバー・Listenサーバー、その他オンラインマルチプレイに関してUEに限って言えば↓のスライドがわかりやすいです。
C++でもやってみる
C++でも同じことをやってみましょう。
レプリケート設定と入力イベント設定
BeginPlayとSetupInputComponentで設定します。
// OnlineMultiPlayerController.cpp #include "OnlineMultiPlayerController.h" #include "GameFramework/GameState.h" #include "GameFramework/PlayerState.h" #include "Blueprint/UserWidget.h" void AOnlineMultiPlayerController::BeginPlay() { Super::BeginPlay(); SetReplicates(true); } void AOnlineMultiPlayerController::SetupInputComponent() { Super::SetupInputComponent(); InputComponent->BindAction(TEXT("SendMessage"), IE_Pressed, this, &AOnlineMultiPlayerController::ServerSendMessage); }
RPC関数の宣言と定義
C++でRPC関数を宣言する場合にはUFUNCTIONマクロにServer, Client, NetMulticastのいずれかを指定し、さらにReliableかUnreliableも指定します(←これ指定しないとコンパイルエラーになります)。
// OnlineMultiPlayerController.h #pragma once #include "CoreMinimal.h" #include "GameFramework/PlayerController.h" #include "OnlineMultiPlayerController.generated.h" /** * */ UCLASS() class ONLINEMULTI_API AOnlineMultiPlayerController : public APlayerController { GENERATED_BODY() protected: virtual void BeginPlay() override; private: virtual void SetupInputComponent() override; UFUNCTION(Server, Unreliable) void ServerSendMessage(); UFUNCTION(Client, Unreliable) void ClientShowUMG(); };
以下実装ファイルです。
// OnlineMultiPlayerController.cpp つづき void AOnlineMultiPlayerController::ClientShowUMG_Implementation() { TSubclassOf<UUserWidget> MessageWidget = TSoftClassPtr<UUserWidget>(FSoftObjectPath(TEXT("/Game/UI/WBP_Message.WBP_Message_C"))).LoadSynchronous(); if (!MessageWidget) { return; } UUserWidget* MessageWidgetObj = CreateWidget<UUserWidget>(this, MessageWidget); if (!MessageWidgetObj) { return; } MessageWidgetObj->AddToViewport(); } void AOnlineMultiPlayerController::ServerSendMessage_Implementation() { if (HasAuthority() && GetWorld()) { AGameStateBase* GameState = GetWorld()->GetGameState(); if (!GameState) { return; } ClientShowUMG(); TArray<APlayerState*> GamePlayerArray; GamePlayerArray = GameState->PlayerArray; for (APlayerState* OnlineMultiPlayerState : GamePlayerArray) { AOnlineMultiPlayerController* OwnerPlayerController = Cast<AOnlineMultiPlayerController>(OnlineMultiPlayerState->GetOwner()); if (!OwnerPlayerController) { return; } OwnerPlayerController->ClientShowUMG(); } } }