スマホ向けゲームのWidgetについて

この記事は[Unreal Engine (UE) Advent Calendar 2022 カレンダーその3](https://qiita.com/advent-calendar/2022/ue)の7日目の記事です。
Qiita初投稿になります。何か間違いなどあればご指摘いただけると大変ありがたいです。

スマホ向けのゲームを作る際にWidgetで困ったこと、という題ですが、要は縦横比（アスペクト比）やウィンドウサイズが変わる環境でのWidget入門です。
最初にDPI Scaleについての説明、次に簡単な作成の紹介をします。
# いろいろなウィンドウサイズの確認方法について
* 右上のスクリーンサイズから変えたり、右下の矢印を動かして直接変更したりできます。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3003468/a8e3964a-cf88-9d31-af80-a977ca820574.gif" width="900" />

# DPI Scaleについて
* Unreal Engineでは、スクリーンサイズが変わったときに、自動的にWidgetをスケーリングする仕組みが入っています。
    * 上のgif動画を参照
* ある一定の横幅からスケールが変わらなくなりました。(こんなスクリーンのデバイスはないと思いますが…)
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3003468/8500e542-16f7-aeb9-4679-52d59f1a8931.png" width="600"/><br>
    * 右下を見ると、DPIスケールが0.44と書かれています。
<br><br>
* 右下の歯車マークからDPIScaleの設定を確認してみます。
    <img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3003468/fae8f890-b769-e233-aa90-0400cc07ed9f.png" width="600"/>
    * デフォルトの設定では、短い辺を基準として、480の時に0.44になり、それ以上短い辺になってもDPIスケールは小さくならないようです。
<br><br>
* 試しに、もっと小さくなるようにしてみます。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3003468/b6bc553c-195f-a0b4-ea10-0d816db96add.png" width="300" /><img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3003468/28590dc8-b849-11c1-9158-c228efb9c95e.png" width="300" />
    * 小さくなりました。
* このように、スクリーンサイズに応じて自動的にWidgetをスケーリングする仕組みが入っています。
    * なので、単純に画面が大きくなったり小さくなったりするようなケースではそのまま動くと思います。（荒くなったりつぶれたりすることはある）
    * 基準となる画面サイズを決めて、それをスケール1にすると良さそうです。(https://docs.unrealengine.com/4.27/ja/InteractiveExperiences/UMG/HowTo/ScalingUI/)
* が、あくまでも均等にスケーリングするだけなので、スクリーン全体のアスペクト比が変わったときに見切れてしまったりします。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3003468/5b86e602-137f-426d-8a15-9798596b17eb.png" width="200" /><img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3003468/40606e37-0de3-d470-3aa7-8db00e4f897b.png" width="200" />

# アスペクト比の変更に対応する作例
こんなのが作りたいとします
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3003468/fcac39f0-0e38-7d7d-f228-4f23ad66f4c8.png)
## 何も考えずに作る
* Canvasを使って作ってみましょう。
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3003468/8b0eb0be-fe84-fe2c-070e-1b160567c65a.png)
* 試しに画面を横にすると飛び出してしまいました
    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3003468/ff78f21a-13e2-0367-a619-cd08c7d95b1a.png)
## アンカーを設定する
* Canvasパネルでは、アンカーを設定することでウィンドウサイズやアスペクト比の変更に対応できます。
    * 例えば、一番下のやつのアンカーを左下にすると…
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3003468/06a69afd-aa03-9539-b111-9fc3f593f1d0.png)
    * 縦横を変えても左下にちょうど来ました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3003468/7416835d-6968-2242-758b-8648e6af2b0e.png)
    * このように、ウィンドウサイズが変わっても大丈夫なようにする仕組みがアンカーです
* ただし、アンカーを設定しても、アスペクト比が変わると、重なってしまったりします。
 ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3003468/dab9a335-d122-5d33-84f7-03890c219a30.png)
* そこで、プリセット以外のアンカー設定を利用してみます。
    * アンカーは起点となる点を指定するだけではなく、任意のサイズにすることができます。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3003468/83e03f73-7a6f-7d69-ade9-9bef2ce8260a.gif" width="900" />

* サイズ変更に対応した、柔軟なものになります。
    * ただし、この場合画像のアスペクト比が変わって、画像がつぶれるようになっています。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3003468/6ac0f55c-ad51-cd2b-cb8d-0f244a73ed28.gif" width="900" />

## ScaleBoxを使う
* アスペクト比を変えないようにするためには、ScaleBoxが便利です。
    * ScaleBoxで囲むと、その子供のウィジェットを元のアスペクト比を維持するようにスケールしてくれます（ストレッチング設定によりますが。）
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3003468/60f5b02b-74e0-c209-303b-7f1de7507f2e.gif" width="900"/><br>
* ただし、やみくもに使うとフォーマンスに影響するため、注意が必要です。
    * https://www.docswell.com/s/EpicGamesJapan/KGLWJZ-UE4_UFE20W_gungho_60fpsAction#p57
## 他のパネルを使う
* 決められた間隔で並べたいときは、そもそもGridやUniformGridを使うほうが楽です。
* 今回はUniformGridを使ってみます。
    * 本筋とは直接関係ないですが、右クリックからの「…でラップする」「…と置換」が色々と試す際にも便利です。
    * 子で置換、もあるので、ScaleBoxでラップして、やっぱりいらないか…となったときに消す等に便利です。
* UniformGridを使えば、ScaleBoxを使うまでもなくきれいに並びます。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3003468/14190668-a09b-3424-fd7b-aaf155854c3c.png" />
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3003468/38f036a0-b8fa-2c49-00eb-a811665e0196.gif" width="600" />

* WrapBoxも使えそうですが、今回の例だとうまくいかないケースもありました。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3003468/8db939a7-4685-a755-f1c7-9d6efe36e8f0.gif" width="900" />

* ほかにも、Horizontal/VerticalBoxで並べる、Spacerで間隔を調整する、など使える手段は色々あると思います。
# まとめ
* 基準となるDPI Scaleを決める
* Canvasを使うときはアンカーをちゃんと設定する
* Canvas以外にも便利なパネルや機能があるので、積極的に使ってみる
* ScaleBoxがアスペクト比を維持するうえで便利
    * 使い方に気を付ける
# 参考にしたページ
* https://www.youtube.com/watch?v=tyeGvkLpcxo
* https://historia.co.jp/archives/5322/
* https://historia.co.jp/archives/7277/
    * 実践的で、非常に参考になります。
* https://docs.unrealengine.com/5.0/ja/umg-anchors-in-unreal-engine-ui/
* https://docs.unrealengine.com/4.27/ja/InteractiveExperiences/UMG/HowTo/ScalingUI/
* https://nasvic.hateblo.jp/entry/2020/09/05/131558