UE5で会話システムを作った備忘録HD2D風-その2


目的

有料アセットとかは使わずに HD2D風(3D空間上に2Dキャラがいる状態)のマップ内で会話システムを作りたかった。
とても長くなってしまいそうなので分割して備忘録として残す。これは前回(その1)の続き。

主な流れとしては以下の通り。

  • その1
    会話システムのベース作り
  • その2(今ここ)
    会話・選択肢表示用のウィジェットの作成
  • その3
    その1,その2で作ったものを使った実際の会話フローの作成+追加機能

選択肢テキスト格納用ウィジェットの作成

コンテンツドロワー内を右クリック → ユーザーインターフェース → ウィジェットブループリント → User Widgetを選択。
W_DialogueOptionsと名付ける。

POINT

これは選択肢の文字を格納する枠のウィジェットとなる。
したがって、何か思いついているデザインがある場合は必ずしもこの通りにする必要性はない。お好みで変えるといい。

これを開き、パレットタブからSize Boxを探して階層タブにドラッグ&ドロップする。
Size Boxを選択し、詳細タブのHeight Overrideの値を80に設定する。

補足

SizeBoxはSizeBoxの子オブジェクトの大きさを固定できる機能らしい。

SizeBoxの子オブジェクトとしてOverlayを追加する。
更にOverlayの子オブジェクトとしてImageを追加する。Imageの名前はImg_Backgroundと設定する。
詳細タブ最上部にあるIs Variableにチェックを入れる。

補足

この二つのオブジェクトは選択肢に引っ付ける飾りのような役割を果たす。だから別になくてもいいが、これが選択肢ですよ~と分かりやすくする効果がある。

Oberlayでは、Overlayの子オブジェクトの配置を数値で管理できるらしい。

Img_Backgroundを選択し、詳細タブのHorizontal Alignment、Vertical Alignmentをそれぞれ水平方向/垂直方向に塗りつぶしを選択する。

POINT


この右側のボタン。マウスを持っていけば内容が表示されるはず。

Color and Opacityから、選択肢の背景色をお好みで設定する。今回は暗い灰色で、少し透過させる程度(A値0.8程度)とした。


続いてパレットタブからHorizontal Boxを選び、Overlayの子オブジェクトとして追加する。
更にHorizontal Boxの子オブジェクトとして、新たにImageを追加する。

Imageを選択し、詳細タブから水平方向に左揃えと垂直方向に塗りつぶしを選択する。
Brushのドロップダウンを開き、更にImage Sizeのドロップダウンも開く。Xの値を4に設定する。
また、詳細タブ最上部にあるIs Variableにチェックが入っていたら外しておく。

HorizontalBoxを選択し、水平方向に右揃え、垂直方向に塗りつぶしを選択する。


続いてパレットタブからTextを選び、Horizontal Boxの子オブジェクトとして追加する。名前をTxt_OptionNameとする。
Textを選択し、詳細タブからIs Variableにチェックを入れる。
更に、水平方向に塗りつぶし、垂直方向に中央揃えを選択する。
Paddingドロップダウンを開き、LeftとRightにそれぞれ16の数値を入力する

こんな感じになる。

POINT

これで文字の左右にマージンができる。

画面右上Fill Screenとなっているところを、希望する画面の幅に変更する。
「テキストブロック」と書かれた文字に対して、選択肢ボタンの大きさがどれくらいになるかが視覚的にわかりやすくなる。
確認できたら、詳細タブのコンテンツTextに入力された「テキストブロック」の文字を消す。

次の作業はひとまずこのまま進める。


次にパレットタブからButtonを選び、Overlayの子オブジェクトとして追加する。
名前をBtn_Optionと付ける。
Is Variableにチェックを入れ、これも垂直方向と水平方向に塗りつぶしを選択する。
ただし、これはボタンとして視覚的に出てこられると邪魔なので(ボタンの見た目ではなく 機能だけ欲しい)、アピアランスのBackground ColorからA値を0にし、透明にする。


レイヤー関係を確認する。
ここまで出来たらコンパイルと保存を行う。


選択肢のイベントグラフ作成

イベントグラフを作成する。右上のデザイナーボタンからグラフボタンに切り替える。
デフォルトでおいてあるノードは消す。

+ボタンを押して、次の表に従い、変数を追加する。

変数名 コンテナタイプ
OwningDialogueComponent AC_Dialogue Base 単一
OptionText Text 単一
Optionindex Integer 単一
Dialogueindex Integer 単一

これら4つの変数について、詳細タブからインスタンス編集可能スポーン時に公開にチェックを入れる。

(完成後にスクショを撮ったのでちょっと順番が違うけど大丈夫)


グラフにノードを追加する。
グラフ上で右クリックしてEvent Constructを検索、追加する。
変数からTxt_OptionNameをGetで呼び出す。
ここからリンクを伸ばし、SetText(Text)を接続。
SetTextノードのIn Textピンに、変数からOptionTextをGetで呼び出して接続する。
SetTextノード左上ピンと、イベントConstructピンも接続する。

変数からBtn_Optionを選択し、詳細タブ内のOn Hoveredの+ボタンをクリックする。
On Hovered(Btn_Option)ノードが追加されたはず。
同様に、On Unhovered(Btn_Option)ノードも追加する。

変数からImg_BackgroundをGetで呼び出し、ここからリンクを伸ばしてSet Color and Opacityを接続する。
これをOn Hoveredノードと接続する。
In Color and Opacityをクリックしてマウスホバー時の選択肢の背景色をお好みで設定する。
ここでは明るいグレーを選択した。

Set Color and Opacity、Img_Backgroundをコピペして、On Unhoveredノードにも同様に繋げる。
ここでのIn Color and Opacityはデザイナー画面で作成したImg_BackGroundのColor and Opascityを選択し、Hex sRGBの値をそのまま入力する。

POINT

これでマウスホバー時には背景色が明るくなり、マウスを外すと元の色に戻る処理を作れた。

マウスホバー時と外した時とで文字色を変える処理を追加する。

On Hoveredノード、On Unhoveredノードの続きに、更にSet Color and Opacityノードを追加する。ターゲットに変数からTxt_OptionNameをGetで呼び出して接続する。
今度は直接色を指定せず、In Color and Opacityピンからリンクを伸ばし、SlateColorを作成ノードを接続する。

公式曰く スレート は完全にカスタマイズ可能でプラットフォームに依存しない UI フレームワークで、例えば Unreal Editor やインゲーム UI の独自の楽しみや効率性など、ツールやアプリケーションのユーザーインターフェースの構築にあわせた設計となっています。 とのこと

SlateColorSpecified Colorから、各々文字色をお好みで設定する。UnHover時の文字色として設定した色のHex sRGB値をコピーし、デザイナーモードのTxt_OptionNameのColor and OpascityのHex sRGBに貼り付ける。

ここではHover時は濃いグレー、UnHover時は薄いグレーとした。

最後に、変数からBtn_Optionを選択し、詳細タブ内のOn Releasedの+ボタンをクリックすることでOn Releasedノードを追加する。
変数からOwningDialogueComponentをGetで呼び出し、リンクを伸ばしてUpdate Selected Optionを接続する。これとOn Releasedノードも接続する。

変数からDialogueindexOptionindexをGetで呼び出し、それぞれUpdate Selected OptionのProgress IndexOption Indexと接続する。


最終的にこうなっていればヨシ!


メインテキスト格納用ウィジェットの作成

前回(その1)で作成したW_Dialogueを開く。
これもメインテキストが画面上でどのように見えるかを設定するものなので、要素さえ揃っていれば完全にお好みで構わない。


デザイナーモードを選択し、パレットから階層のW_Dialogueの子オブジェクトとしてCanvas Panelを追加する。
Canvas Panelの子オブジェクトとしてVertical Boxを追加する。更にVertical Boxの中にOverlay、Overlayの中にImageとVertical Boxを追加する。
Overlay内Vertical Boxの中にTextを2つ追加する。
Canvas Panel直下のVertical Boxに、更にもうひとつVertical Boxを追加する。

それぞれに名前を付ける。
上のTextにはTxt_Speaker、下のTextにはTxt_Dialogueと名付け、どちらもIs Variableにチェックを入れる。

最後に追加したCanvas Panel直下のVertical Box内のVertical BoxにはOptionsContainerと名付け、Is VariableSize to Contentにチェックを入れる。これも水平と垂直方向に塗りつぶししておく。

POINT

OptionsContainerを選択し、中央の作業場?に表示されている上下を切り替えると、メインテキストと選択肢の位置取りを切り替えることができる。
メインテキストの下に選択肢がある(例が思いつかないけど)パターンか、上に選択肢があるゼルダやポケモンのようなパターンを想像するとわかりやすいかも。

POINT

OptionsContainerをVerticalBoxに設定しているため、複数の選択肢は縦に並べられる。
もし横に並べたければ、これをHorizontal Boxに変えればいい。


Overlay内のImageに名前を付ける。
これもIs Variableにチェックを入れ、Color and Opacityで色を設定する。ここではUnhover時のオプションボタンの背景色と同じ色・透明度を設定した。
詳細タブから水平方向と垂直方向に塗りつぶしをそれぞれ選択する。

Canvas Panel直下のVertical Boxを選択して、アンカーを決める。今回は左中央とし、左側にマージン32を入れた

補足

アンカーはウィジェットの設置場所の基準点のようなもので、テキストの長さなどによって大きさが変わるウィジェットの場合でも、アンカーを基準として大きくする、といった処理が可能になる っぽい。

メインテキストの開始位置を少し上げたかったので位置Yを-256とした。


Txt_SpeakerTxt_Dialogueに選択を切り替え、それぞれのフォントの設定(太字がいいとか斜体がいいとか)等をお好みで行う。
また、Txt_Dialogueについてはある程度の文字数になったら改行してほしいのでWrap Text Atで数値を決定する。今回は512とした。

補足

文字数によって文字背景の大きさを変えない(会話枠を一定の大きさにする どうぶつの森方式)ウィジェットを作成した場合は、「Auto Wrap Text」にチェックを入れるといい。

そのほか、Txt_SpeakerとTxt_Dialogueの間をあけたい、文字開始位置を背景から少し離したい等があればPaddingの設定を行う。

POINT

基本的に好きに配置して大丈夫だが、イベントグラフ等から読み込む「Txt_Speaker」「Txt_Dialogue」「OptionsContainer」は作っておいた方がいいかも。特にOptionsContainerは選択肢の数によって必要なスペース等が変わるので、ただ入れ物を配置しただけだとメインのテキストと干渉する事態に陥る。


分かりにくいと思うけどこんな感じになった。
Txtに入力した文章は消しておこう。


メインテキストのイベントグラフ作成

まず変数からOptions ContainerをGetで呼び出す。ここからリンクを伸ばして、Clear Childrenを接続する。Clear ChildrenとUpdate Dialogue Textも接続する。これは、既に表示されている文章をこれからの文章のために消す処理である。

次にClear Childrenからリンクを伸ばし、SetText(text)を繋げる
ターゲットピンには変数からTxt_SpeakerをGetで呼び出し、接続する。
In Textピンには右クリックからget Speaker等でSpeakerを呼び出し、接続する(ピンクの変数のはず)。

補足

もし話す人の名前の後に何か記号を固定で入れたい場合(例えば「田中太郎」のあとに固定でコロン:を入れたい等)、In Textに「テキストをフォーマット」を追加しよう。
Formatに「{Speaker}」と入力すればピンが生成され、そこにSpeaker変数を接続できる。Format欄を「{Speaker}:」とすればSpeakerに入力された値の後にコロンが入るという仕組み。

次にSetText(Text)からリンクを伸ばし、もう一つSetText(Text)を接続する。今度はターゲットに変数からTxt_Dialogueを接続する。In Textピンにはget Dialogueと入力し、ピンク色のDialogueノードを接続する。


途中経過。


続いてSetText(Text)からリンクを伸ばし、For Each Loopを呼び出して繋げる。このノードのArrayピンからリンクを伸ばし、Option Textノードを接続する。
このノードのLoop Bodyピンからリンクを伸ばし、Create Widgetでウィジェットを作成ノードを接続する。ClassにW_DialogueOptionを設定する。
Option TextピンにはForEachLoopノードのArrayElementピンを、OptionindexピンにはForEachLoopノードのArray Indexピンを接続する。
Dialogueindexピンには、変数DialogueIndexをGetで呼び出して繋げる。

似たようなものがいくつかあるので注意。この画像でいう一番下の変数を選ぶ。

OwningDialogueComponentピンには、変数からドラッグ&ドロップしてGetで呼び出したOwning Dialogue Componentを接続する。
OwningPlayerピンには、Get Owning Playerノードを接続する。

続いてOptionsContainerをGetで呼び出し、ここからリンクを伸ばしてAdd Childノードを接続する。Add ChildとW_DialogueOptionウィジェットを作成ノードも繋げる。Add ChildのContentピンには、W_DialogueOptionウィジェットを作成ノードのReturnValueピンを接続する。


横長で申し訳ないがこうなっていればヨシ!必要に応じて拡大して確認してほしい。


まとめ

今回はここまで。がんばった!