第2ターム審査会用②

UI ToolKitの練習(Dialogueの実装)

UnityのUI Toolkitは、**UI Builder(GUIツール)**でUIのレイアウトを作成し、UXMLファイルでUIの構造を、USSファイル(CSSに相当)で見た目を、そしてC#スクリプトでUIの挙動を実装する開発ワークフローが基本です。まずUI BuilderでUXMLファイルを作成し、次にUSSでデザインを調整し、最後にC#スクリプトで各UI要素にイベントリスナーを登録してインタラクティブな機能を追加します。

開発の主な流れ

  1. 1. UI BuilderでUIを作成する
    • メニューの Window > UI Toolkit > UI Builder からUI Builderを開きます。
    • File > New:から新しいUXMLテンプレートを作成します。
    • Visual Element(ボタン、ラベル、テキストフィールドなど)をドラッグ&ドロップで配置し、Inspectorでプロパティ(サイズ、フォント、色など)を調整します。
    • UIのレイアウトや要素の定義が完了したら、UXMLファイルとして保存します。
  2. 2. UIをシーンに適用する
    • シーンに空のGameObjectを作成し、UI Documentコンポーネントを追加します。
    • UI Documentコンポーネントに作成したUXMLファイルをアサインし、必要であればPanel Settingsも設定します。
  3. 3. USSでUIをスタイリングする
    • USSファイルはWebにおけるCSSにあたり、UIの見た目を定義します。
    • UI Builderで右クリックメニューから新しいUSSファイルを作成したり、既存のUSSファイルを編集します。
    • よく使う要素のスタイルをクラスとして保存し、複数の要素に一括適用することも可能です。
  4. 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 AssetDialogue.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(ライト/ダークやプラットフォーム差をまとめる器)。
  • PanelSettingsTheme Style Sheets には .tss しか直接入れられません。
    なので今回のように .tss に .uss をアタッチして経由適用するのは正しいやり方です👍

他の貼り方も一応メモ:

  • UXML内に直接リンク
<UXML ...>
  <Style src="Assets/UI/Dialog.uss"/>
  ...
</UXML>
  • あるいは UIDocumentrootVisualElement.styleSheets.Add(...) を C# で追加(上級者向け)。

仕上げの小ワザ(すぐ効くやつ)

  1. 中央寄せ&余白(もう入ってるかもだけど一応) :
: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 目安)。

コメント

タイトルとURLをコピーしました