【Unity】【UI Toolkit】Visual ElementやUSSのクラスの名前をBEMベースで厳密に命名する

UnityのUI ToolkitのVisual ElementやUSSのクラスの名前をBEMベースで厳密に命名する方法についてまとめました。

厳密な命名の重要性

USSでは以下の記事のように複雑なセレクタの指定方法を使うことで、柔軟にスタイルを適用できます。

light11.hatenadiary.com

しかしながら柔軟であるがゆえに、複雑すぎるセレクタがあったり、セレクタの分け方のルールが曖昧だったりすると、他人が理解できなくなり、大人数で効率よく開発することが難しくなります。

この問題を解決するために、UnityではBEMベースの命名を推奨しています。
BEMとはWebの文脈から生まれた、CSSにおける拡張性や保守性を保つための命名手法です。

本記事ではUnityにおけるBEMの概念や実際の使い方についてまとめます。

BEMの概念

BEMでは適切な命名を行うために、各UIのパーツをBlock, Element, Modifierという概念で整理します。(これらがBEMの名前の由来です)

まず、ヘッダー、検索フィールド、タブなどの各パーツをBlockと呼びます。
また、Blockの中にあるテキストフィールドやボタンなどの各要素をElementと呼びます。

BlockとElement

ここで、検索フィールドが空の時にボタンの見た目が押せない状態に変わるとします。

押せない

このような状態を表すための装飾をModifierと呼びます。
ModifierにはBlockに対するものとElementに対するものがあります。
今回の例はボタンElementに対するModifierということになります。

BEMベースの命名規則

BEMでは、前節で説明したBlock, Element, Modifierを組み合わせて、以下のルールに基づいてクラスを命名します。

  • Block, Element, Modifierを複数単語で構成する場合には単語間をハイフンで繋ぐ
  • BlockとElement間はアンダースコア二つで繋ぐ
  • ElementとModifierの間はハイフン二つで繋ぐ

これを前節の検索フィールドの例に照らし合わせると、各クラスの命名は以下の通りとなります。

  • 検索フィールド: search-field
  • 検索フィールドのボタン: search-field__button
  • 検索フィールドのボタン(押せない状態): search-field__button—-disabled

USSの記述としては以下のようなイメージです。

.search-field {
    flex-direction: row;
}

.search-field__button {
    color: #E5E5E5;
    background-color: #929292;
}

.search-field__button—-disabled {
    color: #929292;
    background-color: #3F3F3F;
}

なおこの辺りの命名ルールはBEMとして特定の決まりがあるわけではありません。
本記事ではUnityが推奨している命名方法を紹介しています。

クラスを適用する

次に、前節のクラスを実際に検索フィールドのボタンに適用することを考えます。

クラスを適用するには、スタイルシートを適用した上で、VisualElement.AddToClassListでデフォルトのスタイルが記述してあるsearch-field__buttonクラスを追加します。

var button = searchField.Q<Button>(); // 適切な方法で対象のボタンを取得する
button.AddToClassList("search-field__button");

スタイルシートの適用の仕方は割愛しますが、以下の記事にまとめていますので必要に応じて参照してください。

https://light11.hatenadiary.com/entry/2020/03/08/202458#StyleをUSSファイルに切り出す

ボタンが押せない状態になったら、押せない状態の時に上書きするべきスタイル情報が記述してあるsearch-field__button—-disabledを追加します。
再び押せる状態になったらVisualElement.RemoveFromClassListでそれを削除します。

button.AddToClassList("search-field__button--disabled"); // クラスを追加
button.RemoveFromClassList("search-field__button--disabled"); // クラスを削除

ちなみにこれらのメソッドの他にVisualElement.EnableInClassListVisualElement.ToggleInClassListがありますが、これらは上述のメソッドの単なるラッパーです。

VisualElementの継承とクラス

次に、UIToolkitのButtonを継承してプロジェクト独自のCustomButton型を作ることを考えます。

このとき、Buttonのコンストラクタではunity-buttonクラスが適用されています。
CustomButtonでスタイルをオーバーライドしたい場合には、custom-buttonのようなクラスを新しく作り、unity-buttonクラスから変更したいスタイルだけを記述し、VisualElement.AddToClassListで追加します。

つまり、CustomButtonにはunity-buttoncustom-buttonの両方のクラスが適用されていることになります。

このように、VisualElementを継承した際には継承した分だけスタイルを記述したクラスを追加していくことになります。

外部ツールなどを作る際の注意点

上述の通りUSSのクラスはそのクラス名を用いて解決されるため、名前が被らないように注意する必要があります。

特に共通パーツや外部ツールを作る際には注意が必要です。

他プロジェクトでも使用するようなUIを作る場合には、unity-buttonunity-のようにPrefixをつけることを検討するべきです。

関連

light11.hatenadiary.com

参考

docs.unity3d.com