この記事はUnity Advent Calendar 2020の12月16日の記事です。
Webフロント技術からインスパイアされて開発されたUnityの次世代UIシステム**「UI Toolkit」がUnity2020.1からランタイムUIサポートをしています。1
今までのGameObjectベースではなくHTML/CSS**のような記述で開発します。
UI Toolkit is based on, and inspired by, standard web technologies. If you have experience developing web pages or applications, much of your knowledge might be transferable, and many of the core concepts might be familiar.
ここ最近はずっとuGUIで開発していた自分としてはとても気になっていたため、UI ToolkitをランタイムUIとして使ってみることにしました。
執筆する上で作ったもの
移動・透明度のアニメーションをしながら表示して、ボタンをクリックしたら全体が消えていくシンプルなUIを作ってみました。
本記事では大きく以下の2点について説明していきます。
- UI Toolkitを使ったUI画面構築方法
- アニメーションの実装方法
UI Toolkit全くやったこと無い人が何となく理解できるようになるというのが目標とします。
環境
- Unity2020.2.0b14.3668
- UI Toolkit 1.0.0-preview.13
1. UI Toolkitを使ったUI画面構築方法
この画面をUI Toolkitで作っていく過程を説明していきます。- 3隅にそれぞれLabel
- 真ん中に画像
- 真ん中下にButtonとLabel
このような構成です。
※背景は3D空間上に配置しているためUI Toolkitで管理していません
旧来のuGUIのように要素ごとにGameObjectとコンポーネントををHierarchyにペタペタ配置していくということはせず、UXMLと呼ばれるXMLのようなフォーマットで書いていきます。
先のUI構成要素をUXMLで表現すると以下のようになります。
※Sample.uxmlというUXMLファイルを作成しています。
<ui:UXML xmlns:ui="UnityEngine.UIElements">
<!-- 左上 : バージョン表記 -->
<ui:Label text="ver1.0.0-preview" name="Version" />
<!-- 右上 : ユーザーID表記 -->
<ui:Label text="UserId : 10378426348" />
<!-- 中央UIパーツ -->
<ui:VisualElement name="LogoContainer">
<!-- ロゴ画像 -->
<ui:VisualElement name="ProductLogo" />
<!-- ボタン -->
<ui:Button text="TAP TO START " name="BtnTapToStart" />
</ui:VisualElement>
<!-- 左下 : コピーライト表記 -->
<ui:Label text="2020(C)ohbashunsuke" />
</ui:UXML>
適宜説明をコメントアウトしているので深く説明しませんが、HTMLでマークアップしていく感覚でUI要素羅列していきます。
UXMLの簡易説明
ソースコードに3つの要素が出てきました。
- VisualElement
- Label
- Button
Label
、Button
、VisualElement
は、それぞれUnityEngine.UIElements
内のクラス名です。
<ui:Label text="ver1.0.0-preview" name="Version" />
例えばこれは「Label
クラスのインスタンスにver1.0.0-preview
という文字列をtext
プロパティに代入した」という意味になります。
ちなみに現状のUXMLを画面に映し出してみます。
UXMLを画面に反映する準備
Create > UI Toolkit > Panel Settings Asset
を選択してProjectにPanelSettings
を作成しておきます。
- ヒエラルキーにGameObjectを追加(UIGameObjectと命名)
- UIGameObjectに
UIDocumentコンポーネント
をAddComponent -
UIDocument
のインスペクタのSourceAsset
に先のUXMLをセット - 予め作成した
PanelSettings
をUIDocument
インスペクタのPanel Settings
にセット
画面に表示!!
UI要素をUXMLに記述しただけなので、**書いた順**に要素が表示されています。ここから配置、装飾処理を施していきます。
USSファイルで配置と装飾
.
├── Sample.uss
└── Sample.uxml
Sample.ussをSample.uxmlと同階層に作成します。
USSファイルはHTML/CSSでいうCSS
にあたります。
USSをUXMLにロードさせる
<Style src="Sample.uss" />
上記のコードをUXMLの2行目に追加します。
これでSample.uxmlがSample.ussを参照するようになります。
スタイルの定義
まずは左上のテキストから。
/*左上要素*/
.lt {
position: absolute;
left: 14px;
top: 14px;
}
position: absolute;
とabsolute
を設定することで絶対座標で配置することを指示します。
left:14px, top:14px
の設定で左から14px、上端から14pxの位置に配置する事になります。
この設定をUXMLで指定します。
<ui:Label text="ver1.0.0-preview" name="Version" class="lt" />
バージョン表記部分に class="lt"
を追加し、先のUSSで定義したlt
を適用しました。
※lt・・・LeftTopという意味
HTML/CSSの考え方と同じで、USSで定義したスタイルをUXMLの要素に割り当てます。この作業他2隅の要素に適用すると、以下のような見た目になります。
(ltの他rt・・・RightTop
、lb・・・LeftBttom
を追加)
ソースコードを見ていきましょう。
Sample.uxml
<ui:UXML xmlns:ui="UnityEngine.UIElements">
<!-- スタイルのロード -->
<Style src="Sample.uss" />
<!-- 左上 : バージョン表記 -->
<ui:Label text="ver1.0.0-preview" name="Version" class="lt" />
<!-- 右上 : ユーザーID表記 -->
<ui:Label text="UserId : 10378426348" class="rt" />
<!-- ~~~~ ロゴ部分省略 ~~~~ -->
<!-- 左下 : コピーライト表記 -->
<ui:Label text="2020(C)ohbashunsuke" class="lb" />
</ui:UXML>
各Label要素にclass
が追加されています。例えばclass="rt"
が追加された要素は右上端から14pxの距離に配置されるという事になります。
USSファイルを見ていきます。
Sample.uss
/*左上要素*/
.lt {
position: absolute;
left: 14px;
top: 14px;
}
/*右上要素*/
.rt {
position: absolute;
right: 14px;
top: 14px;
}
/*左下要素*/
.lb {
left: 14px;
position: absolute;
bottom: 14px;
}
USSには配置処理を記述しており、このclassが適用されるだけで配置処理が実行されます。
余談 : USSに変数を使用する
先のUSSファイル内に14pxがいくつも出てきました。変数に置き換えられると便利ですよね。
USSに変数を使用していきます。
--変数名: 値
という文法で変数定義。var(--変数名)
で変数を使用します。
サンプル
:root{
--hogehoge:428;
}
.piyo{
/* 「width:428;」と同じ処理になる */
width: var(--hogehoge);
}
変数をどこに定義するのか?という話になりますが、公式リファレンスで:root
要素に定義していたのでとりあえずそれに従っています。
Unity - Manual: USS custom properties (variables)
USSで変数を使うことで先のサンプルは以下のようになりました。
:root{
/*変数margin定義*/
--margin: 14px;
}
/*左上要素*/
.lt {
position: absolute;
left: var(--margin);
top: var(--margin);
}
/*右上要素*/
.rt {
position: absolute;
right: var(--margin);
top: var(--margin);
}
.lb {
position: absolute;
left: var(--margin);
bottom: var(--margin);
}
画像の利用
#ProductLogo {
background-image: url('/Assets/Project/Images/Logo.png');
height: 258px;
width: 604px;
}
画像をただ配置するのは簡単です。
background-image: url(画像のパス);
、width
とheight
を指定します。
※width
とheight
の指定が無いとサイズ0という事なのか表示されません。
このように画像も配置されました。
最後にボタン
角丸ボタンを作りたかったので、9スライスでの使用を想定した画像を使用しています。
#BtnTapToStart {
top: auto;
width: 302px;
height: 80px;
position: absolute;
bottom: 18%;
font-size: 26px;
opacity: 1;
background-image: url('/Assets/Project/Images/Atlas/OutGame/button-base-0.png');
-unity-background-scale-mode: stretch-to-fill;
-unity-slice-left: 10;
-unity-slice-top: 10;
-unity-slice-right: 10;
-unity-slice-bottom: 10;
background-color: rgba(0, 0, 0, 0);
color: rgb(210, 210, 210);
}
画像のスライス処理が入っているので、多少コードが長くなっています。
ボタンクリック時の処理をC#側に書いていきます。
var document = GetComponent<UIDocument>();
var root = document.rootVisualElement;
// 「BtnTapToStart」をキーにしてボタンインスタンスを取得
var tapToStartButton = root.Q<Button>("BtnTapToStart");
tapToStartButton.clicked += () => Debug.Log("画面全体の透明アニメーション処理実行");
指定要素の名前で取得する場合はQ<T>(要素名)
を使うと良いでしょう。
UI Toolkitにおける画面の構築方法のまとめ
- HTML/CSSのような感覚で配置とスタイルを適用
- UIDocumentコンポーネントでUXMLを画面に表示
- USSで変数を使う場合、
--変数名: 値
で定義、var(--変数名)
で使用 -
Q<T>(キー)
で要素を取得
2.アニメーションの実装方法
UI要素の配置とデザインの反映は出来ました。
ここからはアニメーションの実装方法を検証していきます。
スクリプトでアニメーション
大好きなDOTweenを使ってアニメーションをさせてみます。
DOTween (HOTween v2) | アニメーション ツール | Unity Asset Store
平行移動アニメーション
ヘッダ・フッターの横スライドアニメーションを実装していきます。2
var root = GetComponent<UIDocument>().rootVisualElement;
VisualElement target = root.Q<VisualElement>("target");
// 2秒かけて(-100, 0, 0)に向かってtargetを移動させる
DOTween.To(() => target.transform.position,
x => target.transform.position = x, new Vector3(-100, 0), 2f)
.SetEase(Ease.OutQuart);
VisualElementにはtransform
というITransform型のプロパティが定義されています。以下がITransform型のソースです。
namespace UnityEngine.UIElements
{
public interface ITransform
{
Vector3 position { get; set; }
Quaternion rotation { get; set; }
Vector3 scale { get; set; }
Matrix4x4 matrix { get; }
}
}
このようにTransformコンポーネントと同じようなプロパティが定義されているため、 VisualElementでも同様の使い方でアニメーションさせることが出来ます。
パーツの初期座標
各パーツVisualElementの実行時の座標ITransform.position
は配置された場所が**(0, 0, 0)**です。
透明度アニメーション
ロゴ部分の透明度アニメーションを実装していきます。
void Alpha(VisualElement ve, float duration, float alphaValue)
{
DOTween.To(() => ve.resolvedStyle.opacity,
x => style.opacity = new StyleFloat(x), alphaValue, duration);
}
透明をアニメーションさせる場合は、このようなコードになります。
ポイントとしては、以下の現在の透明度を取得部分です。
// 現在の透明度
var currentOpacity = ve.resolvedStyle.opacity;
VisualElementクラスのresolveStyle
プロパティから透明度(opacity)を取得する必要があります。1
AnimationClipでアニメーション実装
デフォルト状態では無理(だと思う)。
AnimationClipはシリアライズされるものしか扱えません。
UXML内のオブジェクトにAnimationClipがアクセスすることが出来ないため、工夫が必要になりそうです。少なくともデフォルト状態では使えません。
UI Toolkitのアニメーション実装のまとめ
- スクリプト実装一択**(デフォルト状態ではAnimationClipは使えません)**
- UIパーツの配置座標はそれぞれ(0, 0, 0)
- 現在の透明度は
resolveStyle
から取得する - DOTween便利
まとめ
本記事ではUI ToolkitをランタイムのUIとして使ってみました。
uGUIと違ってUIパーツの数分GameObjectを作らなくてよいのは新鮮でした。
USSでUIパーツの配置・装飾を定義したら、それを反映するだけで全体の見た目を一括で更新できてしまうのは、UIレギュレーションを守る機能として強力だなとも思いました。
(※その分設計が命ということになるののですが...)
今回はあえてUI Toolkitのヘルパー機能UI Builder
には触れていません。実際にUIToolkitでUIを構築する際はUI Builderを使った方が良いです。こちらはまたの機会に。
- デザイナー**「UXMLで構成要素を定義していく、配置や装飾はUSSで記述していく」**
- エンジニア**「C#で各要素の処理を書いていく、アニメーションを付けていく」**
このような分業が理想ではあるし、UI Toolkitで出来るような気もしますが、ただ現状の機能ではまだまだ課題が多い気もします。僕自身の検証も足りていないため、なんとも言えません。
以下パッと思いついた課題を羅列しています。未解決です。
思いつく課題
- GameObjectで作成するより自由度が低い
- ParticleSystemをUIで挟む構造は現状出来そうにない
- UIにポストエフェクトをかけられるか?=>RenderTextureに書き込んでuGUIのRawImageで表示する?
- USSファイルのインポート、使い回し方法
- 複雑なボタンの作り方
- ボタンの長押し対応
- USSからSpriteを利用する方法はある?
- AnimationClipが使えないためデザイナーがUIアニメーションを付けれない?(ソースコードを編集するのはハードル高し)
などなど。
この辺りは引き続き検証していく事になりそうで、まだまだ実戦投入するには早い印象です。
ただHTML/CSS感覚でUnityのUIを作れるのは面白いため試していない方は一度体験してみると良いかと。
今回のサンプルプロジェクトをGithubにアップロードしていますので気になる方はどうぞ。
baobao/UIToolkitRuntimeUISampler
※本記事の内容は説明のため簡略化していますのでリポジトリ内のファイルとは差分があります
最後に
Unityを使ったUI開発について今年のアドベントカレンダーに投稿しています。そちらもよろしくお願い致します。
【Unity】新規ゲームのUI開発で気をつけた39のTips前編 - Qiita
明日は@glitchpopさんの番です。
参考リンク
- Comparison of UI systems in Unity - Unity マニュアル
- UI Toolkit | UI Toolkit | 1.0.0-preview.13
- Overview | UI Builder | 1.0.0-preview.11
- UIElements: First Steps - Unity Learn
- UIElements Tutorial for Unity: Getting Started | raywenderlich.com
- 【Unity】【UIElements】USS(スタイルシート)を使って装飾を行う方法まとめ - LIGHT11