UI ToolKitの練習(Dialogueの実装)
UnityのUI Toolkitは、**UI Builder(GUIツール)**でUIのレイアウトを作成し、UXMLファイルでUIの構造を、USSファイル(CSSに相当)で見た目を、そしてC#スクリプトでUIの挙動を実装する開発ワークフローが基本です。まずUI BuilderでUXMLファイルを作成し、次にUSSでデザインを調整し、最後にC#スクリプトで各UI要素にイベントリスナーを登録してインタラクティブな機能を追加します。
開発の主な流れ
- 1. UI BuilderでUIを作成する
- メニューの Window > UI Toolkit > UI Builder からUI Builderを開きます。
- File > New:から新しいUXMLテンプレートを作成します。
- Visual Element(ボタン、ラベル、テキストフィールドなど)をドラッグ&ドロップで配置し、Inspectorでプロパティ(サイズ、フォント、色など)を調整します。
- UIのレイアウトや要素の定義が完了したら、UXMLファイルとして保存します。
- 2. UIをシーンに適用する
- シーンに空のGameObjectを作成し、UI Documentコンポーネントを追加します。
- UI Documentコンポーネントに作成したUXMLファイルをアサインし、必要であればPanel Settingsも設定します。
- 3. USSでUIをスタイリングする
- USSファイルはWebにおけるCSSにあたり、UIの見た目を定義します。
- UI Builderで右クリックメニューから新しいUSSファイルを作成したり、既存のUSSファイルを編集します。
- よく使う要素のスタイルをクラスとして保存し、複数の要素に一括適用することも可能です。
- 4. C#スクリプトでUIを制御する
- UI Documentコンポーネントを持つGameObjectにアタッチするスクリプト、またはそのUIを使う他のGameObjectのスクリプトを作成します。
- `GetComponent<UIDocument>()`でUI Documentを取得し、
rootVisualElement
から目的のVisual ElementをQ<>メソッド(またはFindなど)で参照します。 - 各UI要素(Buttonなど)の
RegisterCallback
メソッドを使って、ボタンのクリックなどのイベントに対する処理を記述します。
UI Toolkitのメリット
- 視覚的な開発:GUIツールであるUI Builderにより、コーディングなしで直感的にUIレイアウトを作成できます。
- USSによるスタイル管理:CSSに似たUSSでスタイルを定義・管理できるため、デザインの再利用や一貫性の確保が容易です。
- 柔軟性:エディター拡張、ランタイムUI、デバッグツールなど、様々な場面でUI開発が可能です。
①UI DocumentのSourceAssetにDialogue.uxmlをアタッチ
②Dialogue.ussをThema.tssの[StyleSheet]にアタッチ
↓それぞれの中身
1) UXML(Assets/UI/Dialogue.uxml
)
<UXML xmlns="UnityEngine.UIElements">
<VisualElement name="root" class="dialog-root">
<Label name="title" class="dialog-title">Title</Label>
<Label name="body" class="dialog-body">Body</Label>
<VisualElement name="choices" class="choices-row"/>
<VisualElement name="nextIcon" class="next-icon">▶</VisualElement>
</VisualElement>
</UXML>
2) USS(Assets/UI/Dialogue.uss
)
.dialog-root { padding: 16px; width: 640px; background-color: rgba(0,0,0,0.6); border-radius: 12px; }
.dialog-title { font-size: 20px; unity-font-style: bold; margin-bottom: 8px; }
.dialog-body { white-space: normal; margin-bottom: 12px; min-height: 120px; }
.choices-row { flex-direction: row; gap: 8px; }
.next-icon { align-self: flex-end; opacity: 0; }
.next-icon.show { opacity: 1; }
3) シーンに UIDocument
を配置
- GameObjectに
UIDocument
を付け、Source Asset
にDialogue.uxml
を指定。 DialogueUI_Toolkit
(下のC#)を同じオブジェクトに付与。
4) C#(タイプライター+選択肢生成)
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UIElements;
public class DialogueUI_Toolkit : MonoBehaviour
{
public float charsPerSec = 40f;
UIDocument _doc;
Label _title;
Label _body;
VisualElement _choicesRoot;
VisualElement _nextIcon;
Queue<string> _lines = new();
Coroutine _typing;
bool _isTyping;
Action<int> _onChoice;
void Awake()
{
_doc = GetComponent<UIDocument>();
var root = _doc.rootVisualElement;
_title = root.Q<Label>("title");
_body = root.Q<Label>("body");
_choicesRoot = root.Q<VisualElement>("choices");
_nextIcon = root.Q<VisualElement>("nextIcon");
ShowNext(false);
ClearChoices();
}
void Update()
{
// Enter/Spaceで送り or スキップ
if (Input.GetKeyDown(KeyCode.Return) || Input.GetKeyDown(KeyCode.Space))
{
if (_isTyping) FlushTyping();
else if (_lines.Count > 0) StartTyping(_lines.Dequeue());
}
}
public void ShowEvent(string title, IEnumerable<string> lines)
{
_title.text = title;
_body.text = "";
_lines.Clear();
foreach (var ln in lines) _lines.Enqueue(ln);
ClearChoices();
if (_typing != null) StopCoroutine(_typing);
StartTyping(_lines.Dequeue());
}
public void ShowChoices(string[] labels, Action<int> onChoice)
{
ClearChoices();
_onChoice = onChoice;
for (int i = 0; i < labels.Length; i++)
{
var btn = new Button();
int idx = i;
btn.text = labels[i];
btn.clicked += () => _onChoice?.Invoke(idx);
_choicesRoot.Add(btn);
}
ShowNext(false);
}
void StartTyping(string text)
{
if (_typing != null) StopCoroutine(_typing);
_typing = StartCoroutine(TypeRoutine(text));
}
IEnumerator TypeRoutine(string text)
{
_isTyping = true;
ShowNext(false);
_body.text = "";
float t = 0f; int shown = 0;
while (shown < text.Length)
{
t += Time.deltaTime * charsPerSec;
int want = Mathf.Clamp(Mathf.FloorToInt(t), 0, text.Length);
if (want != shown) { _body.text = text.Substring(0, want); shown = want; }
yield return null;
}
_isTyping = false;
ShowNext(_lines.Count > 0);
}
void FlushTyping()
{
if (_typing != null) StopCoroutine(_typing);
_typing = null;
_isTyping = false;
// 簡易:すでに body へ流し込んでいるので何もしない
ShowNext(_lines.Count > 0);
}
void ClearChoices()
{
_choicesRoot.Clear();
}
void ShowNext(bool on)
{
if (_nextIcon == null) return;
if (on) _nextIcon.AddToClassList("show");
else _nextIcon.RemoveFromClassList("show");
}
}
- UI Toolkitのスタイルは2系統あります。
.uss
= 通常の StyleSheet。.tss
= Theme StyleSheet(ライト/ダークやプラットフォーム差をまとめる器)。
PanelSettings
の Theme Style Sheets には.tss
しか直接入れられません。
なので今回のように .tss に .uss をアタッチして経由適用するのは正しいやり方です👍
他の貼り方も一応メモ:
- UXML内に直接リンク
<UXML ...>
<Style src="Assets/UI/Dialog.uss"/>
...
</UXML>
- あるいは
UIDocument
のrootVisualElement.styleSheets.Add(...)
を C# で追加(上級者向け)。
仕上げの小ワザ(すぐ効くやつ)
- 中央寄せ&余白(もう入ってるかもだけど一応)
:
:root { width:100%; height:100%; }
#root { width:100%; height:100%; justify-content:center; align-items:center; }
.dialog-root { width: 640px; padding: 16px; background-color: rgba(0,0,0,.6); border-radius: 12px; }
.dialog-title { font-size: 20px; unity-font-style: bold; margin-bottom: 8px; }
.dialog-body { white-space: normal; min-height: 120px; margin-top: 8px; margin-bottom: 12px; }
.choices-row { display:flex; flex-direction:row; gap:8px; }
.next-icon { align-self:flex-end; opacity:0; }
.next-icon.show { opacity:1; }
2.ボタン幅を揃える .choices-row > Button { min-width: 96px; }
3.高DPIでも読みやすく PanelSettings > Scale Mode = Constant Pixel Size
のままなら、フォントを少し大きめに
(タイトル 22〜24、本文 16 目安)。
コメント