unity-ui-development

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Unity UI Development

Unity UI 开发

Overview

概述

Unity provides two UI systems: UGUI (the established GameObject-based system) and UI Toolkit (the newer retained-mode system inspired by web technologies). This skill covers both systems, when to use each, and implementation patterns.
Unity提供两种UI系统:UGUI(基于GameObject的成熟系统)和UI Toolkit(受Web技术启发的新型保留模式系统)。本技能涵盖这两种系统的使用场景与实现模式。

Choosing a UI System

UI系统选择

FactorUGUIUI Toolkit
Runtime UIMature, full-featuredSupported (Unity 2023+)
Editor UINot supportedPrimary system for editor
World-space UIExcellent (World Space Canvas)Limited
Animation/TweeningDOTween, Animator, LeanTweenUSS transitions, C# manipulation
StylingPer-element, manualUSS stylesheets (CSS-like)
PerformanceDraw call heavy with many canvasesRetained mode, lighter draw calls
Learning curveLower for Unity devsHigher (web-like paradigm)
VR/ARWell-supportedLimited world-space support
Recommendation: Use UGUI for world-space UI, VR/AR, and projects needing maximum asset store compatibility. Use UI Toolkit for editor extensions, complex data-driven UIs (lists, trees), and projects on Unity 2023+.
因素UGUIUI Toolkit
运行时UI成熟、功能完整支持(Unity 2023+)
编辑器UI不支持编辑器首选系统
世界空间UI表现优异(World Space Canvas)支持有限
动画/补间DOTween、Animator、LeanTweenUSS过渡、C#操作
样式设置逐元素手动设置USS样式表(类CSS)
性能多画布场景下Draw Call开销大保留模式,Draw Call开销更低
学习曲线Unity开发者上手难度低难度较高(类Web范式)
VR/AR支持完善世界空间支持有限
建议: 将UGUI用于世界空间UI、VR/AR以及需要最大程度兼容资源商店的项目。将UI Toolkit用于编辑器扩展、复杂的数据驱动UI(列表、树形结构)以及Unity 2023+版本的项目。

UGUI (Canvas-Based UI)

UGUI(基于画布的UI)

Canvas Setup

画布设置

Render ModeUse ForNotes
Screen Space - OverlayHUD, menusAlways on top, no camera needed
Screen Space - CameraPost-processing on UIAssign render camera
World SpaceIn-game signs, health barsSet event camera for interaction
Performance rule: Separate static and dynamic UI into different Canvases. A Canvas rebuilds ALL children when any child changes.
渲染模式适用场景注意事项
Screen Space - OverlayHUD、菜单始终置于顶层,无需相机
Screen Space - Camera带后处理的UI需指定渲染相机
World Space游戏内标识、血条设置交互用的事件相机
性能规则: 将静态UI与动态UI拆分到不同画布中。当画布内任意子物体变化时,整个画布的所有子物体都会重建。

RectTransform Anchoring

RectTransform锚点设置

bash
Anchor presets control how elements resize with parent:
- Stretch-Stretch: Element fills parent (full-screen backgrounds)
- Center-Center: Fixed size at center (popup dialogs)
- Bottom-Left: Fixed corner position (minimap)
- Top-Stretch: Stretches horizontally, fixed at top (nav bar)
Set anchors via the RectTransform anchor preset widget (hold Alt+Shift to also set pivot and position). Use
anchorMin
,
anchorMax
,
offsetMin
,
offsetMax
in code.
bash
锚点预设控制元素如何随父物体调整大小:
- Stretch-Stretch:元素填充父物体(全屏背景)
- Center-Center:固定尺寸居中(弹窗对话框)
- Bottom-Left:固定在左下角(小地图)
- Top-Stretch:水平拉伸,固定在顶部(导航栏)
通过RectTransform锚点预设控件设置锚点(按住Alt+Shift可同时设置枢轴和位置)。在代码中可使用
anchorMin
anchorMax
offsetMin
offsetMax
属性。

Layout Components

布局组件

ComponentPurpose
HorizontalLayoutGroup
Arrange children left-to-right
VerticalLayoutGroup
Arrange children top-to-bottom
GridLayoutGroup
Grid arrangement (inventory slots)
LayoutElement
Override min/preferred/flexible size
ContentSizeFitter
Auto-resize to content
AspectRatioFitter
Maintain aspect ratio
Disable
Layout.childForceExpandWidth/Height
to prevent unwanted stretching.
组件用途
HorizontalLayoutGroup
子物体从左到右排列
VerticalLayoutGroup
子物体从上到下排列
GridLayoutGroup
网格排列(背包格子)
LayoutElement
覆盖最小/首选/灵活尺寸
ContentSizeFitter
根据内容自动调整尺寸
AspectRatioFitter
保持宽高比
禁用
Layout.childForceExpandWidth/Height
可避免不必要的拉伸。

Event Handling

事件处理

csharp
using UnityEngine.UI;
using TMPro;

[SerializeField] Button startButton;
[SerializeField] TMP_InputField nameField;
[SerializeField] Slider volumeSlider;

void OnEnable()
{
    startButton.onClick.AddListener(OnStartClicked);
    nameField.onEndEdit.AddListener(OnNameChanged);
    volumeSlider.onValueChanged.AddListener(OnVolumeChanged);
}

void OnDisable()
{
    startButton.onClick.RemoveListener(OnStartClicked);
    nameField.onEndEdit.RemoveListener(OnNameChanged);
    volumeSlider.onValueChanged.RemoveListener(OnVolumeChanged);
}
Always
RemoveListener
in
OnDisable
to prevent leaks and ghost references.
csharp
using UnityEngine.UI;
using TMPro;

[SerializeField] Button startButton;
[SerializeField] TMP_InputField nameField;
[SerializeField] Slider volumeSlider;

void OnEnable()
{
    startButton.onClick.AddListener(OnStartClicked);
    nameField.onEndEdit.AddListener(OnNameChanged);
    volumeSlider.onValueChanged.AddListener(OnVolumeChanged);
}

void OnDisable()
{
    startButton.onClick.RemoveListener(OnStartClicked);
    nameField.onEndEdit.RemoveListener(OnNameChanged);
    volumeSlider.onValueChanged.RemoveListener(OnVolumeChanged);
}
务必在
OnDisable
中移除监听器,以避免内存泄漏和无效引用。

TextMeshPro

TextMeshPro

Always use TextMeshPro (
TMP_Text
,
TextMeshProUGUI
) over legacy
Text
. Import TMP Essentials when prompted. Use rich text tags:
<color=#FF0000>
,
<b>
,
<size=24>
,
<sprite=0>
for inline icons.
始终使用TextMeshPro(
TMP_Text
TextMeshProUGUI
)替代旧版
Text
。按提示导入TMP Essentials资源包。使用富文本标签:
<color=#FF0000>
<b>
<size=24>
<sprite=0>
用于插入内联图标。

UI Toolkit (USS/UXML)

UI Toolkit(USS/UXML)

Architecture

架构

text
UI Toolkit Stack:
  UXML  -- Structure (like HTML)
  USS   -- Styling (like CSS)
  C#    -- Logic (like JavaScript)
text
UI Toolkit 栈:
  UXML  -- 结构(类似HTML)
  USS   -- 样式(类似CSS)
  C#    -- 逻辑(类似JavaScript)

UXML Structure

UXML结构

xml
<ui:UXML xmlns:ui="UnityEngine.UIElements">
    <ui:VisualElement class="container">
        <ui:Label text="Player Stats" class="title" />
        <ui:ProgressBar name="health-bar" title="HP" high-value="100" />
        <ui:Button name="attack-btn" text="Attack" class="action-btn" />
        <ui:ListView name="inventory-list" />
    </ui:VisualElement>
</ui:UXML>
xml
<ui:UXML xmlns:ui="UnityEngine.UIElements">
    <ui:VisualElement class="container">
        <ui:Label text="Player Stats" class="title" />
        <ui:ProgressBar name="health-bar" title="HP" high-value="100" />
        <ui:Button name="attack-btn" text="Attack" class="action-btn" />
        <ui:ListView name="inventory-list" />
    </ui:VisualElement>
</ui:UXML>

USS Styling

USS样式设置

css
.container {
    flex-direction: column;
    padding: 10px;
    background-color: rgba(0, 0, 0, 0.8);
    border-radius: 8px;
}

.title {
    font-size: 24px;
    color: white;
    -unity-font-style: bold;
    margin-bottom: 10px;
}

.action-btn {
    height: 40px;
    background-color: #4CAF50;
    color: white;
    border-radius: 4px;
    transition-duration: 0.2s;
}

.action-btn:hover {
    background-color: #66BB6A;
    scale: 1.05 1.05;
}
Key USS differences from CSS: use
-unity-
prefix for Unity-specific properties. Flexbox is the layout model (default
flex-direction: column
). Use
transition-duration
,
transition-property
for animations.
css
.container {
    flex-direction: column;
    padding: 10px;
    background-color: rgba(0, 0, 0, 0.8);
    border-radius: 8px;
}

.title {
    font-size: 24px;
    color: white;
    -unity-font-style: bold;
    margin-bottom: 10px;
}

.action-btn {
    height: 40px;
    background-color: #4CAF50;
    color: white;
    border-radius: 4px;
    transition-duration: 0.2s;
}

.action-btn:hover {
    background-color: #66BB6A;
    scale: 1.05 1.05;
}
USS与CSS的核心区别:Unity专属属性需添加
-unity-
前缀。布局模型采用Flexbox(默认
flex-direction: column
)。使用
transition-duration
transition-property
实现动画效果。

C# Integration

C#集成

csharp
[RequireComponent(typeof(UIDocument))]
public class StatsUI : MonoBehaviour
{
    void OnEnable()
    {
        var root = GetComponent<UIDocument>().rootVisualElement;
        var healthBar = root.Q<ProgressBar>("health-bar");
        var attackBtn = root.Q<Button>("attack-btn");
        var inventory = root.Q<ListView>("inventory-list");

        attackBtn.RegisterCallback<ClickEvent>(OnAttack);
        healthBar.value = player.Health;

        // ListView binding
        inventory.makeItem = () => new Label();
        inventory.bindItem = (element, index) =>
            ((Label)element).text = items[index].Name;
        inventory.itemsSource = items;
    }
}
Query elements with
Q<T>("name")
or
Q<T>(className: "class")
. Register callbacks with
RegisterCallback<EventType>
. Always query from
rootVisualElement
.
csharp
[RequireComponent(typeof(UIDocument))]
public class StatsUI : MonoBehaviour
{
    void OnEnable()
    {
        var root = GetComponent<UIDocument>().rootVisualElement;
        var healthBar = root.Q<ProgressBar>("health-bar");
        var attackBtn = root.Q<Button>("attack-btn");
        var inventory = root.Q<ListView>("inventory-list");

        attackBtn.RegisterCallback<ClickEvent>(OnAttack);
        healthBar.value = player.Health;

        // ListView绑定
        inventory.makeItem = () => new Label();
        inventory.bindItem = (element, index) =>
            ((Label)element).text = items[index].Name;
        inventory.itemsSource = items;
    }
}
使用
Q<T>("name")
Q<T>(className: "class")
查询元素。通过
RegisterCallback<EventType>
注册回调。务必从
rootVisualElement
开始查询元素。

Data Binding (Unity 2023.2+)

数据绑定(Unity 2023.2+)

csharp
// Runtime data binding with [CreateProperty] and INotifyBindablePropertyChanged
public class PlayerData : INotifyBindablePropertyChanged
{
    public event EventHandler<BindablePropertyChangedEventArgs> propertyChanged;
    private int _health;

    [CreateProperty]
    public int Health
    {
        get => _health;
        set { _health = value; Notify(); }
    }
    void Notify([CallerMemberName] string prop = "")
        => propertyChanged?.Invoke(this, new BindablePropertyChangedEventArgs(prop));
}
Bind in UXML with
binding-path="Health"
or in C# with
element.SetBinding("value", new DataBinding { dataSourcePath = ... })
.
csharp
// 使用[CreateProperty]和INotifyBindablePropertyChanged实现运行时数据绑定
public class PlayerData : INotifyBindablePropertyChanged
{
    public event EventHandler<BindablePropertyChangedEventArgs> propertyChanged;
    private int _health;

    [CreateProperty]
    public int Health
    {
        get => _health;
        set { _health = value; Notify(); }
    }
    void Notify([CallerMemberName] string prop = "")
        => propertyChanged?.Invoke(this, new BindablePropertyChangedEventArgs(prop));
}
在UXML中通过
binding-path="Health"
绑定,或在C#中通过
element.SetBinding("value", new DataBinding { dataSourcePath = ... })
绑定。

Common UI Patterns

常见UI模式

PatternUGUI ApproachUI Toolkit Approach
Popup dialogEnable/disable child panelAdd/remove from visual tree
Scroll listScrollRect + VerticalLayoutGroupListView (virtualized)
Drag-and-dropIBeginDragHandler, IDragHandler, IEndDragHandlerPointerManipulator
Tab systemToggle group + panelsRadioButtonGroup + display toggling
TooltipFollow cursor panelManipulator + VisualElement positioning
Screen fadeCanvasGroup.alpha tweenUSS opacity transition
模式UGUI实现方式UI Toolkit实现方式
弹窗对话框启用/禁用子面板在视觉树中添加/移除元素
滚动列表ScrollRect + VerticalLayoutGroupListView(虚拟化)
拖拽功能IBeginDragHandler、IDragHandler、IEndDragHandlerPointerManipulator
标签系统Toggle组 + 面板RadioButtonGroup + 显示切换
提示框跟随光标面板Manipulator + VisualElement定位
屏幕淡入淡出CanvasGroup.alpha补间USS透明度过渡

Additional Resources

额外资源

Reference Files

参考文档

  • references/ugui-patterns.md
    -- Advanced UGUI patterns: scroll optimization, dynamic layouts, world-space interaction, localization, safe area handling, screen adaptation
  • references/ui-toolkit-advanced.md
    -- Custom controls, ListView/TreeView mastery, custom manipulators, editor UI integration, theming, responsive layouts
  • references/ugui-patterns.md
    -- UGUI进阶模式:滚动优化、动态布局、世界空间交互、本地化、安全区域处理、屏幕适配
  • references/ui-toolkit-advanced.md
    -- 自定义控件、ListView/TreeView精通、自定义操作器、编辑器UI集成、主题设置、响应式布局