【UE4】会話システム内での選択肢を作成してみる

# 最初に

どうも、ろっさむです。

今回は前回の記事に引き続き、会話システム内での選択肢の簡易実装を行っていきます。

↓↓↓↓前回の記事は以下からご確認ください↓↓↓↓

[【UE4】NPCとの会話システム実装](https://qiita.com/4_mio_11/items/fed86efca7407a7975fb)

## 開発環境

- UE4.26
- Windows 10

# 作業手順

今回行う作業は以下の通りです。

1. 会話用ダイアログ内での選択肢用UI作成
2. 会話ダイアログ用のウィジェット作成時にフラグ操作
3. 選択肢表示の実装
4. 選択肢の選択処理実装

それでは順番に実装していきましょう。

## 会話用ダイアログ内での選択肢用UI作成

前回作成した`WBP_TextDialog`を編集していきます。
![image](https://user-images.githubusercontent.com/10665134/102355747-a2714d00-3fef-11eb-98d1-5e66e24ad8bc.png)

ダブルクリックして開いてください。

開いたら、まずは選択肢用のウィンドウを作成していきます。新たに、選択肢用のCanvasを作成してください。これはテキストウィンドウ用のCanvasの子供にします。
![image](https://user-images.githubusercontent.com/10665134/102355899-d6e50900-3fef-11eb-815e-b372d51b9dc7.png)

Canvas名を`SelectableWindow`とします。
![image](https://user-images.githubusercontent.com/10665134/102355983-f7ad5e80-3fef-11eb-8c4d-e333d957a322.png)

`Anchors`は左上基準で固定します。
![image](https://user-images.githubusercontent.com/10665134/102356061-16135a00-3ff0-11eb-8fce-1fbea8551c70.png)

次に、選択肢ウィンドウの背景を作成します。

`image`を`SelectableWindow`の子供として追加してください。
![image](https://user-images.githubusercontent.com/10665134/102356156-33e0bf00-3ff0-11eb-9c69-2655c8938697.png)

`Anchors`は左基準で固定します。
![image](https://user-images.githubusercontent.com/10665134/102356235-4d820680-3ff0-11eb-92ac-6e0372e543b3.png)

`ZOrder`は文字よりも後ろにいくように-1に設定し、アルファを0.7としておきます。
![image](https://user-images.githubusercontent.com/10665134/102356279-5c68b900-3ff0-11eb-84f0-23a3eef42d17.png)

ここまでで、ざっくり以下のような感じになっているかと思います。
![image](https://user-images.githubusercontent.com/10665134/102356491-9e91fa80-3ff0-11eb-8adf-2831a68acc17.png)

--------------

次に選択肢の中身のUIを作成します。

まずはYES用のCanvasを作成していきましょう。このCanvasは`SelectableWindow`の子供として追加してください。更にその下には先ほどと同じような`Image`と、選択肢テキスト尾を追加します。
![image](https://user-images.githubusercontent.com/10665134/102356733-efa1ee80-3ff0-11eb-9dae-8d4ee1ee1474.png)

三つの要素全てで`Anchors`を左基準で固定します。
![image](https://user-images.githubusercontent.com/10665134/102356235-4d820680-3ff0-11eb-92ac-6e0372e543b3.png)

テキストの`Content > Text`からYES用のテキストを入力しましょう。
![image](https://user-images.githubusercontent.com/10665134/102357006-3d1e5b80-3ff1-11eb-87a8-d252d7579e51.png)

imageは以下のように`ZOrder`とアルファ値を変更するのを忘れないでください。
![image](https://user-images.githubusercontent.com/10665134/102357177-69d27300-3ff1-11eb-9a9d-b23970714643.png)

私の方で作成を行った感じ、ざっくり以下のような見た目になりました。
![image](https://user-images.githubusercontent.com/10665134/102357221-748d0800-3ff1-11eb-8c4e-7525ce6261a7.png)

最後に、NO用のものを用意する必要がありますが、こちらはYES用のCanvasを複製することでさくっと作ることが可能です。
![image](https://user-images.githubusercontent.com/10665134/102357432-a7370080-3ff1-11eb-8936-ec01c9692085.png)

テキストの`Content > Text`からNO用のテキストを入力して、位置を整えればUIデザイン側での作業はOKです。
![image](https://user-images.githubusercontent.com/10665134/102357550-c6ce2900-3ff1-11eb-8e17-3065eda96afb.png)

-------------

次に、選択肢の表示切替え部分を作成していきます。

まずは、選択肢が表示される会話ダイアログ以外の場合では、選択肢を非表示にしておく必要があります。また、選択肢内で現在選択されていないテキストは黄色背景をオフにして目立たせないようにする必要もあります。今回の場合だと、以下の三つが対象となります。
![image](https://user-images.githubusercontent.com/10665134/102358014-57a50480-3ff2-11eb-90fb-619515366342.png)

これらをデフォルトで非表示にするための設定をしましょう。

上記三つの要素をそれぞれ`Behavior > Visibility`から`Hidden`にして非表示にしておき、`Bind`ボタンを押して`CreateBinding`を選択します。
![image](https://user-images.githubusercontent.com/10665134/102358149-8622df80-3ff2-11eb-931a-5167ea0022c1.png)

作成した関数の処理を作成する前に、必要となる変数を二つ用意しましょう。

`bool`型の`IsUpper`と`IsSelectMode`という変数を作成してください。
![image](https://user-images.githubusercontent.com/10665134/102358280-af437000-3ff2-11eb-9701-16efc5f29168.png)

また作成した関数の方も名前を変更しておきます。

- `SelectableWindow`にBindしている関数：`SetActiveSelectMode`
- YESのimageにBindしている関数：`SetActiveUpper`
- NOのimageにBindしている関数：`SetActiveRow`
![image](https://user-images.githubusercontent.com/10665134/102358452-dc901e00-3ff2-11eb-99f0-d43518c40bbb.png)

それぞれの関数の中身を作成していきます。

まずは`SetActiveSelectMode`は`IsSelectMode`がtrueの時に表示するようにします。
![image](https://user-images.githubusercontent.com/10665134/102358798-490b1d00-3ff3-11eb-947a-922422ad0776.png)

次に`SetActiveUpper`では`IsUpper`がtrueの時に表示するようにします。
![image](https://user-images.githubusercontent.com/10665134/102358888-67711880-3ff3-11eb-89ac-6646861c8962.png)

最後に`SetActiveRow`では`IsUpper`がfalseの時に表示するようにします。
![image](https://user-images.githubusercontent.com/10665134/102358963-87084100-3ff3-11eb-80e8-c07d55fb99d4.png)

これでWidget側での土台作成は完了です！

----------------------

## 会話ダイアログ用のウィジェット作成時にフラグ操作

前回の記事で、会話ダイアログの作成を行う初期化処理はEvent BeginPlayから開始させていました。ここに、選択肢用のフラグ操作も入れておきます。`IsSelectMode`はデフォルトで`false`となっているので、選択肢が必要になったWidgetにだけ`true`と設定する必要があります。今回は会話の最後に選択肢用のウィンドウを表示するようにします。

Widget追加処理内に以下の処理を追加しましょう。

1. `Dialog`リスト内から最後の要素をGETし、`ForEachLoop`での要素と合致してるか確認。
2. 合致している場合は、`IsSelectMode`を`true`にする。
![image](https://user-images.githubusercontent.com/10665134/102359484-347b5480-3ff4-11eb-8f87-45cbcb6df0ad.png)

これで初期化処理側での作業はOKです。

---------------------

## 選択肢処理の実装

こちらの実装は、前回の「話しかける処理の実装」のTrace処理から続く、「0番目の要素が既に表示されているかのチェックから、表示されていれば、その要素をTextDialogListから取り除きます。」と紹介している箇所からスタートします。

TextDialogListから要素を取り除く前に、現在のWidgetが次に選択肢用のウィンドウを表示させたいのかどうか`IsSelectMode`でチェックします。
![image](https://user-images.githubusercontent.com/10665134/102360915-f7b05d00-3ff5-11eb-9321-8e5e6247d914.png)

次に`true`だった場合のYESとNO用の処理を作成します。今回はNOを選んだ時に会話用ダイアログが追加され、会話のループフラグがオフとなり、戦闘へと進みます。

以下の処理を追加しましょう。

1. YESを選んだ場合、つまり`IsUpper`が`true`だった場合には会話は終了します。
2. NOを選んだ場合には、追加Widgetを作成して`TextDialogList`に要素を追加し、テキストを設定します。また、会話ループフラグである`IsLoop`は`false`に設定します。
![image](https://user-images.githubusercontent.com/10665134/102361212-51b12280-3ff6-11eb-9430-f0f4ff72c20a.png)

会話終了時の処理は前回の記事の最後と同じです。
![image](https://user-images.githubusercontent.com/10665134/102361671-d3a14b80-3ff6-11eb-8687-b16eb9a91d06.png)

全体を通すと以下のようなグラフになります。
![image](https://user-images.githubusercontent.com/10665134/102361820-fdf30900-3ff6-11eb-8d82-ef898c5c2a3c.png)

------------------

## 選択肢の選択処理実装

最後に選択肢を選択する処理を入れていきます。

今回使用するキーは、`Up`と`Down`です。以下のように処理を入れていきます。

1. `Up`と`Down`のいずれかが押下された場合に、現在の会話ダイアログ用Widgetの`IsSelectMode`が`true`だった場合は`IsUpper`を反転してSetし直す。
![image](https://user-images.githubusercontent.com/10665134/102362214-7659ca00-3ff7-11eb-8cdf-1cca8ad6a4e0.png)

-----------------

# 完成

これで一通りの実装は終わりました。

私の環境で実行した場合は以下のような動きとなります。

![dialog-select](https://user-images.githubusercontent.com/10665134/102363282-9e95f880-3ff8-11eb-8934-000cc2324bb5.gif)

# 参考

- [アンリアルエンジンで学ぶゲーム系UI・HUDデザイン - UnrealEngine4 UMG/Widget入門‐](https://www.udemy.com/course/uihud-unrealengine4-umgwidget/)