この記事は Unreal Engine (UE)のカレンダー | Advent Calendar 2022 2日目の代理投稿記事です。
はじめに
突然ですが、UE4.27から追加されているCommon ConversationプラグインをUE5.1で動かしてみたというサンプルプロジェクトを公開しました!
github.com
上図のようなグラフベースのUIを使って、会話・クエスト・ゲームの進行状況などを管理・制御することができます。
良い所
注意
- Common Conversation自体が公式でサポートされていない実験的機能のため、本サンプルも動作保証をしません。何かトラブルが発生した際は独自に解決・拡張・エンジン改造をお願いします。
- 最低限の内容ですので、実際に使用する際はプロジェクトに応じてカスタマイズする必要があります
- Common Conversationの仕様によりBPのみでは実装することができないため、C++プロジェクトになっています。また同じく仕様によりGameFeature機能を活用しています。
- Common Conversationはマルチプレイを想定した機能になっているため、エンジン標準ではシングルプレイゲームでは機能しません。そのため、シングルプレイでも動作するようにC++で専用のBPノードを用意しています。
…動作保証しない感じならサンプルなんて作るなよ!という言葉も聞こえてきそうですが、僕も含めてどんな機能なのか気になった方も多いと思うので公開してみました!(現時点でドキュメントや参考記事類が社内外で一切ないのでエンジンコードを見るしかないですし…結構たいへんでした…)
ということもあり、初心者の方にはオススメできるものではなく…サンプルにおける実装とエンジンコードを合わせて機能・仕様を把握・カスタムできる人を想定としたものです。ご理解くださいまし。
以降はサンプルのちょっした解説とか、エンジンコードで見るべき所とかを雑多に書いてます。
Common Conversationってなに?
各機能のざっくり解説
- Entry Point : 開始位置。ここで設定したTagをBPからStart Conversationノードで指定する
- Task : グラフにおけるメインとなるノード。TaskNodeを継承したものを用意しグラフ上に配置していく。
- Choice : Taskノードに追加するノード。その名の通り、選択肢を管理。
- Requirement : Taskノードに追加するノード。追加したTaskを実行するために必要な条件を設定
- Side Effect : Taskノードに追加するノード。追加したTaskの後に実行される
サンプルのぷち解説
Conversation_Sample_00/DA_CommonConversation_00
ConversationDatabaseを元に作られたDataAssetであり、今回の目玉であるグラフベースのUIはここで設定されています。Common ConversationはGameFeatureプラグインにあるDataAssetしか認識しないようになっているため、このアセットはConversation_Sample_00というGameFeaturesプラグイン内に格納されています。
ビヘイビアツリーを触ったことがある方には親しみのあるエディタになっています。操作もだいたい同じです。
グラフやタスクノードの右クリックメニューから各要素を追加していき、右側の詳細パネルで各パラメータを設定します。
また、新規でノードを作成する際はエディタ上部のツールバーにある「New ~」ボタンが便利です。
CommonConversationSample/BP/
DA_CommonConversation_00で使用している各ノードがここに格納されています。
基本的にはノード標準の関数をOverrideし、引数のConversation Contextから各情報を取得し、何か独自の処理をし、最後にCommonConversationに対して次のノードへの移動をリクエストしたりします。
そんなに複雑なものではないので是非各BPの実装を見てみてください。
Content/UE5_ComnConvoSample/BP_SpeakerPawn
DA_CommonConversation_00を呼び出したり、入力に応じてCommonConversationに対して選択処理をリクエストしたりしています。
特に複雑なことはしていないのですが、シングルプレイにするためにC++で用意したノードがいくつか使用されています。
Load ad Activate Game Feature Pluginノード
指定のGameFeaturesの有効化をリクエストし、Activateが完了したらSucceededピン以降の処理を実行するLatentノードです。
Unreal Engine 5 早期アクセスの注目機能総おさらい Part 2【CEDEC 2021】 | ドクセル
UE5.1.0現在、残念ながらBPオンリーのプロジェクトでGameFeaturesを有効化するにはConsoleCommandから実行する必要があります。また、対象のGameFeaturesプラグインの有効化が完了化したかを知るすべがありません。なので、C++側では用意されている完了通知付きの有効化リクエスト処理をBP化しました。といっても、↓を呼んでいるLatentノードを [UE4]簡単なLatentノードの作り方|株式会社ヒストリア を参考に作っただけです。
GameFeaturesSubsystem->LoadAndActivateGameFeaturePlugin(PluginURL, GameFeaturePluginLoadComplete);
詳しくは Plugins/CommonConversationSample/Source/CommonConversationSample/Private/AsyncActionLoadAndActivateGameFeaturePlugin.cpp で。そこそこ便利だと思うので、GameFeaturesプラグインを活用しようとしてる方は是非。
SampleConversationInstanceクラス
マルチプレイの場合ですと良い感じに処理が走るのですが、シングルプレイの場合ですと下記処理の後半が機能しません、そのため、選択肢に対しての対応が諸々良い感じに動きません。
void UConversationParticipantComponent::SendClientConversationMessage(const FConversationContext& Context, const FClientConversationMessagePayload& Payload) { #if WITH_SERVER_CODE LastMessage = Payload; //@TODO: CONVERSATION: We could potentially send the user no choices? I guess that's a possibility. if (GetOwner()->GetRemoteRole() == ROLE_AutonomousProxy) { ClientUpdateConversation(LastMessage); } #endif }
なので、OnStartWatingChoiceデリゲートや選択待ち用のフラグを追加した UConversationInstance継承のクラスを用意しています。
なお、自作のUConversationInstanceを使ってCommonConversationを動かすためには、プロジェクト設定のConversationInstanceClassで設定する必要があります。
GetLastMessage
ConversationParitipantComponentが管理している各選択肢の内容を取得するためのノードです。USampleConversationBlueprintFunctionLibraryで実装してます。
RequestCustomAdvanceConversationノード
どの選択肢を選んだかを伝えるノードです。本来はConversationParitipantComponentのRequestServerAdvanceConversationを実行すればいいんですが、シングルプレイにおける問題を解決するためにSampleConversationInstanceが持つ選択待機フラグをfalseにする処理を入れたものを自作しています。
Asset ManagerにConversationDatabase を追加
ConversationDatabaseはAssetManagerを使って収集されることを前提としているため、上図の様に指定することが必須になります。設定していなかった場合は下記のようなワーニングが出ますし、CommonConversationが機能しません。
LogCommonConversationRuntime: Warning: Entry point Conversation.Entry.Sample.00 did not exist or had no destination entries; conversation aborted
また、ConversationDatabaseは直接使われるのではなくGameplayTagを通じて使用されるため、参照関係が設定されずCookの対象外になることがあります。そのため、上図のようにAlwaysCookにしておくとパッケージでも正常に動作します。
あとはサンプル内のBPやC++コードをご確認ください!
さいごに
グラフベースのUIで処理の流れを組めるのはやはり強力であり、色々可能性を感じる機能ではあるのですが…今回のサンプルのように少し工夫が必要であったり、場合によってはエンジンコードの解析・改造が必要になることもあるかと思います。公式のサポート・動作保証に関しても、少なくともUE5.1.0時点ではありません(某うどんでのサポートもあまり期待しないでください)。
github.com
そのため、もしかすると上記の非公式プラグインの方が動作の安定性など様々な面でメリットが大きいかもしれません。このプラグインは今回調べる過程で教えてもらったものなのですが、Reikon Gamesというゲーム会社で実際に使われた機能を公開したもののようです。個人的も凄く気になるのでいつか触ってみようと思ったりしてます。
なんだかCommon Conversationの宣伝というよりも「期待できるけど少し大変だし、いろいろ自己責任で」というのが目立ってしまった気もしますが、先日のアンリアルフェスの講演を見て気になっていた方の参考になれば幸いです。
おしまい