白黒羊

UIToolkit を Runtimeで使ってみる①

UI ToolkitはUnityでもHTMLとかCSSのような感じでWEBっぽくUIを組んでみようという機能です。
まだPreviewで機能も不足していますし、今後の変更も激しいでしょうが、試しに使ってみようと思います。
Unity 2021.2.0bのマニュアルを読んでいます。

https://docs.unity3d.com/2021.2/Documentation/Manual/UIElements.html

UI Toolkitでだけできること

  • CSSが使えるよ
  • Shader Graphとの連携(する予定だよ)

UGUIではできるけど UI Toolkitではできないこと

  • Prefab (代わりにAttribute overrideがあるよ)
  • Rich text tags / SDF text / Font fallbacks (そのうちできるようになるよ。UGUIではTextMeshProでできるよ)
  • Grid Layout (できるかどうか調べてるよ)
  • 新しい Input System / Serialized events (計画中だよ)
  • 3D (計画中だよ)
  • Animation (計画中だよ)

UI Toolkit導入方法

Unity 2021.2以降はBuiltInとして含まれているのでRuntime UI用のパッケージ(com.unity.ui)のインポートの必要はありません。それより前のバージョンで使う場合は、Unity Package ManagerでPreview packageを使えるようにして、Add package from git URL… を選択して com.unity.ui を入力します。

UXMLを書く

Projectペインで右クリックして、Create > UI Toolkit > UI Document を選択します。タイトル画面を作るのでTitle.uxml と命名しました。

Unity – Manual: Writing UXML Templates

<?xml version="1.0" encoding="utf-8"?>
<engine:UXML xmlns:engine="UnityEngine.UIElements" >
    <engine:Label text="Select something to remove from your suitcase:"/>
    <engine:Box>
        <engine:Toggle name="boots" label="Boots"/>
        <engine:Toggle name="helmet" label="Helmet"/>
        <engine:Toggle name="cloak" label="Cloak of invisibility"/>
    </engine:Box>
    <engine:Box>
        <engine:Button name="cancel" text="Cancel"/>
        <engine:Button name="ok" text="OK"/>
    </engine:Box>
</engine:UXML>

内容はUnity公式のマニュアルに載っていたものとほぼ同じです。

次に、設定用のファイルを作成します。Projectペインで右クリックしてCreate > UI Toolkit > Panel Settings Assetを選択し、PanelSettingsと名前をつけました。
このPanelSettingsのInspectorで今までCanvasでしていた設定に近い設定ができます。試しに Scale ModeScale With Screen Size に設定して解像度を 1920 x 1080 にしてみました。この辺りはお好みで。
この中に Theme Style Sheet という設定項目があるのでここでスタイルシートが設定できるのだと思います。

Hierarchyペインに新しいGameObjectを追加しました。名前をCanvasとしましたが、従来のUGUIと同じ名前は紛らわしいかもしれません。UI Document用だから UIDocとかの方がいいかもですね。
Add Componentで UI Document コンポーネントを追加して Panel SettingsSource Assetにそれぞれ今作成したファイルを設定しました。

Game ペインにUnityっぽい雰囲気のUIが出てきました。

試しに表示できたところで、ここから自作ゲームの背景とロゴの設置をしてみようと思います。

まずはロゴを表示するために先程のTitle.uxmlを書き直します。

<?xml version="1.0" encoding="utf-8"?>
<ui:UXML xmlns:ui="UnityEngine.UIElements"
         xsi="http://www.w3.org/2001/XMLSchema-instance" editor-extension-mode="False">
    <ui:Box name="Canvas"
            style="justify-content: space-around; width: 100%; height: 100%; padding-left: 200px; padding-right: 200px; padding-top: 50px; padding-bottom: 100px;"> <!--Adobe XDでもガイドラインを指定していましたが、要素が外側に行きすぎないように一番外側の要素にマージンを設定しました。マージンにマイナスの値など正しくない値を設定すると Invalid property errorがConsoleに大量に表示されるようです。-->
        <ui:Box name="LogoBox" style="align-items: center; display: flex;"> <!--alignは子要素に適用されるようなのでlogo画像を持つ要素の一つ上にBoxを作成しました。-->
            <ui:Image name="logo"
                      style="background-image: url('/Assets/Project/Textures/UITexture.psd#title-logo'); width: 995px; height: 311px; display: flex;"/> <!--multipleで設定したSpriteの指定方法です。テクスチャまでのパスに#をつけて分割したSpriteの名前を書くとそのSpriteだけを持ってきてくれます。%かpxで縦横をそれぞれ指定する形式です。-->
        </ui:Box>
    </ui:Box>
</ui:UXML>

と、さっくり書ければ良いのですが、実際には何をどうすれば良いのかわからなかったので、UI Builderを使って元になるUXMLを自動生成し、最終的に手で整形しました。慣れたら自分でUXMLを書いて画面を作り上げたいですね。

これを表示すると灰色の背景が表示されました。UXMLでスタイルシートやクラスは指定していないのになぜ……? と思っていたのですが、PanelSettingsのTheme Style Sheetに自動的にUnityの Default Runtime Theme File というアセットが設定されていました。これが要素を検出して .unity-box というクラスを与えて背景の色を変えていたようです。

Debugging your styles | UI Builder | 1.0.0-preview.18

スタイルシートはあとで設定することにして、ひとまずProjectペインで右クリックしてCreate > UI Toolkit > TSS Theme Fileを選択し、RainbowSeaThemeと名前をつけました(RainbowSeaは私が作っているゲームの名前です)。
これをPanelSettingsのTheme Style Sheetに設定することでデフォルトのスタイルがなくなり、背景が透明になってくれました。
これにて解決! と思ったのですが、Default Runtime Theme Fileを使わないことで動かなくなるコンポーネントが多々あるようです。現時点ではDefault Runtime Theme Fileはただ見た目を変える以上の働きをしているようなので、今回のケースならば自作のStyleSheetで.unity-boxを上書きするなどして対応する方が正しいようです。

今回はこのまま、次に背景画像を置いてみます。
まずは先ほどのCanvas要素にbackground-imageとして設定してみます。

なぜか上半分に寄ってしまいました。
PanelSettingsのTheme Style Sheetに先ほどのDefault Runtime Theme Fileを戻してあげるとちゃんと全面に表示されます。これが先ほど書いたDefault Runtime Theme Fileを使わないことの副作用の一種です。

UI Toolkit Debuggerを使って確認すると、すべての要素の上位要素にあたるTemplateContainerクラス(unity-ui-document__root)にflex-grow: 1;が指定されている必要があるみたいです。
Projectペインで右クリックしてCreate > UI Toolkit > Style Sheetを選択し、自作のUSSファイルを作成します。共通のスタイルを書いていきたいので Common.ussとしました。
他のアセットと同じで名前は後から変えられるのですが、名前を変えると簡単に参照が外れてしまうので慎重に名前をつけておいた方が後々楽です。

.unity-ui-document__root {
    flex-grow: 1;
}

このように記述して、RainbowSeaThemeのStyle Sheetsに追加してあげます。これで全画面に表示されるようになりました。

また、画像の縦横比を維持したいので、-unity-background-scale-mode: scale-and-crop;オプションを使いました。クロップの基準点が常に真ん中になってしまうのですが、これを左下とかに変更する方法はあるのでしょうか……。
今回は手っ取りばやく、画像をちょうど良いサイズに編集しました。

<?xml version="1.0" encoding="utf-8"?>
<ui:UXML xmlns:ui="UnityEngine.UIElements"
         xsi="http://www.w3.org/2001/XMLSchema-instance" editor-extension-mode="False">
    <ui:Image name="bg"
              style="position: absolute;
                     left: 0; top: 0; bottom: 0;
                     flex-grow: 1; flex-shrink: 0; 
                     background-image: url('/Assets/Project/Textures/UITexture.psd#title-bg');
                     height: 100%; width: 100%;
                     -unity-background-scale-mode: scale-and-crop;"/>
    <ui:Box name="Canvas"
            style="justify-content: flex-start;
                   width: 100%; height: 100%;
                   padding-left: 200px; padding-right: 200px; padding-top: 50px; padding-bottom: 100px;">
        <ui:Box name="LogoBox"
                style="align-items: center; display: flex;">
            <ui:Image name="logo"
                      style="background-image: url('/Assets/Project/Textures/UITexture.psd#title-logo');
                             width: 995px; height: 311px; display: flex;"/>
        </ui:Box>
    </ui:Box>
</ui:UXML>

できました。UXMLファイルがごちゃごちゃしてきたのでこのあたりでスタイルを全部スタイルシートに移そうと思います。

タイトル画面専用のスタイルシートを作成してTitle.ussとしました。
これをThemeの Style Sheetsに追加するか、UXML内に <ui:Style src="/Assets/path-to-stylesheet/Title.uss"/>という記述を足すことで新たなスタイルシートが使えるようになります。
今回はTitle.uxml専用のスタイルシートなので、ui:Styleを指定することにしました。

<?xml version="1.0" encoding="utf-8"?>
<ui:UXML xmlns:ui="UnityEngine.UIElements"
         xsi="http://www.w3.org/2001/XMLSchema-instance" editor-extension-mode="False">
    <ui:Style src="/Assets/Project/Scripts/UIs/ToolkitItems/StyleSheets/Title.uss"/>
    <ui:Image name="bg" class="title-bg"
              style="background-image: url('/Assets/Project/Textures/UITexture.psd#title-bg');"/>
    <ui:Box name="Canvas" class="title-canvas">
        <ui:Box name="LogoBox" class="title-logo-box">
            <ui:Image name="logo" class="title-logo-image"
                      style="background-image: url('/Assets/Project/Textures/UITexture.psd#title-logo');"/>
        </ui:Box>
    </ui:Box>
</ui:UXML>
.unity-ui-document__root {
    flex-grow: 1;
}

.title-bg {
    position: absolute;
    left: 0;
    top: 0;
    bottom: 0;
    flex-grow: 1;
    flex-shrink: 0;
    height: 100%;
    width: 100%;
    -unity-background-scale-mode: scale-and-crop;
}

.title-canvas {
    justify-content: flex-start;
    width: 100%;
    height: 100%;
    padding: 50px 200px 100px;
}

.title-logo-box {
    align-items: center;
    display: flex;
}

.title-logo-image {
    width: 995px;
    height: 311px;
    display: flex;
}

USSに分けた方がIDEの補完も効くしごちゃごちゃ感が減るので嬉しいですね。

Q&A

ussで全ての要素にスタイルを適用したい。

CSSと同じで * を指定すると全ての要素に適用される。
Unity – Manual: USS Selectors

枠線設定した覚えもないし、ussで border-width: 0 にしたのにGame ペインに何故かうっすら枠線が表示される。

UI Toolkit Debuggerの Show Layout がオンになっているかも?

UIElementSchemaというフォルダが勝手にできた。消したりgitignoreして良い?

RiderとかVisual StudioのようなIDEの自動補完用ファイルらしい。自分が作成した型の自動補完もしてくれて便利なので普通にバージョン管理すればいいと思いますが、消してもゲームが動かなくなるとかはありません。
https://forum.unity.com/threads/uielementsschema.954252/