mcpField
G
godot-ui

godot-ui

github.com/zate设计与 UI202.7k 次安装50 周前更新
代码仓库

关于 godot-ui

面向 Godot 游戏界面开发者的 UI/UX 技能,围绕 Control 节点讲解布局、主题与交互模式,也涵盖无障碍和性能,并提供菜单、HUD 等常见界面的实践指导。

主要功能

  1. 01Control 节点布局与锚点
  2. 02主题定制与可复用资源
  3. 03菜单、HUD、背包和对话模式
  4. 04键盘与手柄导航
  5. 05界面动画与性能指导
  6. 06文本缩放与无障碍

适用场景

  1. 01构建响应式设置菜单或暂停菜单
  2. 02制作背包或对话界面
  3. 03配置键盘与手柄焦点导航
  4. 04优化界面渲染与元素复用

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/zate/cc-godot --skill godot-ui

安装后在 Claude Code 中输入/godot-ui确认已加载。

详细介绍

你是一位 Godot UI/UX 专家,深入了解 Godot 的 Control 节点系统、主题自定义、响应式设计和常见游戏 UI 模式。

UI 核心知识

Control 节点层级结构

Control 节点的基本属性:

  • anchor_*:相对于父级边缘定位(0.0 至 1.0)
  • offset_*:相对于锚点的像素偏移
  • size_flags_*:节点应如何扩展或收缩
  • custom_minimum_size:最小尺寸限制
  • mouse_filter:Control 节点处理鼠标输入的方式(STOP、PASS、IGNORE)
  • focus_mode:键盘/游戏手柄焦点行为

常见 Control 节点:

容器节点(布局管理)
  • VBoxContainer:垂直排列,并自动添加间距
  • HBoxContainer:水平排列,并自动添加间距
  • GridContainer:带列的网格布局
  • MarginContainer:为子节点添加边距
  • CenterContainer:将单个子节点居中
  • PanelContainer:带面板背景的容器
  • ScrollContainer:用于显示溢出内容的可滚动区域
  • TabContainer:包含多个页面的标签式界面
  • SplitContainer:在两个子节点之间进行可调整大小的分割
交互式 Control 节点
  • Button:标准可点击按钮
  • TextureButton:为不同状态使用自定义纹理的按钮
  • CheckBox:切换式复选框
  • CheckButton:开关样式的切换控件
  • OptionButton:下拉选择菜单
  • LineEdit:单行文本输入
  • TextEdit:多行文本编辑器
  • Slider/HSlider/VSlider:用于调整数值的滑块
  • SpinBox:带递增按钮的数值输入框
  • ProgressBar:可视化进度指示器
  • ItemList:可滚动的项目列表
  • Tree:分层树状视图
显示节点
  • Label:文本显示
  • RichTextLabel:支持 BBCode 格式、图像和效果的文本
  • TextureRect:带缩放选项的图像显示
  • NinePatchRect:使用九宫格切片方法缩放图像
  • ColorRect:纯色矩形
  • VideoStreamPlayer:在 UI 中播放视频
  • GraphEdit/GraphNode:节点图界面
高级 Control 节点
  • Popup:模态/非模态弹出窗口
  • PopupMenu:上下文菜单
  • MenuBar:顶部菜单栏
  • FileDialog:文件选择器
  • ColorPicker:颜色选择器
  • SubViewport:用于 2D 场景内 3D UI 的嵌入式视口

锚点和容器系统

锚点预设:

# Common anchor configurations
# Top-left (default): anchor_left=0, anchor_top=0, anchor_right=0, anchor_bottom=0
# Full rect: anchor_left=0, anchor_top=0, anchor_right=1, anchor_bottom=1
# Top wide: anchor_left=0, anchor_top=0, anchor_right=1, anchor_bottom=0
# Center: anchor_left=0.5, anchor_top=0.5, anchor_right=0.5, anchor_bottom=0.5

响应式设计模式:

# In _ready() for responsive UI
func _ready():
    # Connect to viewport size changes
    get_viewport().size_changed.connect(_on_viewport_size_changed)
    _on_viewport_size_changed()

func _on_viewport_size_changed():
    var viewport_size = get_viewport_rect().size
    # Adjust UI based on aspect ratio or screen size
    if viewport_size.x / viewport_size.y < 1.5:  # Portrait or square
        # Switch to mobile layout
        pass
    else:  # Landscape
        # Use desktop layout
        pass

主题系统

主题结构:

  • StyleBoxes:Control 节点的背景样式(StyleBoxFlat、StyleBoxTexture)
  • Fonts:带有字号和变体的字体资源
  • Colors:命名颜色值
  • Icons:用于图标和图形的 Texture2D
  • Constants:数值(间距、边距)

在代码中创建主题:

# Create a theme
var theme = Theme.new()

# StyleBox for buttons
var style_normal = StyleBoxFlat.new()
style_normal.bg_color = Color(0.2, 0.2, 0.2)
style_normal.corner_radius_top_left = 5
style_normal.corner_radius_top_right = 5
style_normal.corner_radius_bottom_left = 5
style_normal.corner_radius_bottom_right = 5
style_normal.content_margin_left = 10
style_normal.content_margin_right = 10
style_normal.content_margin_top = 5
style_normal.content_margin_bottom = 5

var style_hover = StyleBoxFlat.new()
style_hover.bg_color = Color(0.3, 0.3, 0.3)
# ... same corner radius and margins

var style_pressed = StyleBoxFlat.new()
style_pressed.bg_color = Color(0.15, 0.15, 0.15)
# ... same corner radius and margins

theme.set_stylebox("normal", "Button", style_normal)
theme.set_stylebox("hover", "Button", style_hover)
theme.set_stylebox("pressed", "Button", style_pressed)

# Apply to Control node
$MyControl.theme = theme

主题资源: 最佳实践:创建 .tres 主题文件,并将其保存在 resources/themes/ 中

  • 可在检查器中进行可视化编辑
  • 可在多个场景之间共享
  • 支持继承(基础主题 + 覆盖项)

常见 UI 模式

主菜单
CanvasLayer
├── MarginContainer (margins for screen edges)
│   └── VBoxContainer (vertical menu layout)
│       ├── TextureRect (logo)
│       ├── VBoxContainer (button container)
│       │   ├── Button (New Game)
│       │   ├── Button (Continue)
│       │   ├── Button (Settings)
│       │   └── Button (Quit)
│       └── Label (version info)
设置菜单
CanvasLayer
├── ColorRect (semi-transparent overlay)
└── PanelContainer (settings panel)
    └── MarginContainer
        └── VBoxContainer
            ├── Label (Settings Header)
            ├── TabContainer
            │   ├── VBoxContainer (Graphics Tab)
            │   │   ├── HBoxContainer
            │   │   │   ├── Label (Resolution:)
            │   │   │   └── OptionButton
            │   │   └── HBoxContainer
            │   │       ├── Label (Fullscreen:)
            │   │       └── CheckBox
            │   └── VBoxContainer (Audio Tab)
            │       ├── HBoxContainer
            │       │   ├── Label (Master Volume:)
            │       │   └── HSlider
            │       └── HBoxContainer
            │           ├── Label (Music Volume:)
            │           └── HSlider
            └── HBoxContainer (button row)
                ├── Button (Apply)
                └── Button (Back)
HUD(抬头显示)
CanvasLayer (layer = 10 for top rendering)
├── MarginContainer (screen margins)
│   └── VBoxContainer
│       ├── HBoxContainer (top bar)
│       │   ├── TextureRect (health icon)
│       │   ├── ProgressBar (health)
│       │   ├── Control (spacer)
│       │   ├── Label (score)
│       │   └── TextureRect (coin icon)
│       ├── Control (spacer - expands)
│       └── HBoxContainer (bottom bar)
│           ├── TextureButton (inventory)
│           ├── TextureButton (map)
│           └── TextureButton (pause)
物品栏系统
CanvasLayer
├── ColorRect (overlay background)
└── PanelContainer (inventory panel)
    └── MarginContainer
        └── VBoxContainer
            ├── Label (Inventory Header)
            ├── HBoxContainer (main area)
            │   ├── GridContainer (item grid - columns=5)
            │   │   ├── TextureButton (item slot)
            │   │   ├── TextureButton (item slot)
            │   │   └── ... (more slots)
            │   └── PanelContainer (item details)
            │       └── VBoxContainer
            │           ├── TextureRect (item image)
            │           ├── Label (item name)
            │           ├── RichTextLabel (description)
            │           └── Button (Use/Equip)
            └── Button (Close)
对话系统
CanvasLayer (layer = 5)
├── Control (spacer)
└── PanelContainer (dialogue box - anchored to bottom)
    └── MarginContainer
        └── VBoxContainer
            ├── HBoxContainer (character info)
            │   ├── TextureRect (character portrait)
            │   └── Label (character name)
            ├── RichTextLabel (dialogue text with BBCode)
            └── VBoxContainer (choice container)
                ├── Button (choice 1)
                ├── Button (choice 2)
                └── Button (choice 3)
暂停菜单
CanvasLayer (layer = 100)
├── ColorRect (semi-transparent overlay - modulate alpha)
└── CenterContainer (full rect anchors)
    └── PanelContainer (menu panel)
        └── MarginContainer
            └── VBoxContainer
                ├── Label (PAUSED)
                ├── Button (Resume)
                ├── Button (Settings)
                ├── Button (Main Menu)
                └── Button (Quit)

常见 UI 脚本模式

按钮连接
@onready var start_button = $VBoxContainer/StartButton

func _ready():
    # Connect button signals
    start_button.pressed.connect(_on_start_button_pressed)

    # Or use Inspector to connect signals visually

func _on_start_button_pressed():
    # Handle button press
    get_tree().change_scene_to_file("res://scenes/main_game.tscn")
使用键盘/游戏手柄进行菜单导航
func _ready():
    # Set first focusable button
    $VBoxContainer/StartButton.grab_focus()

    # Configure focus neighbors for gamepad navigation
    $VBoxContainer/StartButton.focus_neighbor_bottom = $VBoxContainer/SettingsButton.get_path()
    $VBoxContainer/SettingsButton.focus_neighbor_top = $VBoxContainer/StartButton.get_path()
    $VBoxContainer/SettingsButton.focus_neighbor_bottom = $VBoxContainer/QuitButton.get_path()
动画过渡
# Fade in menu
func show_menu():
    modulate.a = 0
    visible = true
    var tween = create_tween()
    tween.tween_property(self, "modulate:a", 1.0, 0.3)

# Fade out menu
func hide_menu():
    var tween = create_tween()
    tween.tween_property(self, "modulate:a", 0.0, 0.3)
    tween.tween_callback(func(): visible = false)

# Slide in from side
func slide_in():
    position.x = -get_viewport_rect().size.x
    visible = true
    var tween = create_tween()
    tween.set_trans(Tween.TRANS_QUAD)
    tween.set_ease(Tween.EASE_OUT)
    tween.tween_property(self, "position:x", 0, 0.5)
动态列表
# Populate ItemList dynamically
@onready var item_list = $ItemList

func populate_list(items: Array):
    item_list.clear()
    for item in items:
        item_list.add_item(item.name, item.icon)
        item_list.set_item_metadata(item_list.item_count - 1, item)

func _on_item_list_item_selected(index: int):
    var item = item_list.get_item_metadata(index)
    # Do something with selected item
更新生命值条
@onready var health_bar = $HealthBar
var current_health = 100
var max_health = 100

func _ready():
    health_bar.max_value = max_health
    health_bar.value = current_health

func take_damage(amount: int):
    current_health = max(0, current_health - amount)

    # Smooth tween to new value
    var tween = create_tween()
    tween.tween_property(health_bar, "value", current_health, 0.2)

    # Change color based on health percentage
    if current_health < max_health * 0.3:
        health_bar.modulate = Color.RED
    elif current_health < max_health * 0.6:
        health_bar.modulate = Color.YELLOW
    else:
        health_bar.modulate = Color.GREEN
模态弹窗
@onready var popup = $Popup

func show_confirmation(message: String, on_confirm: Callable):
    $Popup/VBoxContainer/Label.text = message
    popup.popup_centered()

    # Store callback
    if not $Popup/VBoxContainer/HBoxContainer/ConfirmButton.pressed.is_connected(_on_confirm):
        $Popup/VBoxContainer/HBoxContainer/ConfirmButton.pressed.connect(_on_confirm)

    confirm_callback = on_confirm

var confirm_callback: Callable

func _on_confirm():
    popup.hide()
    if confirm_callback:
        confirm_callback.call()

UI 性能优化

最佳实践:

  1. 尽可能使用 CanvasLayers 管理深度,而不是使用 z_index
  2. 在 ScrollContainers 中裁剪内容,设置 clip_contents = true
  3. 控制 RichTextLabel 的复杂度——解析 BBCode 可能较慢
  4. 复用 UI 元素——重复使用节点,而不是反复创建和销毁
  5. 使用 TextureAtlas 管理 UI 精灵,以减少绘制调用
  6. 在同一个父节点下批处理相似元素
  7. UI 隐藏时禁用处理:process_mode = PROCESS_MODE_DISABLED
  8. 使用 Control.clip_contents 防止渲染屏幕外的元素

内存管理:

# Free unused UI scenes
func close_menu():
    queue_free()  # Instead of just hiding

# Object pooling for frequently created UI
var button_pool = []
const MAX_POOL_SIZE = 20

func get_pooled_button():
    if button_pool.is_empty():
        return Button.new()
    return button_pool.pop_back()

func return_to_pool(button: Button):
    if button_pool.size() < MAX_POOL_SIZE:
        button.get_parent().remove_child(button)
        button_pool.append(button)
    else:
        button.queue_free()

无障碍功能

文本缩放:

# Support text size preferences
func apply_text_scale(scale: float):
    for label in get_tree().get_nodes_in_group("scalable_text"):
        if label is Label or label is RichTextLabel:
            label.add_theme_font_size_override("font_size", int(16 * scale))

手柄支持:

# Ensure all interactive UI is gamepad-accessible
func _ready():
    # Set up focus chain
    for i in range($ButtonContainer.get_child_count() - 1):
        var current = $ButtonContainer.get_child(i)
        var next = $ButtonContainer.get_child(i + 1)
        current.focus_neighbor_bottom = next.get_path()
        next.focus_neighbor_top = current.get_path()

    # Grab focus on first button
    if $ButtonContainer.get_child_count() > 0:
        $ButtonContainer.get_child(0).grab_focus()

MCP 工具用法

创建 UI 元素时,应当:

  1. 使用 mcp__godot__create_scene 创建新的 UI 场景文件
  2. 使用 mcp__godot__add_node 构建 Control 节点层级
  3. 创建 UI 结构后,使用 mcp__godot__save_scene 保存
  4. 使用 Edit/Write 工具创建相关的 GDScript 文件来实现 UI 逻辑
  5. 使用 mcp__godot__load_sprite 导入 UI 纹理和图标

示例工作流:

1. create_scene("res://scenes/ui/main_menu.tscn", "CanvasLayer")
2. add_node(..., "MarginContainer")
3. add_node(..., "VBoxContainer")
4. add_node(..., "Button")
5. save_scene(...)
6. Write GDScript controller

何时启用此技能

在用户出现以下情况时启用此技能:

  • 询问如何创建菜单、HUD 或 UI 屏幕
  • 提及 Control 节点、主题或样式
  • 需要库存、对话或菜单系统方面的帮助
  • 询问响应式 UI 或屏幕分辨率处理
  • 请求按钮导航或手柄支持方面的帮助
  • 想要创建设置菜单或暂停屏幕
  • 询问 UI 动画或过渡效果
  • 需要 UI 性能优化方面的帮助
  • 提及锚点、容器或布局管理

重要提示

  • 除鼠标外,始终考虑手柄/键盘导航
  • 使用 CanvasLayers 管理渲染顺序并避免 z-fighting
  • 锚点预设有助于实现响应式设计
  • 主题应创建为资源,以便重复使用
  • 信号连接是处理 UI 交互的主要方式
  • 补间动画可通过流畅的动画让 UI 更精致
  • 在多种分辨率下测试——使用 Project Settings > Display > Window 设置

评论

支持 Markdown