【UE5】バイオ等にある3D空間上のWidgetにアクセスするギミックを実装する

# 0. 最初

こちらの記事はUnrealEngine Advent Calendar 2024 シリーズ3の2日目の記事になります

https://qiita.com/advent-calendar/2024/ue

とても長いので参考程度で読んでいただけると
助かります

# 1. 概要
今回実装するのはバイオ7に登場する
3D空間上に配置されてあるUIに入力するギミックを再現してみます

以下の動画を視聴して概要を把握してください

<iframe width="560" height="315" src="https://www.youtube.com/embed/R5S613RqcVQ?si=RC3nDjAKbqEtouUw&amp;start=30" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>

完成はコレ↓

https://x.com/je5fRC7sJff04i9/status/1859252647704035493

# 2. 実装するアセット
|　|アセット名  |概要  |実装意図|
|---|---|---|---|
|本体　　　　　　　　　　|BP_NumberPad         |||
|本体液晶　　　　　　　　|BP_NumberPadMonitor  |||
|入力キーの親クラス　　　|BP_KeycapBase  |使用するキーの親|今回は0とEnterは形が他のキーを異なるため共通クラスを実装|
|入力キー標準クラス　　　|BP_Keycap      |1~9,Cancelで使用||
|入力キー 0　　　　　　|BP_KeycapZero      |横が通常３倍の大きさキー|Widgetを実装すると解像度が乱れるため専用のクラスを用意|
|入力キー決定　　　　　|BP_KeycapEnter      |縦が通常３倍の大きさキー|Widgetを実装すると解像度が乱れるため専用のクラスを用意|
|開くドア　　　　　　　　　|BP_Door         |||
|本体を映すカメラ　　　　　|BP_LookCamera   |||

 ◯UI
| |アセット名  |概要  |実装意図 |
|---|---|---|---|
|モニターのWidget　　　　　|WBP_NumberPadMonitor  |  |
|キーキャップWidgetの親クラス      |WBP_KeycapBase   | ||
|キーキャップWidgetの標準　　　　　|WBP_Keycap  | 200*200  |
|決定用のWidget      　　　|WBP_KeycapEnter  |200*600  ||
|0番用のWidget       　　　|WBP_KeycapZero   |600*200  ||

◯素材は以下から好きなものをDLしてください
・音

　https://www.springin.org/sound-stock/category/system/

・フォント

　https://www.1001fonts.com/digital-7-font.html 

# 3. 実装準備をする
　FirstPersonでプロジェクトを作ります
　Fab UE Pluginをエンジンにインストールします
<iframe width="560" height="315" src="https://www.youtube.com/embed/ffxSGRBn0J8?si=YlP-ZfHoS337VVnf" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe>

　EditorからFabWindowを開き「Old Mine」を導入します
![スクリーンショット 2024-11-22 223620.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1918199/40de6ad6-fcf4-1b13-d858-c9fb792fa95e.png)


　結構時間がかかるのでしばしお待ちを....
　導入が完了したら以下のMapを開き実装して行きます
```
 Content/Scene_OldMine/Maps/Industrial_OldMine.umap
```
# 4. 扉を実装する
導入したアセットを使用して扉を装飾していきます
![スクリーンショット 2024-11-24 231135.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1918199/5175b89e-5386-a298-dfdd-f7701a7708e7.png)


「OnOpenDoor」を定義して、ドアの動きをつけます
![スクリーンショット 2024-11-24 231108.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1918199/60888efa-6fbd-d21b-86d5-0ecfd15dc656.png)

# 5. 本体(NumberPad)を仮組みする
「BP_NumberPad」を作り「StaticMesh」を追加、レベル上に配置します

![スクリーンショット 2024-11-26 234416.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1918199/997f7893-7f4a-4299-3ea4-23481b44c36f.png)

Scaleは(0.1,0.1,0.1)です


# 6. カメラの動きを実装する
1.「ActorCamera」を継承した「BP_LookCamera」作りレベルに配置します
2.「BP_NumberPad」からレベル場に配置したカメラを取得しておきます
3.「BoxCollision」を追加します

![スクリーンショット 2024-11-26 235449.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1918199/d35f1560-6db6-765f-ecf3-cd121f2cb21a.png)


 4.「SetCamera」という関数を定義して以下の様に繋げます
 ![スクリーンショット 2024-11-27 000149.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1918199/80c1e78e-2159-3529-c006-d4a750c60258.png)
|引数名|概要  |
|---|---|
BlendTimed      | カメラの移動時間
LookTarget      | 視線用のアクタ
ShowMouseCursor | マウスカーソルを表示するか？

 5. 当たり判定に接触したときに以下のように繋ぎます
![スクリーンショット 2024-11-27 000224.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1918199/70a30456-531f-919b-5962-38867b561be6.png)

 
# 7. キーのWidgetを作る
「WBP_KeycapBase」を作成して以下を継承します
・WBP_Keycap
・WBP_KeycapEnter
・WBP_KeycapZero

Baseで「Setup Key」と定義します
引数は「KeyLabel(キーの名前)」を定義します

![スクリーンショット 2024-11-28 234122.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1918199/8b075e5f-bc08-3182-f755-05d10b1e4cfc.png)

「GetTextBlock」のPure関数を定義し「空のText Block型」を返します
![スクリーンショット 2024-11-28 234139.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1918199/dba54071-2975-29cd-64f4-2312fbc3f98b.png)

各子どもの関数で中身を「GetTextBlock」をOverrideします
- WBP_Keycap
- WBP_KeycapEnter
- WBP_KeycapZero

![名称未設定.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1918199/3a624e68-bc69-cb6d-985a-0acfdccd7919.png)
「Setup Key」へ戻り「SetText」をKeylabelでTextを更新します

「OnClickKeyCup」というDelegateを定義、引数はありません
![名称未設定_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1918199/9269ceb4-dc30-f9e0-59e9-e522a0920570.png)

# 8. キーのBPを作る
「BP_KeycapBase」を作成して以下を継承して作成します
・BP_Keycap
・BP_KeycapEnter
・BP_KeycapZero

「BP_KeycapBase」で「Setup」と定義します
引数は「KeyLabel(キーの名前)」「ClickSE(クリック時のSE)」を定義します
「KeyLabel」「ClickSE」を変数として保持します

その後Wigetから「KeycapBase」でキャストし「Setup key」で初期化します
Wigetの参照から「BindEventOnClickKeyCup」を呼び中身を定義します
![スクリーンショット 2024-11-28 234028.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1918199/4508baf9-ed3d-0eac-0e00-848cd52aa49c.png)

バインドする関数名は「OnClickKeyCap」です
「BP_KeycapBase」でも「OnClickedBase」というDelegateを定義して
「OnClickKeyCap」は以下の用に書きます
![スクリーンショット 2024-11-29 000610.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1918199/4bb4d8bc-df42-befb-048c-520cbdbc76e8.png)


# 9. 本体にキーを配置
本体に「Child Actor Component」を12個追加し
各場所に対応したBPを配置します

![スクリーンショット 2024-11-29 001053.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1918199/b4da5e55-3d61-43cb-64c2-61e78b9b39e6.png)

配置したChild Actor Componentのセットアップをします

「Setup」と関数を作り「BeginPlay」でコールします
「Setup BPKeycap」と関数を作り中身を以下のように書きます
![スクリーンショット 2024-11-29 010724.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1918199/a7e4cf82-2aee-0b85-ea41-c9c273b8b387.png)

「Setup」内で「BP_KeycapBase」型の配列を定義してください

![スクリーンショット 2024-11-29 014516.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1918199/8678a6c3-f204-cc7b-6d5e-6235b343fce0.png)

以下のように「ChildActor」を繋げて初期化を行います
![スクリーンショット 2024-11-29 011351.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1918199/93d98f70-85c6-a999-69c6-c4c41d36723e.png)

![スクリーンショット 2024-11-29 012238.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1918199/294e20af-f1dd-d68d-c54d-dc2024400204.png)

登録が終わったキーにクリック時の関数を登録します
![スクリーンショット 2024-11-29 012253.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1918199/0f81744d-09f1-7936-66fb-a7f615fc9ab3.png)

Playをして各キーに対応したテキストが表示されていれば問題ないです
![スクリーンショット 2024-11-30 035009.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1918199/6e8cd6e7-e353-05bc-2e0b-6462904bc627.png)



# 10. 入力を取る
PlayerのBPを開き「Widget Interaction」を追加してパラメータを変更します
|変数名|値|
|---|---|
Virtual User Index   | 1
Interaction Distance | 6000
Interaction Source   | Mouse

![スクリーンショット 2024-11-30 040413.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1918199/6b36ad0a-4e14-c4c5-7edd-b4d0afcd7d6c.png)


「IA_KeycapSelect」というInputActionを追加します
「IA_KeycapSelect」と「Widget Interaction」を以下の用に配置
![名称未設定.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1918199/a08a8893-3909-c111-24c3-eefd7a9a1f36.png)

「Press Pointer key」「Release Pointer Key」を配置し
マウスの左クリックを割り当てて起きます
![スクリーンショット 2024-11-30 035604.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1918199/5526eedd-c78e-c16a-92cc-e61917057952.png)

# 11.モニターのWigetの作成
「WBP_NumberPadMonitor」を作成します
モニターには「数字入力中」「失敗」「成功」の3パターンがあります
WidgetSwitcherを配置して各ステータスのWigetを配置します

![無題の動画-‐-Clipchampで作成-_1_.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1918199/e2e0367f-63a8-3757-4760-46ccda61af37.gif)

Enumで「E_MonitorState」としてモニターの状態を定義します
|変数名 |対応ステータス|
|---    |---|
Defalut | 数字入力中
Open    | 成功
Error   | 失敗

![スクリーンショット 2024-11-30 041818.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1918199/4d605a32-0646-25ae-0880-78677968dd07.png)

「Set Status」と関数を追加して外部から変更できるようにします

![スクリーンショット 2024-11-30 042043.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1918199/abe2b15b-cafc-2ad8-4f42-c23f1c1d22e9.png)



# 12. モニターのBPを作成
「BP_NumberPadMonitor」を作成してWigetを追加します
![スクリーンショット 2024-11-30 043149.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1918199/6f77938e-09b8-fc5c-5161-705944a84292.png)


「Begine Play」から参照を保持しておきます
![スクリーンショット 2024-11-30 042619.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1918199/882c1811-8009-06e3-a6cb-6359095ba4ae.png)

・「On Update Status」関数を追加し引数に「E_MonitorState」を追加します
Wigetの参照から「Set Status」を呼びます
![スクリーンショット 2024-11-30 042856.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1918199/98c4f3bd-8aa7-fa4a-0ffb-711f93437381.png)

・「On Update Wiget」と関数を追加し「Text」を引数に追加します
　Wigetの参照から「Default Text Block」を呼び「Set Text」を呼びます
 ![スクリーンショット 2024-11-30 043434.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1918199/1fc96166-923c-3f1c-6401-25f9c77a06e2.png)


# 13. モニターの配置・表示
「BP_NumberPad」の「Child Actor Component」として追加します
![スクリーンショット 2024-11-30 044318.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1918199/367e22f7-be3e-678f-92ff-58a28f60c8d4.png)


「BP_NumberPadMonitor」の参照を保存します
「On Update Monitor Text」に空のTextを追加します
![スクリーンショット 2024-11-30 044343.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1918199/2b7fcf3e-0bc3-1f91-9a1e-3ed66e82c995.png)



# 14. 各キーの初期化とモニターへ値を反映する
「BP_NumberPad」の「OnClickedKeycap」で以下の関数をコールします

「OnClickedKeycap」は「KeyLabel」を受け取るので分岐します
分岐用の以下の関数を追加します
|関数名|概要  |
|---|---|
On Click Enter|押下時に正解かを判別します
On Click Cancel|押下時に入力された文字列を右から削除してします
On Click Number|押下時に入力された文字列の右から挿入してします

![スクリーンショット 2024-11-30 045039.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1918199/7ea2cb4a-4e4c-ca5d-07d1-79514dcac56a.png)




各ボタンの処理を書く前に以下の変数を追加しておきます

|変数名|概要  |
|---|---|
Correct word|正解キーを入力(配置後のレベルから入力を行う)
Current Input Text|現在入力されているテキストを保存

![スクリーンショット 2024-11-30 045557.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1918199/5f1a8957-44e9-cd12-9b8c-b3f56a673fcb.png)

# 15. 通常キーの押下後判定
「On Click Number」を書きます

![スクリーンショット 2024-11-30 182302.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1918199/5b12297f-8e98-3b46-1f4f-3592cde5d332.png)

「In Under Limit Length」（入力文字数が正解文字数を超えていないか）
![スクリーンショット 2024-11-30 182346.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1918199/331a515c-74f9-f273-234a-714195165a7c.png)

「Append Text Right」(入力文字を右から追加する)
![スクリーンショット 2024-11-30 182431.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1918199/a869d078-adfa-2463-13c0-b21686f09515.png)


# 16. エンターを判定

入力されている「文字列」と「正解の文字列」を比較して分岐します
![スクリーンショット 2024-11-30 050521.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1918199/b71cfff9-88d3-c1ae-9bef-31ec236a85e6.png)


・【等しいとき】

![スクリーンショット 2024-11-30 050544.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1918199/d7e4de9e-38da-2651-2585-b56f44142c06.png)

・【等しくない】
![スクリーンショット 2024-11-30 050603.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1918199/bc063e99-4928-2d81-0173-ca943e385801.png)

「Set Timer by Event」に「On Reset Monitor」と関数をBindします
![スクリーンショット 2024-11-30 050640.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1918199/8a719284-ea41-499e-7238-5d636a63d106.png)



 
# 17. キャンセルを判定
現在の入力値があるかを判定します
![スクリーンショット 2024-12-01 021317.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1918199/c2e8d989-2796-83bf-cd35-e398d5ddf71b.png)

![スクリーンショット 2024-12-01 021335.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1918199/cfdcb56a-c857-aeec-ff86-1e5fcad1a085.png)

# 18. レベル上で最終設定
「BP_NumberPadr」から必要な情報を入力してきます
![スクリーンショット 2024-12-01 021949.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1918199/6676f9b4-26a7-74cb-3ed6-3fef6990fea0.png)

# 19. 完成！
入力を間違えると「Error」と表示
入力に正解すると「Open」と表示され扉が開くようになりました！

![無題の動画-‐-Clipchampで作成5.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1918199/32a2fc86-c94b-5109-a9c9-b45510e1e193.gif)



 
# 20. 終わり
ここまで拝見していただきありがとうございます！
とても長い工程をお疲れ様でした！

これが少しでも参考になれば幸いです！！