Community编程与开发github.com

Godot 场景组织

为 Godot 4 项目提供结构指导,确保场景可复用且引用不中断——涵盖何时创建场景、脚本或资源,如何使用 @export PackedScene、preload 与 load、@onready 与 %UniqueName 节点访问、_ready 顺序、组、自动加载、class_name 以及 Godot 4.4 添加的 .uid 文件,包括如何安全移动或重命名文件。在创建新场景或脚本、决定节点间查找方式、添加全局状态或事件总线、移动或重命名文件时使用。

Godot 场景组织 是什么?

该技能为 Godot 4 项目提供了全面的场景组织指南,帮助开发者保持代码的可维护性和可复用性。它清晰区分了场景 (.tscn)、脚本 (.gd) 和资源脚本 (extends Resource) 的适用场景,强调了场景应能独立运行的设计原则。在节点访问方面,技能详细介绍了 %UniqueName、@onready 和 _ready 顺序的最佳实践,并推荐使用 @export 和信号替代 get_parent() 链。对于资源引用管理,它解释了 PackedScene、preload 和 load 的区别,以及 Godot 4.4 引入的 .uid 文件系统,指导开发者如何安全地移动或重命名文件而不破坏引用。此外,技能还涵盖了组、自动加载、class_name 的合理使用,并指出了常见错误如硬编码路径、在 _init() 中使用节点以及滥用自动加载。该技能适用于在任何支持 Godot 4 的 AI 代理或编辑器(如 Claude Code、Cursor、Codex)中指导场景和脚本的创建与重构。

兼容平台~Claude Code~Codex CLI~Cursor
Part ofMobile
npx skills add https://github.com/ziva-sh/godot-skills/tree/HEAD/skills/godot-scene-organization

Installed? Explore more 编程与开发 skills: steipete/bluebubbles, steipete/eightctl, steipete/blucli · View all 6 →

在你喜欢的 AI 中提问

打开一个已预加载此 Agent Skill 的新对话。

文档

Godot 4 中的场景组织

场景、脚本或资源

  • 场景 (.tscn):一个可复用的节点树,包含自身资源:玩家、敌人、拾取物、UI 面板、关卡。任何需要放置多次或单独运行 (F6) 的内容都应作为场景。
  • 脚本 (.gd):绑定到一个节点的行为。将其附加到所控制场景的根节点,而不是附加到需要深入访问的父节点。
  • 资源脚本(extends Resource 配合 class_name):没有节点的数据,如物品属性、波次定义或存档数据,可在 Inspector 中编辑并保存为 .tres。

场景应能独立运行。如果场景需要来自外部的数据(目标、生成父节点),请使用 @export 或信号,而不是通过 get_parent() 链获取。

实例化场景

extends Node2D

@export var enemy_scene: PackedScene
@export var spawn_parent: Node2D


func spawn_enemy(at: Vector2) -> void:
	var enemy := enemy_scene.instantiate() as Node2D
	enemy.position = at
	spawn_parent.add_child(enemy)


func load_level(path: String) -> void:
	var packed := load(path) as PackedScene
	if packed == null:
		push_error("Level not found: " + path)
		return
	get_tree().change_scene_to_packed(packed)
  • @export var x: PackedScene 在 Inspector 中设置,并在文件移动后仍然有效,因为场景存储了引用。
  • const BULLET := preload("res://bullet.tscn") 在解析时确定:文件缺失会导致解析错误(Preload file "res://bullet.tscn" does not exist.)。适用于固定资源。
  • load() 在运行时解析,如果路径错误则返回 null 并报错。适用于运行时构建的路径。
  • instantiate()(不是 Godot 3 的 instance())返回一个 Node;将其转换为所需类型。使用 queue_free() 释放实例。

查找节点

extends Control

@onready var title: Label = %Title
@onready var start_button: Button = %StartButton
@onready var music: AudioStreamPlayer = $Music
@onready var optional_hint: Label = get_node_or_null("Hint")


func _ready() -> void:
	title.text = "Main menu"
	start_button.grab_focus()
	if optional_hint:
		optional_hint.visible = false
	music.play()
  • $Path 是 get_node("Path") 的简写,相对于当前节点。如果节点不存在会报错;对于可选节点使用 get_node_or_null()。
  • %Name 可访问在同一场景中标记为“Access as Unique Name”(在场景停靠栏中右键单击)的节点,即使节点在场景内重新父级化也能找到。但不能访问实例化的子场景内部的节点:请使用 $Child/%Name,或者当实例本身是唯一节点时使用 %Child/%Name。
  • @onready 变量在 _ready() 之前立即赋值。它们在 _init() 和 _enter_tree() 中为 null。
  • _ready() 在子节点上先于父节点执行,所以父节点的 _ready() 可以使用其子节点,但子节点的 _ready() 不能依赖父节点已就绪。
  • 为 @onready 变量指定类型(: Label),以便解析器检查成员名称;未指定类型的路径上的 $Label.txt 只能在运行时捕获错误。

组

组用于跨场景标记节点。在节点停靠栏(组标签)或代码中添加组,然后查询场景树:

extends Node


func _ready() -> void:
	add_to_group("enemies")


func alert_all_enemies() -> void:
	get_tree().call_group("enemies", "set_alerted", true)
	for enemy in get_tree().get_nodes_in_group("enemies"):
		print(enemy.name)
	var player := get_tree().get_first_node_in_group("player") as Node2D
	if player:
		print("Player at ", player.global_position)

call_group() 在 Godot 4 中立即执行。使用 get_tree().call_group_flags(SceneTree.GROUP_CALL_DEFERRED, ...) 来延迟执行。

自动加载

自动加载是一个在主场景之前创建并跨场景切换保持存在的节点。用于管理跨越单个场景的状态(设置、存档数据、场景切换器)以及仅用于信号的全局事件总线。在“项目设置 > 全局 > 自动加载”中注册;project.godot 中将包含:

[autoload]

Events="*res://autoload/events.gd"

* 表示“已启用”复选框被勾选,因此脚本可以直接使用其名称(Events.player_died.emit())。不要对自动加载使用 free() 或 queue_free()。尽量少用自动加载:任何属于单个关卡的内容都应放在该关卡的场景中。

工具注意事项:godot --check-only --script 不会加载自动加载,因此使用自动加载名称的脚本会在该命令下报告 Identifier not found: Events,即使它正常运行。请通过运行场景来验证此类脚本(请参考 godot-verify 技能)。

class_name

class_name Player 注册一个全局类型,这样其他脚本可以写 var player: Player 并启用成员检查。编辑器会在 .godot/ 中维护一个全局类缓存;如果在编辑器外部添加了 class_name,请先打开编辑器或运行 godot --headless --path . --import,再检查使用它的脚本。

UID、.uid 文件和移动文件

Godot 4 同时通过 UID 和路径引用资源。场景和资源在文件头中存储自己的 UID,导入的资源存储在 .import 文件中,从 Godot 4.4 开始,脚本和着色器会生成一个配套文件(player.gd.uid、water.gdshader.uid)。编辑器中保存的引用如下所示:

[ext_resource type="Script" uid="uid://dxxoiuob4eoum" path="res://game_manager.gd" id="1_feb5d"]

规则:

  • 将 *.uid 文件提交到版本控制。不要将它们添加到 .gitignore。
  • 尽量在 Godot 文件系统停靠栏中移动和重命名文件;它会自动更新引用。
  • 如果在 Godot 外部(终端、IDE、代理)移动脚本或着色器,请同时移动其 .uid 文件,然后打开编辑器或运行 godot --headless --path . --import,以便 UID 缓存学习新路径。在完成扫描之前,仍然使用旧路径并且加载会失败。
  • 如果 .uid 文件被遗留在原地,Godot 会为移动的脚本生成一个新的 UID,之前引用旧 UID 的场景会回退到旧的路径:WARNING: ... invalid UID: uid://... - using text path instead: res://old/path.gd,然后 ERROR: ... referenced non-existent resource。
  • 手动编写 .tscn 时,不要随意编造 uid= 值。省略该属性并使用正确的 path;编辑器会在下次保存场景时添加 UID。
  • 手动编写的 Godot 4 场景以 [gd_scene format=3] 开头,并通过字符串 id 引用资源:[ext_resource type="Script" path="res://main.gd" id="1_main"],然后在节点上使用 script = ExtResource("1_main")。子节点需要 parent="." 或从根节点开始的路径。Godot 4.7 还会在节点上写入 unique_id=;手动编写时省略它。
  • load("uid://...") 和 preload("uid://...") 接受 UID 字符串。从文件系统停靠栏右键菜单复制 UID;ResourceUID.id_to_text(ResourceLoader.get_resource_uid(path)) 可从代码中返回它。

常见错误

  • 使用 get_parent().get_parent() 或绝对路径如 /root/Main/Player 向上查找。应导出引用、使用组或发射信号。
  • 在 _init() 中或节点进入场景树之前使用节点。
  • 在许多脚本中硬编码 res:// 路径,而不是导出 PackedScene。
  • 移动 .gd 文件时未移动其 .uid 文件,或在 git 中忽略 *.uid。
  • 为了方便而将所有内容都设为自动加载。

Individual skills in this repo

This repo contains 4 individual skills — each has its own dedicated page.

ziva-sh/godot-4-api-pitfalls

Fixes Godot 3 APIs and GDScript syntax that AI assistants write into Godot 4 projects, such as KinematicBody2D, move_and_slide(velocity), export, onready, setget, yield, string connect(), instance(), rand_range, the Tween node, File, change_scene, Spatial, rect_* properties, Pool*Array, OS time functions and the old TileMap API. Use when writing, reviewing or porting GDScript for Godot 4.x, when a script fails with "not declared", "not found in base", "was removed in Godot 4" or "is not present

ziva-sh/godot-control-layout

Builds Godot 4 Control UIs that resize instead of clipping or overlapping - containers versus anchors, anchor presets from code, size flags, custom_minimum_size, MarginContainer margins, Label wrapping, mouse_filter, CanvasLayer HUDs, Godot 4 theme override names (add_theme_color_override and friends) and the display/window/stretch settings for multiple resolutions. Use when creating or fixing menus, HUDs, dialogs and inventory screens, when UI elements are cut off, zero-sized, overlapping, igno

ziva-sh/godot-signals

Godot 4 signal patterns and their failure modes - declaring typed signals, emit(), connect() and disconnect() with Callables, bind() and unbind(), lambdas, CONNECT_ONE_SHOT and CONNECT_DEFERRED, await on signals and timers, editor-made connections stored in .tscn files, and the call down, signal up rule. Use when wiring UI buttons, timers, Area2D or body events and custom game events, when replacing Godot 3 connect("signal", self, "method") or yield code, or when debugging "already connected", "

ziva-sh/godot-tilemaplayer

Correct Godot 4.3+ TileMapLayer code - set_cell(coords, source_id, atlas_coords), reading cells and custom data, erasing, coordinate conversion, terrains, tile collision through TileSet physics layers, finding the tile a body hit, and migrating from the deprecated TileMap node and its layer-index API. Use when placing, reading or erasing tiles from GDScript, building a TileSet in code, generating levels procedurally, handling tile collisions, or porting TileMap code from Godot 3 or Godot 4.0 to

相关技能