UIの面倒事すべてをスクリプトひとつで解決

「UI周りがごちゃごちゃしないようにプロジェクトの最初から最後まで面倒を見続けてくれる誰か」が居ないプロジェクト向け。

下手なことするよりは分かりやすいし拡張性もある、というモノをサクッと作れるように。


# Unity での UI 作成はすごく面倒

その理由は UI イベントの発生時、イベントコールバック側からは発生元のオブジェクトや変更された数値、状態などの情報が何も得られないから。


<!---
https://qiita.com/sator_imaging/items/640c1367541a9da96084
-->

とりあえずイベントの発生元が分からない問題をどうにかしよう。というかそれさえ解決すればすべてがうまくいく、ということで、その試みと結果がコチラ。


<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">UIの面倒事すべてをスクリプトひとつで解決しよう、という試み<br>その２　完全に自動化した　もうインスペクターと戦う必要はないな<a href="https://twitter.com/hashtag/unity3d?src=hash&amp;ref_src=twsrc%5Etfw">#unity3d</a> <a href="https://t.co/jthXEz9A7s">pic.twitter.com/jthXEz9A7s</a></p>&mdash; サトー™ (@sator_imaging) <a href="https://twitter.com/sator_imaging/status/1459140310504992768?ref_src=twsrc%5Etfw">November 12, 2021</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>


<!--
- 面倒事を全部解決する機能をひとつに集約。
- 冗談みたいなコードだけど想像以上にうまいこと働いてくれる。
-->




# イイところ・ダメなところ


✅ 指定のメソッドが実装されていれば実行。という Unity っぽさ。
✅ スクリプト１つ。余計な依存なし。
✅ イベントの発生元が通知される。イベントハンドラ側からの参照は不要。
❌ ~~ビルドして試してない。けど多分大丈夫。~~
❌ 楽するために無駄が多い。無駄が多いだけで処理自体は単純、重くない。
❌ イベントを `Editor and Runtime` にするとなんかエラー出る。



イベントハンドラの中身は UI の数値取ってきて実際に処理を行うメソッドに渡すだけ、１～２行なんてザラだしオブジェクト名で `switch` で十分でしょう。


ノープランで初めて「ラジオボタン作りたくなった」「チェックボックスの状態でコントロールオンオフしたくなった」て時もかなり気楽に対応できる。
~（若干冗長、２～３行で済んだことが５～６行になるかもしれないけど）~


<!--
ノープランで適当に作り始めても取っ散らかったりしない。
UI のみに延々と時間かけてられないときに最適。
-->



<!--

※ `EventSystem.current.currentSelectedGameObject` で発生元の `GameObject` は一応取得できる。
（でもUIイベントはスクリプト経由でも発火するけどその時は何が入ってる？）

コールバック側が UI の存在を知らなくてもいい状態は唯一、同じ `GameObject` に存在するときだけ。ただし、タブやラジオボタンのような複数を連動させるような処理で破綻。

結局どこかのタイミングで UI 関連をまとめる Event Manager 的なモノを作る羽目になる。


その Event Manager も中身は命名にも困るような、メソッドにただ別名付けてるだけのような代物。とにかく面倒ですね。

-->

<!--
イベント発生元がわからないから名前でとりあえず `switch`、も無理。変更された状態・数値も通知されないから結果１コントロール＝１スクリプトとかになりがち。
-->


<!--
設定箇所も UI コントロールにイベントアクション、イベントハンドラにイベント送信元への参照の２か所、という場合が多い。
-->




# ダウンロード＆インストール

👉 [UIEverything-Unity: Simplifies UI event handling on Unity.](https://github.com/sator-imaging/UIEverything-Unity)




<!--
## UI コントロールの設定

UI の各コントロールに `UIEverything` を追加、インスペクターでコントロールの種類に合わせたアクションとターゲットとなるイベントハンドラを設定。

一度 UI コントロールを設定したら、あとはイベントハンドラ側しか見なくて大丈夫になる。

<img src="https://dl.dropbox.com/s/xfrwrt4rt7ipa8a/UIEverything_OnButtonClick.png" />

-->

## イベントハンドラの実装


`MonoBehaviour` の `Awake`、`OnEnable`、`Start` 等と同様、イベントハンドラに指定のメソッドが実装されていれば発生元の UI コントロールを添えて実行してくれる。

※ イベントを処理するコンポーネントは `UIEverything` と同じ `GameObject` に追加すること。





```csharp:コールバック一覧
void OnSliderChanged(Slider sender);
void OnButtonClick(Button sender);
void OnToggleChanged(Toggle sender);
void OnInputFieldChanged(InputField sender);
void OnInputFieldChangedInteractive(InputField sender);
void OnScrollbarChanged(Scrllbar sender);
void OnDropdownChanged(Dropdown sender);
void OnScrollRectChanged(ScrollRect sender);
```



イベントハンドラ上のコンポーネントすべてが対象なので、重厚長大なコンポーネントひとつでもいいし、小分けで沢山ぶら下げても良い。

<!--
追加で何かしたくなったとしても `sender` が見えてるからイベントハンドラ側だけで何とかなるし、どうにもならない大事になりそうならまた考えればいい。

（大抵の場合イベントが起きたら○○機能を呼ぶ、とか繋ぎ込みがメインだから問題なんて起きないでしょう）
-->


### 注意点

イベントハンドラから `sender` に対して数値等の設定を行う場合は、`SetValueWithoutNotify` を使う。

`sender.value = ...` だとコールバックが一生ループするかも。



<!--

### Dropdown、Scrollbar、Scroll View (ScrollRect)

これらのイベントアクションはインスペクターだと引数の指定があるが、構わず `GameObject` を設定できるようにしていて、問題なく動作してるように見える。

本来受けられるはずだった引数は `sender` から辿って取得。

-->


## コンポーネントモードとヘルパー関数


UIEverything を `Slider` または `InputField` と同じ `GameObject` に追加すると、コンポーネントモードとして動作する。

コンポーネントモードでは「スライダーと数値表示を連動させる」「入力フィールドとスライダーを同期させる」というよくある処理のヘルパー関数が使用可能になる。


ヘルパー関数一覧：

- Synchronize_Slider_to_Text
    - 👇 小数点以下の扱いが違うバージョン
    - Synchronize_Slider_to_Text_1_0
    - Synchronize_Slider_to_Text_1_00
    - Synchronize_Slider_to_Text_1_000
- Synchronize_Slider_to_InputField
- Synchronize_InputField_to_Slider
  

<img src="https://dl.dropbox.com/s/8hplfw2kymvyg5t/UIEverything_HelperFunctions.png" />


# おわりに

<!--
UI作る時、どうやっても見るべき場所が散らかってしまうのでどうにかしたかった。けど上手い解決策が見つからなかったヤツ。

UI は何も難しくないんだけど、メソッドに別名付けるだけのような、命名に困る細切れを大量に用意＆イベント発生元を参照するフィールドも用意、がとにかく面倒だったっていう。
-->

<!--
ラジオボタンとか作ろうとすると結局イベントハンドラに参照は必要になる。けど、ボタンとかスライダーの数値を使ってあーだこーだ、とかがビックリする位簡単になる。

オブジェクト名で `switch` するの最高に楽です。
-->

Unity の UI はイベントが発生したことしか分からないので、コールバック側は事前に呼び出し元になる UI への参照を保持しておく、というのが当たり前の状態。
→ UI が増えた・減った
→ 色んなところに UI を参照するフィールドを増やす・減らす
→ 繰り返し

このあたりが UI メンドクサイ！ と思う理由ですかね。

「そういうの全部解決できますよ（どデカいライブラリどーーん）」もちょっと違うんですよね。もっと単純でいい。



--


以上です。お疲れ様でした。