電通総研 テックブログ

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

UE5 PixelStreamingで、Blueprint経由でWebブラウザを操作する

こんにちは、ISID 金融ソリューション事業部の岡崎です。
今回は前回のこちらの記事(UE5 PixelStreamingで、WebUI経由でUE Blueprintを操作する)の続きとして、
EpicGames社が提供するゲームエンジン、UnrealEngine5 のPlugin「PixelStreaming」を使用し、UE5でブラウザのJavaScriptで反応可能なカスタムイベントの作成や、一定時間非アクティブであったユーザーの接続を自動的に切断するタイムアウトの作成などを行いました。

はじめに

PixelStreamingを利用してブラウザのJavaScriptを発火させるためには、UE上のBlueprintとJavaScriptを紐づける必要があります。

前回のこちらの記事(UE5 PixelStreamingで、WebUI経由でUE Blueprintを操作する)では、ブラウザ上にボタンを設置し押下時にBlueprintを利用してUE画面上に文字をプリントする実装を行いました。
今回はUEを操作してプレイヤーと任意のオブジェクトが接触した際にBlueprintで接触を感知し、JavaScriptに通信を行いブラウザ上でイベントを発火させる方法について解説します。

検証環境/ツール

  • Unreal Engine5.1
  • AWS EC2
    • Windows_Server-2022-English-Full-Base-2023.01.19
  • Chrome ver.110.0.5481.177

実装手順

1. プレイヤーが任意のオブジェクトに接触した際にイベントを発火させる処理

1-1. プレイヤーの他オブジェクトの接触を感知するBlueprintを作成
1-2. UEからブラウザに通信する処理を作成
1-3. JavaScriptでUEからの通信を受け取る処理を作成
1-4. PixelStreamingを起動して接続テスト

2. 一定時間非アクティブであったユーザーがいた場合タイムアウトする処理

2-1. タイムアウト処理の設定
2-2. タイムアウト処理の実行テスト

1. プレイヤーが任意のオブジェクトに接触した際にイベントを発火させる処理

1-1. プレイヤーの他オブジェクトの接触を感知するBlueprintを作成

今回の検証では「ThirdPersonTemplate」を使用してプロジェクトを作成します。
プロジェクト作成からPixelStreamingのプラグインを有効にするまでの流れは、前回のこちらの記事(UE5 PixelStreamingで、WebUI経由でUE Blueprintを操作する)を参考にしてください。

まずは、プレイヤーが何かに接触した際、UE上に文字列を表示するBlueprintを作成します。
使用するBlueprintは「ThirdPersonTemplate」でプロジェクトを作成した際に自動的に作られるプレイヤーアクターのBlueprintである「BP_ThirdPersonCharacter」を使用します。
All>Content>ThirdPerson>Blueprints内に「BP_ThirdPersonCharacter」を見つけることができます。

Blueprint内のEventGraphには既にプレイヤーの動きやカメラ操作についてなどの処理が記述されています。

そのファイル内の空いている箇所に「Event Hit」というイベントノードを追加し、PrintStringと繋ぎます。
これにより、UEプレイ中にプレイヤーアクターが何かに接触した際、UE画面上に「Hit」の文字がプリントされます。

1-2. UEからブラウザに通信する処理を作成

ここまではUE上で「Hit」の文字をプリントするだけでしたが、今回はこの接触した際のイベントをUEからブラウザに通信しなければいけないので、その処理を追加します。

PixelStreamingのプラグインを追加した状態でBlueprintエディターの左側のComponentsタブのAddを押下し、PixelStreamingInputを検索し左側のコンポーネント一覧に追加します。
次に追加されたPixelStreamingInputを右側のグラフにドラッグ&ドロップを行い、ノードを追加します。

接触した際のイベントをUEからブラウザに通信するためには今追加したPixelStreamingInput内のSendPixelStreamingResponseというファンクションを使用します。
SendPixelStreamingResponse内にDescriptorのボックスがあるので、今回はわかりやすく「PlayerHitEvent」と記述します。

次にこのファンクションをプレイヤーが接触した際に発火するようにするため、先ほど作成した「Event Hit」のノードとつなげます。

以上でUE側の、ブラウザに通信するための処理は終わりなのでJavaScript側の処理に移っていきます。

1-3. JavaScriptでUEからの通信を受け取る処理を作成

前回のブラウザ上にボタンを設置し押下時にBlueprintを利用してUE画面上に文字をプリントする実装の記事と同様に、
player.html内部にJavaScriptの処理を追加していきました。

まずはplayer.html内で呼ばれているapp.jsで提供されているaddResponseEventListener関数を使用して、イベントハンドル用の関数を登録します。
次にplayer.htmlのJavaScriptに、UEからの通信を受信するたびに起動するイベントハンドラ関数を記述します。
これにより、UEのBlueprintで設定したSendPixelStreamingResponseノードにより送信された文字列引数(Descriptorで指定した「PlayerHitEvent」の文字列)が渡されます。

// リスナー登録
addResponseEventListener("handle_responses", myHandleResponseFunction);

//イベントハンドラ関数作成
function myHandleResponseFunction(data) {
        if (data === "PlayerHitEvent") {
            console.log("プレイヤーがオブジェクトに接触しました");
        } else {
            console.log("その他のイベント");
        }
}

この関数を作成することで、UE側からプレイヤーがオブジェクトに接触した際に発行されるBlueprintで、Descriptorで指定した「PlayerHitEvent」の文字列をJavaScript側で受信できるようになります。
また、より複雑なデータをやり取りする必要がある場合は、Descriptor内の文字列をJSON形式に記述し、JavaScript側で文字列をデコードしJSONとして使用する方法も公式で推奨されています。

1-4. PixelStreamingを起動して接続テスト

ここまでで、UE側のBlueprintの設定とJavaScriptイベントハンドラーとしての設定、ログを出力する処理の全ての実装が完了したので、実際にPixelStreamingを起動して接続テストを行います。
UEのプロジェクトをパッケージ化し、SignallingServerを立ち上げてアプリを起動します。

プレイヤーを操作し、ゲーム内のオブジェクトや壁などに接触すると、ブラウザの検証画面からJavaScriptで出力したコンソールログを見ることができます。
今回はプレイヤーをジャンプさせ、着地したときに地面のオブジェクトとプレイヤーが接触したため、JavaScriptで「プレイヤーがオブジェクトと接触しました」というログを出力しています。

今回は検証のため接触とログ出力だけを行いましたが、BlueprintとJavaScriptを変更することで、
例えばプレイヤーが獲得したアイテムを実際に購入できるECページに遷移させるといった従来のJavaScriptで行える処理なども記述できます。

2. 一定時間非アクティブであったユーザーがいた場合タイムアウトする処理

2-1. タイムアウト処理の設定

UEを使用してマッチメイキングを行うアプリなどをPixelStreamingで配信したい際、UEのアプリケーションやサーバー負荷の観点から、非アクティブなユーザーの接続を切りたい場合などが出てくると思います。
そういった際に必要になってくるのが非アクティブなユーザーに対するタイムアウト処理です。

PixelStreamingプラグインでは、app.jsの中で既にタイムアウトの関数が既に用意されています。
デフォルトではタイムアウトをしない設定になっているので、JavaScriptを使用して設定を変えていきます。

player.htmlの中に下記3つの記述をJavaScriptを追加していきます。

afk.enabled = true;

AFKとはAway From Keybordの略で、ユーザーが一定期間操作を行っていない事を指します。
デフォルトではfalseになっているので、有効に変更します。

afk.warnTimeout = 60;

ここではタイムアウトまでの時間を設定します。デフォルトでは120秒になっているので、今回は60秒に変更しました。

afk.closeTimeout = 20;

ここでは「afk.warnTimeout」の期間が過ぎてから、ユーザーに対して接続切断の警告メッセージを表示する秒数を設定します。
画面上ではここで設定した秒数のカウントダウンが表示され、設定した時間を過ぎると接続が切断されます。

2-2. タイムアウト処理の実行テスト

最後に実際にタイムアウト処理が実行できるかテストをしてみます。
afk.warnTimeoutで設定した秒数を放置した後、下のような画面が出ていれば、正常にタイムアウト処理の設定が変更できています。
また、AFKにはemitCommandやemitUIInteraction関数で設定したインタラクションも挙動に反映されてしまうので注意が必要です。

タイムアウトにより接続が切断されると、PixelStreaming開始時に表示される画面に変わります。

タイムアウトの処理の説明は以上になります。

所感

UEからJavaScriptへの通信や、逆にJavaScriptからUEへの通信の仕方はとても簡略化してあり、使いやすいと感じました。
文字列でのやり取りだけでなく、Blueprintを工夫することでJSON形式に変更することで、Webアプリケーションでできることの幅はとても広がりそうだと感じました。
特にノンゲームの領域では、別のURLへ遷移させたり、Webアプリケーションにつなげることでプロダクトの幅や品質を向上させることができると思います。

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

参考

https://docs.unrealengine.com/5.1/ja/customizing-the-player-web-page-in-unreal-engine/

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