電通総研 テックブログ

電通総研が運営する技術ブログ

UE5でマルチプレイに対応したエモート機能を作成する

こんにちは、ISID 金融ソリューション事業部の岡崎です。
今回はUE5でマルチプレイに対応したエモート機能を作成する方法をご紹介します。
エモート機能とは、マルチプレイのゲームにおいて、各キャラクターが感情を表現する為の機能です。
例えばFortniteではキャラクターに様々な動きやダンスをさせることができます。

はじめに

UE5では「ListenServer」と「DedicatedServer」の2種類のゲームサーバー方式が使用可能です。
各サーバーについては、こちらの金融ソリューション事業部の山下さんの記事で紹介されているので説明は割愛します。
今回は検証を簡易にするため「ListenServer」を使用してマルチプレイでのエモート機能を作成しますが、
「DedicatedServer」でも同じ挙動をする実装方法となります。

検証環境/ツール

  • Unreal Engine5.2.0
  • AWS EC2
    • Windows_Server-2022-English-Full-Base-2023.01.19

実装手順

  1. エモート用WidgetBlueprintの作成
  2. エモート用WidgetをCharacterBlueprintに付与
  3. CharacterBlueprintの動作作成
  4. マルチプレイレプリケーション設定

1. エモート用WidgetBlueprintの作成

今回はThirdPersonテンプレートを利用したプロジェクトで進めていきます。

まず初めにエモート用Widgetに使用するWidgetBluePrintを作成します。
コンテンツドロワーより、WidgetBlueprintを選び「WBP_IconEmote」で作成します。

以前紹介したUE5 PixelStreamingで、マウスカーソルを別の画像に変更してクリックイベントを作成する記事にて、
WidgetBlueprint内に任意の画像を取り込む方法があるので、同様にエモートに使用したい画像を取り込み「IconEmote」という名前をつけました。また前回同様「Canvas Panel」を作成し、位置を中心に変更させます。

今回は別のBlueprintでもこのWidgetを呼び出して使う予定なので、詳細タブの上部にある「Is Variable」にチェックを入れておきます。

2. エモート用WidgetをCharacterBlueprintに付与

まずはCharacterBlueprintを開きます。
ThirdPersonTemplateを使用して作成したプロジェクトの場合、
All>Content>ThirdPerson>Blueprints内に「BP_ThirdPersonCharacter」を見つけることができます。

「BP_ThirdPersonCharacter」のViewPortを開き、Widget用のコンポーネントを追加します。

ViewProt左上部の追加ボタンを押してWidgetと検索し、クリックで選択します。
左側のコンポーネント一覧に追加されるので、名前を「Widget_IconEmote」と変更します。

次に追加した「Widget_IconEmote」と、先ほど作成した「WBP_IconEmote」を連携させます。
右側のユーザーインターフェース内の「Widget Class」のセレクトボックスで「WBP_IconEmote」を選択します。

連携すると下記画像のように追加したエモート画像がCharacterBlueprintに付与されます。
トランスフォーム欄から位置や大きさを動かすことで、エモート機能のようになってきました。

コンパイル、保存を行い、レベルのViewPortで確認してみます。

キャラクターの頭上にアイコンを出すことは出来ましたが、キャラクターが横を向くとアイコンも横を向いてしまい画面から見えなくなってしまいました。
解決法として、ViewPortのユーザーインターフェース欄のSpaceのセレクトボックスを「World」から「画面」を選ぶことで、キャラクターがどの向きを向いてもエモートアイコンが見えるように設定できます。

3. CharacterBlueprintの動作作成

今回は、キーボードの「5」を押した時に、2秒間エモートアイコンがキャラクターの頭上に表示されるBlueprintを作成します。
今のままでは、アイコンが出続けてしまうのでまずゲームスタートとともにアイコンを隠す処理を行います。

まずは「BP_ThirdPersonCharacter」から「WBP_IconEmote」に接続できるように「Event BeginPlay」から「Cast To WBP_IconEmote」をピンでつなげます。

さらにObject属性に対して、「Get User Widget Object」ノードを追加し、ターゲットを「Widget Icon Emote」にします。
これにより、「BP_ThirdPersonCharacter」から「WBP_IconEmote」に接続できるようになりました。

次に、アイコンを非表示にするために「Set Visibility」ノードを接続し、ターゲットを「Cast To WBP_IconEmote」の「As WBP Emote」に接続します。「In Visibility」の値を「非表示」にすることで、アイコンを非表示にできますが、今回は後述のマルチプレイ用に、表示・非表示を司る変数を作成しておきます。

画面左下の変数欄からプラスを押し、「var_IconEmoteByte」という変数を作成します。タイプはByteを選択し、デフォルト値を「2」にします。

作成した「var_IconEmoteByte」を「Set Visibility」につなぎます。

ここで入力した「2」というのは、「In Visibility」の値の配列番号になり、「非表示」を意味しています。

ここまでで、キャラクター頭上のアイコンは一旦非表示になっているはずです。

次にキーボードの「5」を押した時にアイコンを表示させる処理の説明を行います。
「Press 5」ノードを追加し、「Pressed」のピンに対して、「var_IconEmoteByte」をセットするためのノードを追加します。

変数の値を「0」(「In Visibility」の値の配列番号になり、「表示」の意)に書き換える処理の後に、先ほど作成した「Cast To WBP_IconEmote」の流れを複製します。

これにより、キーボードの「5」を押した時にアイコンを表示させることができます。

しかし、この状態ではまだ左右の画面(マルチプレイヤー間)での同期は取れていないので、
次のステップで修正していきます。

4. マルチプレイレプリケーション設定

UEでマルチプレイを行う際は、ゲームサーバーがゲームクライアントに対して、情報をレプリケート(複製)することで、
各クライアント間(プレイヤー間)での差異が生じないようになります。
今回作った処理は、まだクライアントで行われているだけの処理なので、クライアント間では同期されません。

まずは先ほど作成した変数「var_IconEmoteByte」をレプリケーションする設定にします。
「var_IconEmoteByte」の詳細画面からレプリケーションの設定を「RepNotify」に設定します。

「RepNotify」とは、変数の値をウォッチしており、変数の値が変わった時に後続の関数「OnRep〜」関数を起動させます。
変数を「RepNotify」設定にすると、左側の関数欄に「OnRep_var_IconEmoteByte」という関数ができます。

次に、「Cast To WBP_IconEmote」から「Set Visibility」の処理をコピーし、「OnRep_var_IconEmoteByte」内で貼り付けます。
「OnRep_var_IconEmoteByte」内への移動方法は、左側の関数欄の「OnRep_var_IconEmoteByte」をダブルクリックすることで移動ができます。

貼り付けた処理を「On Rep Var Icon Emote Byte」ノードに繋ぎ、元あった場所の処理は消してしまいます。

これで、「var_IconEmoteByte」の値が書き換わった際に、後続の処理をゲームサーバーからゲームクライアントに同期してくれるようになります。

アイコンを表示させてから2秒後にアイコンを消す処理を追加するために、下のように「Delay」ノードと「var_IconEmoteByte」のセット用のノードを追加します。非表示にする必要があるので、「var_IconEmoteByte」の値は「2」にします。

実行すると下の動画のようになります。

左側の画面のプレイヤーのエモートアイコンは右側の画面に同期されているのに対し、右側の画面のエモートは左側に同期されていないことがわかります。

この挙動はリッスンサーバーで検証を行っているためのものです。
リッスンサーバーでは1人のプレイヤーがゲームサーバーとゲームクライアント両方の機能をもち、
その他のプレイヤーがゲームクライアントだけの機能を持ちます。
上の動画では左側がゲームサーバーを持っているクライアントで、右側がゲームクライアントだけの画面になります。
UEのレプリケーション機能では、
ゲームサーバーが情報をレプリケート(複製)してゲームクライアントの情報を書き換え、同期することは出来ますが、
ゲームクライアントがゲームサーバーの情報を書き換えて同期することは出来ません。
また、クライアントがクライアントの情報を書き換えて同期することも出来ません。

まとめると下記のようになります。

  • ゲームサーバーでの情報変更 → ゲームサーバーの情報を複製して、ゲームクライアントの情報を書き換えることが可能
  • ゲームクライアントでの情報変更 → ゲームクライアントの情報を複製して、ゲームサーバーの情報を書き換えることは不可能
  • ゲームクライアントでの情報変更 → ゲームクライアントの情報を複製して、他のゲームクライアントの情報を書き換えることは不可能

上記の理由から、左側の行動はレプリケートされ、右側の行動はレプリケートされない処理になってしまっているので修正していきます。

まずはカスタムイベントを追加します。今回は「Press5sync」という名前のイベントにしました。


「Press5sync」を選択し、右側の詳細画面から複製欄で「サーバーで実行」を選択します。

次にキーボードの「5」を押して起動する処理を、今作った「Press5sync」に繋ぎ直し、キーボードの「5」の処理は「Press5sync」を起動させるためにつなぎます。

これにより、ゲームクライアント側で行われた処理も、ゲームサーバーで実行する形式をとるのでゲームサーバーとゲームクライアント間で同期が取れるようになります。

同様の手順で、TextタイプのWidgetBlueprintを追加することで、下記のようなエモートを追加することもできます。
以上が、マルチプレイに対応したエモート機能を作成する手順になります。

所感

今回のUE5でマルチプレイに対応したエモート機能を作成する手順を調査するにあたり、
UE5でのWidgetBlueprintの使用方法や、マルチプレイのゲームサーバー、ゲームクライアント間の関係性などに詳しくなることが出来ました。
マルチプレイのプロジェクトを作成するにあたり、プレイヤー間の情報の同期(レプリケーション)は必須事項になるので、今回の基礎的な知識をもとに、より複雑なレプリケーションの方法なども学習していきます。

現在ISIDはweb3領域のグループ横断組織を立ち上げ、Web3およびメタバース領域のR&Dを行っております(カテゴリー「3DCG」の記事はこちら)。 もし本領域にご興味のある方や、一緒にチャレンジしていきたい方は、ぜひお気軽にご連絡ください!
私たちと同じチームで働いてくれる仲間を、是非お待ちしております!
ISID採用ページ(Web3/メタバース/AI)

参考

執筆:@okazaki.wataru、レビュー:@wakamoto.ryosuke
Shodoで執筆されました