初心者がUE4でルービックキューブ風なものを作って強化学習用のライブラリにしたい #1

# まえおき

- 最近、Pythonの勉強を進める傍ら、データエンジニアの身ではありますがUE4を少しずつ触りはじめています（初心者）。
- 本やらUdemy動画を進めてみたりしていますが、ブループリント的なビジュアルスクリプティングはほとんど触った経験が無く、不慣れなところも多く、個人的には自分で考えて手を動かしていないと忘れてしまいそうです。
- そこで、ブループリントとPythonを使って簡単な四角の面を揃えるパズル、いわゆるルービックキューブ風なものを作成して慣れていこうと思います（挫折しそうなので最初はシンプルなものから・・・）。
- 恐らくかなり長くなるので複数記事に分けていこうと思います（しばらくPython関係は出てこない気配が・・・）。
- また、作ったものはいけそうであればOpenAIのGymライブラリ的なインターフェイスのPythonライブラリにまでできたらいいなと考えています。後日別途強化学習でルービックキューブを解いたりにチャレンジしてみたいです。UE4も勉強できて、強化学習やPythonの勉強も出来て、一粒で3度美味しい（？）。
- なお、本人はルービックキューブを解けたことが無い模様・・

## なぜにUE4？

- きっかけは、Youtubeでれレコメンドされてきた、スピードデザインというか、短時間でハイクオリティのマップ（レベル）を作る動画があって、「なにこれすげえ」と感動したのが1点。
- NVidiaの研究の「[NVIDIA、AIで仮想世界のすべてを自動生成する手法](https://pc.watch.impress.co.jp/docs/news/1156514.html)」の発表内容に当時驚いて、且つどうやらUE4が使われているらしい・・・と興味を持ったのが1点。
- CEDEC2019でも、ディープラーニングとUE4を組み合わせた発表（[［CEDEC 2019］ゲーム技術が主役？ 「Unreal Engine」を活用した運転支援システム開発の実例を紹介](https://www.4gamer.net/games/999/G999905/20190907007/)）が見受けられた（実際に現地に行って聞いてきた）というのが1点

といった具合です。他にも、CEDECではシミュレーションの重要性などに触れる会社様が多かったので、機械学習方面だけでなくこれらの技術もある程度触れるようになっておくとスキルセット的に差別化できるのでは・・・と考えています（ある程度機械学習方面を触れて、データ基盤的なところも触れて、シミュレーション関係の技術も少しはある・・・といった競合しにくい器用貧乏な方面に）。

単純にGymライブラリでAtariとかで遊ぶよりも、自分で作った方が楽しそうというのもあります。

# ちゅういがき

- 後で見直したり復習するという側面が強く、うまくいかなかった点などの過程のメモなどもたくさん残していくため、ベテランのつよつよな方々からすると無駄な記述とか基本的な記述がかなりたくさん書いてある状態になると思いますが、ご了承ください。

# 使う環境

- Ubuntuのデスクトップマシン
- UE4のバージョン4.21.2
- UnrealEnginePythonのプラグイン
- Python3.6

※UbuntuへのUE4のインストールに関しては、[Linux（Ubuntu）へのＵＥ4のインストールメモ](https://qiita.com/simonritchie/items/4dc6c9331d41a8c7a388)、UnrealEnginePythonのプラグインに関しては[UE4でPythonを使ってみる](https://qiita.com/simonritchie/items/58fe6d4d3c411c8c062f)で以前記事にしています。必要に応じてそちらもご確認ください。

※以前別のPCで試していた感じ、WindowsでもGPUの利用を含めてPyTorchとUE4で問題なさそうな気配がありましたが、手元でしっかりとしたGPUを積んだWindowsマシンがノートPCしか持っていないというのもあり、デスクトップで進めるためUbuntuのマシンを使っていきます。

# できたら実現したいこと（ゆるめの要件）

※何だか難しそうであれば適宜やりやすいように調整します・・

- 3x3のルービックキューブを作ります
- マウス操作で扱えるようにします。
- 外部のPythonライブラリとしても、操作や値の取得などができるようにします（出来たらGymに近いインターフェイスに・・・）。
- 出来たら学習データの収集を高速化できるように、12個とか同時に動かせるものを目指します（参考 : CEDEC2019の[「強い」を作るだけが能じゃない！ディープラーニングで3Dアクションゲームの敵AIを作ってみた](https://cedec.cesa.or.jp/2019/session/detail/s5c8f2a755dbc1)のセッション）。
- また、並列で走らせた際に描画が重そうであれば非表示にするといったオプションも対応できたらいいな・・・と考えています。

# まずは余分なものを消す

とりあえずスターターパックを使う形でプロジェクトを作り、ExponentialHeightFogだけ残して後は余分なものを消しておきます。この状態で宙に四角が浮く形で、カメラは固定する方向を想定して進めてみます。

![20190909_2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/59c818fc-7816-ef68-9971-02c2d8084134.png)

# 四角を配置していく

Cubeオブジェクトを配置していきます。

![20190909_3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/6754f9c7-5d26-9bd6-e71b-b34cfba99f89.png)

とりあえず四角を配置しました。
これを後で(0, 0, 0)の座標を中心に四角を周囲に配置するように移動させます。

ところでこの最初から用意されている四角のオブジェクト、サイズはどこで見ればいいのか・・・フォトショとかでいうところのバウンディングボックスというか、情報ウインドウというか、そういったものを探しました。
Detailsタブには、スケールの比率などは見れるものの、サイズ（幅とか高さとか）が見れません（配置する上でサイズを知りたい）。

![20190909_4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/c79db874-18b0-4ec5-aa03-40c560580794.png)

少し悩んでいたのですが、どうやらメッシュの詳細画面で見れたようです。

メッシュのサムネイル部分をダブルクリック :

![20190909_5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/34dd57ef-ee54-9a63-7452-ef95e55f01aa.png)

すると編集画面的なウインドウが開きます。

![20190909_6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/14d91c03-d606-f14d-a642-00265ed4f384.png)

画面の左上を見てみると、メッシュに関する情報が表示されています。どうやらここの`Approx Size`の箇所を見れば良さそうな気配があります。`100x100x100`とあります。

![20190909_7.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/9fb807ef-9b1f-cafd-5c6f-870a31bd923e.png)

> 物理の世界では、距離は"m（メートル）"、時間は"s（秒）"が普通の単位。だけど、UE4での単位距離は"cm（センチメートル）"だ。UE4はここでも独自路線。で、これを"1 Unreal Unit"と言うらしい。
> [UE4の距離、単位は1cm](http://ore2wakaru.hatenablog.com/entry/2016/03/24/230000)

UE4の世界では単位は1cmのようです。つまりこれは1辺1mの四角の立方体・・・ということでしょうか。

## 座標を考える

とりあえず分かりやすいように(0, 0, 0)の位置に立方体を配置します（ただし、これは後で周りに他の立方体が配置されるため見えなくなります。あくまで位置の確認用に使います）。

![20190910_2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/750f8132-9f4d-e95b-13aa-e00fa0918531.png)

表示をPerspectiveだけでなくLeftビューやFrontビューなども表示してみます。

![20190910_3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/e6e7d256-0975-b4e4-59d3-1c4c352f2bcf.png)

赤青緑のがきっと各方向の0の値・・・ですよね。
どうやら各線が立方体の中央を通っているので、ピボットポイント的なものは立方体の中央に設定されているようです。

## 色を調べる

ルービックキューブの色の組み合わせってどうなっているんだっけ・・・ということで調べます。
困った時のWikipedia先生。

> ルービックキューブの世界標準配色は、白を手前に見ると奥が黄色、そして側面が時計回りに青・赤・緑・橙色となっている。
> [ルービックキューブ - Wikipedia](https://ja.wikipedia.org/wiki/%E3%83%AB%E3%83%BC%E3%83%93%E3%83%83%E3%82%AF%E3%82%AD%E3%83%A5%E3%83%BC%E3%83%96)

ということなので、X軸のマイナス方向を手前として（Z方向とかの方が自然なのだろうか？）、手前がオレンジ、上が緑、奥が赤、下が青、左が白、右が黄色として設定していきましょう。

## 手前のオレンジの設定

コピーした立方体にX座標に-100を設定し、手前のオレンジのための立方体を配置していきます。

![20190910_5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/3e8c8430-348b-f2ff-b774-f7879f5ba398.png)

![20190910_4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/84073227-8bd3-3a2d-09fb-2e277f941497.png)

X = -100のまま、他の座標をずらして周囲の8個の立方体も配置していきます。

![20190910_6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/23905895-d1b9-64f9-992c-9b14a60e88aa.png)

また、ごちゃっとしないように、World Outlinerのウインドウ（フォトショなどでのレイヤー的なもの）で各立方体のオブジェクトをフォルダにまとめておきます（Orangeというフォルダ名にしました）。

![20190910_7.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/1e693af7-92ce-dd13-903e-d68fdd62e3c5.png)

## 色の設定を考える

UE4で色の設定どうやるのがいいのだろう・・と考えます。
一案として浮かんできたのが、マテリアルで立方体に各色が付いたものを用意して立方体に設定する案。
しかし、3D関係のデータの準備はいまいち分かっていませんし、ツールも入っていません（UE4からもできたりするのでしょうか・・？）。
立方体を特定の1色を設定したりはできそうですが、各面を別の色にしたりがよく分かりません。

そこで、UE4から平面（Plane）のオブジェクトを設置して、そちらに単色を設定し、立方体に重なる形で設定する形で進めてみます。

### マテリアルの準備

プロジェクトのContentフォルダの下にMaterialsフォルダを作成しました。
また、そちらのフォルダにM_OrangePlaneという名前でマテリアルのファイルを新規作成しました。

![20190910_8.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/85660176-e044-cafe-4a51-f5a98984370b.png)

追加したマテリアルをダブルクリックして詳細を開きます。
Constant3Vectorノードを追加して、Base Colorに繋げてオレンジ色にします。
また、Roughnessに特にあまり意味はないのですが今回は定数で1を設定しました。

![20190910_10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/7b7c770d-be22-8fdc-cdef-bafe38344096.png)

色は一旦適当に以下のようにしました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/30b9c28f-7a1a-f377-dbbd-9a3d396d8d74.png)

平面にこのマテリアルを設定していきます。

![20190910_12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/03e11d9c-7722-20d9-8655-23884c1af232.png)

![20190910_13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/738b482d-f834-3152-b757-a2a5ecab6fff.png)

？？配置してみましたが何やら綺麗なオレンジではなく、黒に近い色になってしまいました。
何故だろう・・・と考えていたところ、そういえば最初にほぼほぼのものを削除しているので照明的なものがないようです。ライト関係を配置します。

DirectionalLightを適当な位置・光量で設定します。詳しくないので細かい値は後で調整します。

![20190910_14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/ec46ab5c-58df-3b74-5d97-a89b64ce057d.png)

とりあえずマテリアルで設定したようにオレンジの色が平面で確認できるようになりました。

![20190910_15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/b7c51f66-59a2-63fd-88c8-6d4e27943f87.png)

平面の座標を調整していきます。

X軸で-100の座標に加えて、四角の立方体の幅の半分の値を加味し-150にし、それよりもわずかに多く前面に表示するので-151のX座標の位置に配置します。

![20190910_16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/a71859c8-9f1e-eb01-74b3-fd9a3c3ba8ae.png)

ちゃんと真ん中の位置にオレンジの平面が設定されました。
扱いやすいようにWorld Outlinerで立方体と平面を同じフォルダに配置しておきます。
が、よく考えたらオレンジなどでフォルダを切っていると、上の位置のものなどはオレンジ以外の面も必要になるのでフォルダの切り方が不適切に感じてきました。
どういう面の組み合わせが必要なのか先に整理しようと思います。

# 面の組み合わせの整理と名前付け

フォルダ名とかのために名前を整理します。

- Orange
- OrangeWhite
- OrangeWhiteGreen
- OrangeWhiteBlue
- OrangeGreen
- OrangeBlue
- OrangeYellow
- OrangeYellowGreen
- OrangeYellowBlue
- White
- WhiteRed
- WhiteRedGreen
- WhiteRedBlue
- WhiteGreen
- WhiteBlue
- Yellow
- YellowRed
- YellowRedGreen
- YellowRedBlue
- YellowGreen
- YellowBlue
- Green
- GreenRed
- Red
- RedBlue
- Blue

整理するとこのパターンの組み合わせの立方体が必要なようです。一応各色で検索すると9個ずつヒットするので合っているはず・・

# 整理したフォルダ名を使ってフォルダ名を調整していく

前述の組み合わせによる名称の整理などを加味し、以下のような感じにOutlinerの構成を調整しました。

- 立方体関係のフォルダをCubeListに変更
- その下に組み合わせに応じたフォルダを設置
- その中に立方体とオレンジの平面を設置

![20190911_2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/1e2aa820-dffc-4fc7-64f2-ef2ddd7b4961.png)

平面の座標も調整して、以下のようになっています。

![20190911_3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/be187b28-6810-8720-254d-28f88be17656.png)

グループ設定のような機能もあるようですが、他の色のオブジェクトの配置が終わって必要そうだったら試してみます。
（オブジェクトというかアクターと表記した方がいいのだろうか・・？）

# 白の面の配置

オレンジの左側に回り込んで、白い立方体を配置していきます。
元々Cubeのアクターにで白いマテリアルが設定されているので、平面設定は省いて対応します。

![20190911_4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/491340dd-0dc8-54a1-b8b8-c4bdfbfd36b7.png)

若干色がグレー寄りなのは、光がオレンジ側に設定してあるので、影気味になっているからです。

![20190911_5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/d068cb7b-5a70-97a4-2e43-ff574a56bc28.png)

# 黄色の面の配置

同様にどんどん配置していきます。次はオレンジの右側に回り込んで黄色を配置していきます。
M_YellowPlaneという名前でマテリアルを追加しておきます。

![20190911_6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/5a92b9a1-4cf3-bfad-a910-f06010dab1c9.png)

同様に配置します。

![20190911_7.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/3cadb61c-a833-f836-897c-992aa587e598.png)

# 緑の面の配置

上に緑の面を配置していきます。M_GreenPlaneという名前のマテリアルを用意して、同様の作業を繰り返します。

![20190911_8.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/d66c0fb9-2d7f-9f81-a82e-2218f735817c.png)

![20190912_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/bb0c4e0d-af35-5a19-efd4-df44c48b122b.png)

ちょっとそれっぽくなってきました。

# 赤色の面の配置

奥面に赤色の面を配置していきます。ライトの反対側になる都合、色が大分暗く見えるので、もし回転させて見て色に違和感があるようなら後で色は調整します。
M_RedPlaneという名前でマテリアルを追加します。

![20190912_2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/e05bd98c-b7c2-edd7-8259-59e765403979.png)

かなり黒に近いですね・・。

![20190912_3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/f170c12e-3e02-69df-ca99-a2a656ec2de0.png)

# 青色の面の配置

最後の面です。M_BluePlaneという名前でマテリアルを用意します。
この面も暗くなります。というか真っ黒でよく分かりません。

![20190912_4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/c8186277-2493-8d6b-8619-3e2ebd0dfc75.png)

一時的に下から上へのライトを追加して作業します。

![20190912_5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/f20ce7b2-8604-f4d9-a52f-884cae2b53e9.png)

デフォルトのものをそのまま使う白以外で、最終的にマテリアルは以下のようなリストになりました。

![20190912_6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/717bbcc7-ef9f-4afd-116f-4979c99102b1.png)

![20190912_7.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/56217cd2-b5c5-81cd-8720-d5176fefe0c9.png)

そして、ライトを複数配置していて、色が見やすいし別に複数のライトがあってもいいか・・・という印象がしてきたため、そのまま配置しておきます。
むしろ、背面の方にもDirectionalLightを追加しておきます。

これで色の設定は完了です。最後にOutliner内のアクターの名称の調整などをしておきます。
以下のように、大分Outlinerの中身が大所帯な感じになってきました。

![20190912_8.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/482ed064-3063-f987-ac5c-1e4e1ceb2f04.png)

# グループ化しておく

立方体や平面を、それぞれでグループ化しておきます。グループ化することで、平面などを選択した際に一緒に設定されているものが同時に選択され、移動や回転を位置関係などを崩さないまま設定できます。

右クリックしてコンテキストメニューからグループ化するか、もしくはCtrl + Gでグループ化ができます。
設定後、GroupActor○という名称でOutlinerにグループ関係のものが追加されます。

![20190912_9.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/be17ffda-4347-7495-cb04-2615f6ad2630.png)

# 回転を考える

ルービックキューブ的に、回転時に軸となる位置（ピボットポイント的なもの）を中央の(0, 0, 0)にする必要があります。

DetailsウインドウにPivot Offsetという設定があり、なんだかそれらしさを醸し出しています。

![20190912_10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/4bfdbb90-85ac-549d-a799-e9cb5aafca7e.png)

が、これの値を変えてみてもなんだか回転などには変化が無さそうです。
他にもマウスホイールでクリックすると、一時的にピボットポイントを変えれそうですが、これは一時的な設定ですぐに戻ってしまいます。これだとブループリントとかでの制御を考えると多分使えない気配があります。

追加でググってみました（最終的にブループリントで制御するのを加味してブループリントで）。

> You cant change the pivot but you can attach it to a scene component(or an arrow component) and then set the offset of your asset and use that scene component's position as pivot. 
> [How do I change the pivot position on a component in a blueprint?](https://answers.unrealengine.com/questions/75405/how-do-i-change-the-pivot-position-on-a-component.html)

ふむ・・なるほど、基本的には変えられず、ただ余分なアクターを追加してグループ化などすればいける・・といった感じでしょうか？

少し試してみましょう。

まず、ルービックキューブの近くに立方体を適当な位置に配置します。

![20190912_11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/509ef9a5-7565-80a8-13f4-57b1759b3e56.png)

続いて、その立方体と真ん中のオレンジのものをグループ化してみます。

![20190913_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/89b9375c-388b-c722-0d91-85010bb37e99.png)

その状態でアクターをクリックして、回転ウィジェットを表示すると、確かに回転の基準点がずれてくれます。

![20190913_2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/1c62546d-872c-cd07-cc5c-b288370d926f.png)

試しに少し回転させてみましたが、想定したよう回転になってくれそうな気配があります。

![20190913_3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/a72a3ba9-8ac4-e6aa-9e1b-a753de87a6ce.png)

そのため、ブループリントで回転制御をする場合、

- その立方体の反対側の位置に透明を四角の立方体を追加する。
- それを非表示（Visible=False）にする。
- グループ化する
- 回転させる
- 回転が終わったらグループ化を解除して追加した立方体を削除する

みたいな制御でいけそうな気配がします。
この辺り、ブループリントもよく分かっていないので試していきます。

# ブループリントのための準備

そもそも、ブループリントを使っていく上で、画面に配置したアクターの参照をどうやって取るのだろう・・・というところからスタートします。

# ブループリントで回転制御を試す

先に荒削り気味ですが、前述の内容で問題ないか、ブループリントで試してみます。
軽くドキュメントなどを眺めていたところ、若干手間という印象を受けました。そもそも最初からブループリントクラスで用意してから、そこにアクターを配置すべきだった感があります（不慣れで大分遠回りしている感）。

一旦プロジェクト直下にBluePrintsフォルダを設置して、そちらにブループリントクラスを追加していきます。

レベルに配置されているアクターをブループリントクラス内にカット＆ペーストで配置できるか・・？と試してみましたが、どうやらできなさそうな気配があります（もっとよく調べればできる感も・・・）。
やらかした感がありますね・・・・（作業やり直しの気配・・）

## ブループリントの基底クラスの用意

色の組み合わせに応じた各ブループリントクラスを用意していく形になると思いますが、回転制御などはある程度使いまわしできそうな気がするので、先に基底クラスを追加しておきます。BP_CubeBaseという名前にしました。

この基底クラスには、Cubeアクターと各色の平面を配置しておきます。
一通りの色は配置しておいて、サブクラス側で必要な色の平面だけ表示する、という形で進めてみます。

![20190913_5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/3708c40c-2e5e-627f-823e-30a4fcc73940.png)

基底クラスのブループリントのViewPortに配置していきます。

![20190913_6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/1e5f56b7-46f6-03bb-b360-19a8b83830eb.png)

Componentsは以下のようになっています。

![20190913_7.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/0a04aed1-2549-bc41-a0de-8abc4f7896f5.png)

## 基底クラスを継承した立方体のブループリントの準備と差し替え

試しにこのクラスを継承するサブクラスも設けてみます。BP_Orangeという名称で、オレンジ面の中央のアクターを想定して作ります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/cf9cd21f-d032-5b0a-defc-107fa8a7a1eb.png)

問題なく基底クラスの方の設定を引き継げているようです。このクラスではオレンジ面だけ表示するように、各面のVisibleの値のチェックを外しておきます（白い面に）。

![20190913_8.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/79a5e549-0b0e-bfcc-673f-f8f021657087.png)

他の色の組み合わせも用意していきます。ひたすら同じような作業な感じではあるので、この記事では詳細はスキップします。

![20190917_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/f8d89275-3861-4576-440d-3f370adfd9b9.png)

表示する面だけ切り替えた、同じ基底クラスを持つ各ブループリントクラスが用意できました。

既存のアクターをこれらのブループリントクラスで差し替えていきます。
基準点は一緒なので、既に配置されているCubeアクターと同じ座標に設置することでぴったりと同じ位置に配置できます。

![20190917_2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/40e7f080-b1cd-3c3c-0967-ca057cd6c28f.png)

![20190917_3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/34ff0526-1794-be7b-5b3e-ec22b266a6e1.png)

フォルダやグループを挟む必要が無くなったのでOutlinerは大分すっきりしました。
また、以前と変わらない立方体の配置を再現できました。

## 位置関係の定数と変数を用意しておく

回転関係を実装していくに伴い、対象の立方体がどこに位置しているのかの値が必要になります。
それらの変数と定数を先に基底クラスの方に定義しておきましょう。

実際の座標方向とは異なるのですが、自分で分かりやすいようにオレンジの面を手前として、水平方向をCUBE_X_1～CUBE_X_3（左から）、垂直方向をCUBE_Y_1～CUBE_Y_3（上から）、奥行き方向をCUBE_Z_1～CUBE_Z_3（手前から）として表現します。

また、現在の位置の変数を以下のような名前でBP_CubeBaseに持たせます。

- currentCubeX
- currentCubeY
- currentCubeZ

変数の方は以下のように値を設定しておきました。
Slider RangeとValue Rangeという箇所の設定で、何らかのミスなどで想定していない値が設定されないように範囲を指定できる？ようです。
設定しておいて特に損はないでしょう・・ということで設定しておきます。
デフォルト値はサブクラス次第で様々なので、0のままにしておきます。

![20190918_1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/68f1fd24-0f0f-7e9b-1fd2-3623fa255976.png)

各定数は以下のように設定しておきました。
厳密にはデフォルトではブループリントでは定数は扱えない？っぽい気配があるため、実際には定数ではないのですが、値の範囲設定とデフォルト値とアッパーケースでの識別で定数的に使っていきます。

![20190918_2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/87f6a4ce-8e2a-8d28-8f62-7e47bcf20068.png)

ついでに値をまとめて更新するための関数も追加しておきます。ノードを分けるにはSequenceノードを使うようです（Out部分で繋げていくよりも見やすくなりそうな）。

![20190918_3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/db1c1969-2907-f8dd-3d6d-5cb0aeacacf8.png)

各サブクラスのための初期位置も変数に設定しておきます（リセット時などに使うことになりそうなきで）。

initialCubeX～Zという名前を付けました。
また、こちらも値設定用の関数を用意しておきます。

![20190918_4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/11a8001b-48f1-5136-6603-a49ae0865079.png)

各サブクラスに、ゲーム開始時のイベントで位置情報の初期値が設定されるようにしておきます。

以下は底面の真ん中の立方体のBP_Blueの例です（継承して知りましたが、自動でsuperメソッドが呼ばれるようなブループリントの形になっていて、親切で分かりやすいですね・・）。

![20190918_5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/2b8aae9f-11ba-594e-e028-02e895c65798.png)

サブクラス側で設定した初期値を、基底クラスの方のBeginPlayイベントで実際の位置情報に設定されるようにしておきます。

![20190918_6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/e55aeeb8-e6cd-206d-bd20-925971bfaa71.png)

他の各サブクラスも同様に設定します（作業なので割愛）。

## レベルのブループリントで、各ブループリントへの参照を得る

画面に配置してある各ブループリントを、レベル（マップ）のブループリントでの参照の仕方が最初分かりませんでした。
ただ、たまに右クリック時に「Create a Reference to BP_Orange」といったような表記が出てきていて、なんのトリガーで出ているのだろう・・・とか考えていました（そちらで用意した参照なら、画面に配置している各ブループリントの値などを参照できる）。

調べてみたところ、どうやら画面で現在選択しているアクターが対象になっているようです。

> Those are references to objects in your level. If you select an Actor in the Editor Viewport or the Scene Outliner then when you right click in the Level Blueprint graph you will see the option to add a reference to that Actor or add events for that Actor. 
> ["from Persisten Level" blueprint node](https://answers.unrealengine.com/questions/32620/view.html)

扱いやすいように、各ブループリントへの参照を変数化しておきます。

![20190918_8.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/e0535af3-3c5c-db29-ca41-784564deb9d0.png)

### ブループリントの配列の準備

各立方体のブループリントをループで回していったりするために、ブループリントの配列の準備をしていきます。
ブループリント上で配列はどう配列にするのだろう・・・？と、変数の型のところでArrayと検索して眺めていましたが、適したものが見つかりません。
（最初、Array of Actorでいいのか？と試していましたがうまくいかず）

![20190918_9.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/21ca5a6b-b2d5-ed3f-f529-d59c37537b17.png)

どうやら、ドキュメントを見ると、まず対象の格納したいデータの型を選んで、その後隣にあるアイコンをクリックすることで切り替えできる模様です。

> 配列は、変数の作成時に配列グリッドをクリックするだけで作成できます。 
> [ブループリントの配列](https://docs.unrealengine.com/ja/Engine/Blueprints/UserGuide/Arrays/index.html)

無事各ブループリントを格納する配列を作れたので、デフォルト値の中に各ブループリントのアクターを追加していきます。

![20190918_10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/a583d492-1cc2-8b8d-f3a0-dde6494f48f7.png)

## 位置情報のバリデーションを追加する

位置の値の初期設定をしたのはいいものの、手作業でやったので数が多くミスしていそうな気配があります。また、回転させる段階になってもミスしそうな気がします。

念のため、各立方体の位置情報が他と被っていないことを確認するための関数を用意しておきます。

立方体の配列を2重ループで回して、重複している位置の値の立方体が無いことをブループリントで作ります。ForEachLoopというループ用のノードが用意されているようなので、そちらに用意した立方体の配列を指定してループを回していきます。

![20190918_11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/62968e78-1b86-ee39-9cc8-061e7e3f0872.png)

比較の左辺と右辺で同じアクターだった場合に処理しないように、if文に該当するBranchノードを設けて、False（別のアクター同士の比較）の場合のみ先に進むようにしておきます。

![20190918_12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/fb44df4d-1351-9eb8-05cd-6584fff427b6.png)

続いて、対象の立方体のブループリント内のXYZの値を取って、ベクトルに入れて、値が一致していないことをチェックします。

![20190918_13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/f09e2239-0d53-ea5d-3f07-b05428ea3c06.png)

なお、作業していて知りましたが、ノード間のリンクの途中に追加する●部分（ダブルクリックで追加できるもの）、見やすくするために使っていましたが、それだけではなく複数のノードに分けたりにも使えるようです。便利。

ベクトルが一致していたら（True）気づけるように、赤いログ表示をするようにしておきました。
例外投げていい気もしますが、ブループリントでの例外の投げ方が良く分かりませんでした。

![20190918_15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/008392cd-e394-7233-06bc-20b8aa70ec9f.png)

ログに表示する文字列は、比較の左辺と右辺の立方体のブループリントの配列のインデックスを表示してくれるようにしておきました。
文字列連結はJoin String Arrayノードでいけるようです。

![20190918_16.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/1e2f0359-7981-1667-f134-18ab698f6d4a.png)

※追記 : ログ表示で改行とかは効かないようなので後でスペースに差し替えました。

俯瞰してみるとこんな感じに。

![20190918_17.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/da421e93-7757-128f-369b-ae3283f80fca.png)

このループとかも含めてコードが可視化される感じ、何だか不思議な感覚です（普通のコードしか書いたことがなく、ビジュアルスクリプティングがブループリントがはじめてなのもあり・・）。

実際にゲーム開始直後に呼び出してみます。各ブループリントでの初期化が先に流れるように、一応遅延用のDelayノードを挟んでおきます。

![20190918_18.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/f07d9dd3-1fd9-c9de-eabf-d0a6c57fe317.png)

![20190918_19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/c2324d58-1841-5bfe-2f92-01513bf87419.png)

3と6のインデックスのブループリントの位置の値が重複しているようです。やはりやらかしていましたので修正します。

修正後、表示が消えているのが確認できました。

![20190918_20.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/228778/6dcb0acb-df78-b0f1-3e8a-262e6c20cf91.png)

大分中途半端で、且つろくにものができていませんが、結構長くなったので一旦本記事はこのくらいに留めておこうと思います。
（きっと）次回記事に続きます。

# 参考文献

- [作って覚えるアンリアルエンジン【Unreal Engine 4】～ダンジョンゲーム編～ ](https://www.udemy.com/unreal-engine-4-award/)
    - ※まだ私も途中（序盤）ですが、とても分かりやすいのでオススメです・・・！
- [「強い」を作るだけが能じゃない！ディープラーニングで3Dアクションゲームの敵AIを作ってみた](https://cedec.cesa.or.jp/2019/session/detail/s5c8f2a755dbc1)
- [UE4の距離、単位は1cm](http://ore2wakaru.hatenablog.com/entry/2016/03/24/230000)
- [ルービックキューブ - Wikipedia](https://ja.wikipedia.org/wiki/%E3%83%AB%E3%83%BC%E3%83%93%E3%83%83%E3%82%AF%E3%82%AD%E3%83%A5%E3%83%BC%E3%83%96)
- [How do I change the pivot position on a component in a blueprint?](https://answers.unrealengine.com/questions/75405/how-do-i-change-the-pivot-position-on-a-component.html)
- ["from Persisten Level" blueprint node](https://answers.unrealengine.com/questions/32620/view.html)
- [ブループリントの配列](https://docs.unrealengine.com/ja/Engine/Blueprints/UserGuide/Arrays/index.html)

# 後で調べるためのメモ

- UE4の自動保存が走るとアクティブなウインドウがUE4になってしまい、他の作業をしていると気になりました。無効化する設定などが無いか調べます。
- ブループリントって、テストコードどう書くのがいいのでしょう。ベストプラクティス的なところがよく分かっていません。
- ブループリントでの例外の投げ方がよく分かっていません（投げられない？）。
- Ubuntu版だけかもしれませんが、コメントなどに日本語のテキストが入力できていません。設定で変なところがあるのでしょうか・・？（Windows版だとUIは英語表示でも日本語入力できたような・・。）少し不便ですね・・・