Community程式設計與開發github.com

ziva-sh/godot-場景組織

結構化 Godot 4 專案,讓場景保持可重用且參考不中斷——何時建立場景、腳本或資源,使用 @export PackedScene 進行實例化、preload 與 load、@onready 與 %UniqueName 節點存取、_ready 順序、群組、自動載入、class_name,以及 Godot 4.4 新增的 .uid 檔案與資源 UID,包括如何安全地移動或重新命名檔案。在建立新場景或腳本、決定節點如何相互查找、加入全域狀態或事件匯流排、移動或重新命名檔案時使用。

godot-場景組織 是什麼?

這個技能提供了一套完整的指南,用於在 Godot 4 中組織場景和資源,確保專案的可維護性和可重用性。它詳細說明了何時使用場景(`.tscn`)、腳本(`.gd`)或繼承自 Resource 的腳本,並強調每個場景都應該能夠獨立運作,並透過 `@export` 或信號與外部互動,而不是依賴脆弱的 `get_parent()` 鏈。技能深入探討了場景實例化的三種方式:`@export var PackedScene`(編輯器可設定,檔案移動後仍有效)、`preload()`(編譯時解析,適合固定資源)和 `load()`(執行時解析,適合動態路徑),並示範了使用 `instantiate()` 與 `queue_free()` 進行生命週期管理。對於節點查找,它推薦使用 `%UniqueName` 和 `@onready` 來建立健壯的參考,並解釋了 `_ready()` 的父子執行順序。此外,技能還涵蓋了群組、自動載入(autoload)、`class_name` 全域型別註冊,以及 Godot 4.4 中引入的 `.uid` 檔案系統,強調了在檔案移動或重新命名時必須一併移動 `.uid` 檔案的重要性,以避免參考中斷。最後,它列出了一些常見的錯誤模式,例如向上爬取節點樹、在 `_init()` 中使用節點、硬編碼路徑以及過度使用自動載入。

相容平台~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):沒有節點的資料,例如物品屬性、波次定義或存檔資料,可在檢查器中編輯並儲存為 .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("找不到關卡:" + path)
		return
	get_tree().change_scene_to_packed(packed)
  • @export var x: PackedScene 在檢查器中設定,並且在檔案移動後仍然有效,因為場景會儲存參考。
  • 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 = "主選單"
	start_button.grab_focus()
	if optional_hint:
		optional_hint.visible = false
	music.play()
  • $Path 是 get_node("Path"),相對於此節點。如果節點遺失則會報錯;對於可選節點,請使用 get_node_or_null()。
  • %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.global_position)

在 Godot 4 中,call_group() 會立即執行。若想延遲執行,請使用 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 外部(Shell、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

相關技能