godot-autoload-architecture by thedivergentai/gd-agentic-skills
npx skills add https://github.com/thedivergentai/gd-agentic-skills --skill godot-autoload-architectureAutoLoad 是 Godot 的单例模式,允许脚本在整个项目生命周期内全局访问。本技能指导如何实现健壮、可维护的单例架构。
使用 static var 实现高性能的全局状态,无需依赖 SceneTree 的存在。
健壮的场景切换逻辑,处理延迟释放和根层级管理。
用于验证和调试单例初始化顺序的诊断工具。
用于解耦不同系统(成就、统计、游戏事件)的集中式信号路由器。
用于在 change_scene_to_file() 后必须保留的数据的模式(库存、设置)。
广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
内存高效的单例模式,按需实例化而非在启动时实例化。
基于 CanvasLayer 的调试覆盖层,可从任何游戏上下文访问。
用于多个单例之间通信的专家规则和安全检查。
使用 Mutex 和 call_deferred 从后台线程安全地访问全局数据。
验证工具,确保在尝试访问前 AutoLoad 已正确注册。
_init() 中访问 AutoLoad — AutoLoad 是按顺序初始化的。在 _init() 中访问一个 AutoLoad 可能会发现空引用 [12]。_ready() 中修改单例的大小或子节点 — 如果多个单例在启动期间互相引用对方的树,可能会导致布局/排序错误。Parent.method() 调用 — AutoLoad 位于根节点。它们是最终的“顶层”。使用信号与活动场景通信。_process() 或信号,请改用 class_name 脚本中的 static var [7]。get_tree().current_scene 在 _ready() 中是准确的 — 在 AutoLoad 中,活动场景可能仍在初始化。通过 get_tree().root.get_child(-1) 访问它 [6]。process_mode 配置 — 如果你的全局控制台或音乐管理器需要在游戏暂停时工作,请设置 process_mode = PROCESS_MODE_ALWAYS。好的用例:
避免使用 AutoLoad 的情况:
示例:GameManager.gd
extends Node
# 全局事件信号
signal game_started
signal game_paused(is_paused: bool)
signal player_died
# 全局状态
var score: int = 0
var current_level: int = 1
var is_paused: bool = false
func _ready() -> void:
# 初始化 autoload 状态
print("GameManager initialized")
func start_game() -> void:
score = 0
current_level = 1
game_started.emit()
func pause_game(paused: bool) -> void:
is_paused = paused
get_tree().paused = paused
game_paused.emit(paused)
func add_score(points: int) -> void:
score += points
项目 → 项目设置 → AutoLoad
game_manager.gdGameManager(PascalCase 命名约定)在 project.godot 中验证:
[autoload]
GameManager="*res://autoloads/game_manager.gd"
* 前缀使其在启动时立即激活。
extends Node2D
func _ready() -> void:
# 访问单例
GameManager.connect("game_paused", _on_game_paused)
GameManager.start_game()
func _on_button_pressed() -> void:
GameManager.add_score(100)
func _on_game_paused(is_paused: bool) -> void:
print("Game paused: ", is_paused)
# ✅ 好
var score: int = 0
# ❌ 不好
var score = 0
# ✅ 好 - 允许解耦的监听器
signal score_changed(new_score: int)
func add_score(points: int) -> void:
score += points
score_changed.emit(score)
# ❌ 不好 - 紧耦合
func add_score(points: int) -> void:
score += points
ui.update_score(score) # 不要直接调用 UI
res://autoloads/
game_manager.gd
audio_manager.gd
scene_transitioner.gd
save_manager.gd
# scene_transitioner.gd
extends Node
signal scene_changed(scene_path: String)
func change_scene(scene_path: String) -> void:
# 淡出效果(可选)
await get_tree().create_timer(0.3).timeout
get_tree().change_scene_to_file(scene_path)
scene_changed.emit(scene_path)
enum GameState { MENU, PLAYING, PAUSED, GAME_OVER }
var current_state: GameState = GameState.MENU
func change_state(new_state: GameState) -> void:
current_state = new_state
match current_state:
GameState.MENU:
# 加载菜单
pass
GameState.PLAYING:
get_tree().paused = false
GameState.PAUSED:
get_tree().paused = true
GameState.GAME_OVER:
# 显示游戏结束画面
pass
# 一次性预加载重型资源
const PLAYER_SCENE := preload("res://scenes/player.tscn")
const EXPLOSION_EFFECT := preload("res://effects/explosion.tscn")
func spawn_player(position: Vector2) -> Node2D:
var player := PLAYER_SCENE.instantiate()
player.global_position = position
return player
由于 AutoLoad 总是被加载,避免在 _ready() 中进行繁重的初始化。使用惰性初始化或显式的初始化函数:
var _initialized: bool = false
func initialize() -> void:
if _initialized:
return
_initialized = true
# 繁重的设置放在这里
每周安装次数
77
代码仓库
GitHub 星标数
59
首次出现
2026年2月10日
安全审计
安装于
gemini-cli74
codex73
opencode72
github-copilot71
kimi-cli70
amp70
AutoLoads are Godot's singleton pattern, allowing scripts to be globally accessible throughout the project lifecycle. This skill guides implementing robust, maintainable singleton architectures.
Using static var for high-performance global state that doesn't need SceneTree presence.
Robust scene transitioning logic that handles deferred freeing and root-level management.
Diagnostic utility for verifying and debugging the initialization sequence of Singletons.
Centralized signal router for decoupling disparate systems (Achievements, Stats, Game Events).
Pattern for data that must survive change_scene_to_file() (Inventory, Settings).
Memory-efficient singleton pattern that instantiates on-demand rather than at boot.
CanvasLayer-based debug overlay accessible from any game context.
Expert rules and safety checks for communication between multiple Singletons.
Using Mutex and call_deferred to safely access global data from background threads.
Validation utility to ensure Autoloads are correctly registered before attempting access.
_init() — AutoLoads are initialized sequentially. Accessing one in _init() may find a null reference [12]._ready() — If multiple Singletons refer to each other's trees during boot, it can cause layout/sorting errors.Parent.method() calls from an Autoload — Autoloads sit at the root. They are the ultimate "top". Use signals to talk to the active scene._process() or signals, use a static var in a class_name script instead [7].Good Use Cases:
Avoid AutoLoads For:
Example: GameManager.gd
extends Node
# Signals for global events
signal game_started
signal game_paused(is_paused: bool)
signal player_died
# Global state
var score: int = 0
var current_level: int = 1
var is_paused: bool = false
func _ready() -> void:
# Initialize autoload state
print("GameManager initialized")
func start_game() -> void:
score = 0
current_level = 1
game_started.emit()
func pause_game(paused: bool) -> void:
is_paused = paused
get_tree().paused = paused
game_paused.emit(paused)
func add_score(points: int) -> void:
score += points
Project → Project Settings → AutoLoad
game_manager.gdGameManager (PascalCase convention)Verify inproject.godot:
[autoload]
GameManager="*res://autoloads/game_manager.gd"
The * prefix makes it active immediately on startup.
extends Node2D
func _ready() -> void:
# Access the singleton
GameManager.connect("game_paused", _on_game_paused)
GameManager.start_game()
func _on_button_pressed() -> void:
GameManager.add_score(100)
func _on_game_paused(is_paused: bool) -> void:
print("Game paused: ", is_paused)
# ✅ Good
var score: int = 0
# ❌ Bad
var score = 0
# ✅ Good - allows decoupled listeners
signal score_changed(new_score: int)
func add_score(points: int) -> void:
score += points
score_changed.emit(score)
# ❌ Bad - tight coupling
func add_score(points: int) -> void:
score += points
ui.update_score(score) # Don't directly call UI
res://autoloads/
game_manager.gd
audio_manager.gd
scene_transitioner.gd
save_manager.gd
# scene_transitioner.gd
extends Node
signal scene_changed(scene_path: String)
func change_scene(scene_path: String) -> void:
# Fade out effect (optional)
await get_tree().create_timer(0.3).timeout
get_tree().change_scene_to_file(scene_path)
scene_changed.emit(scene_path)
enum GameState { MENU, PLAYING, PAUSED, GAME_OVER }
var current_state: GameState = GameState.MENU
func change_state(new_state: GameState) -> void:
current_state = new_state
match current_state:
GameState.MENU:
# Load menu
pass
GameState.PLAYING:
get_tree().paused = false
GameState.PAUSED:
get_tree().paused = true
GameState.GAME_OVER:
# Show game over screen
pass
# Preload heavy resources once
const PLAYER_SCENE := preload("res://scenes/player.tscn")
const EXPLOSION_EFFECT := preload("res://effects/explosion.tscn")
func spawn_player(position: Vector2) -> Node2D:
var player := PLAYER_SCENE.instantiate()
player.global_position = position
return player
Since AutoLoads are always loaded, avoid heavy initialization in_ready(). Use lazy initialization or explicit init functions:
var _initialized: bool = false
func initialize() -> void:
if _initialized:
return
_initialized = true
# Heavy setup here
Weekly Installs
77
Repository
GitHub Stars
59
First Seen
Feb 10, 2026
Security Audits
Gen Agent Trust HubPassSocketPassSnykPass
Installed on
gemini-cli74
codex73
opencode72
github-copilot71
kimi-cli70
amp70
Android 整洁架构指南:模块化设计、依赖注入与数据层实现
1,400 周安装
get_tree().current_scene is accurate in _ready() — In Autoloads, the active scene might still be initializing. Access it via get_tree().root.get_child(-1) [6].process_mode configuration — If your global console or music manager needs to work while the game is paused, set process_mode = PROCESS_MODE_ALWAYS.