godot-autoload-architecture
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseAutoLoad Architecture
AutoLoad架构
AutoLoads are Godot's singleton pattern, allowing scripts to be globally accessible throughout the project lifecycle. This skill guides implementing robust, maintainable singleton architectures.
AutoLoad是Godot的单例模式,允许脚本在整个项目生命周期内全局可访问。本指南将指导你实现健壮、可维护的单例架构。
NEVER Do
绝对不要做的事
- NEVER access AutoLoads in — AutoLoads aren't guaranteed to exist yet during _init(). Use
_init()or_ready()instead._enter_tree() - NEVER create circular dependencies — If GameManager depends on SaveManager and SaveManager depends on GameManager, initialization deadlocks. Use signals or dependency injection.
- NEVER store scene-specific state in AutoLoads — AutoLoads persist across scene changes. Storing temporary state (current enemy count, UI state) causes leaks. Reset in .
_ready() - NEVER use AutoLoads for everything — Over-reliance creates "God objects" and tight coupling. Limit to 5-10 AutoLoads max. Use scene trees for local logic.
- NEVER assume AutoLoad initialization order — AutoLoads initialize top-to-bottom in Project Settings. If order matters, add explicit calls or use
initialize()carefully.@onready
- 绝对不要在中访问AutoLoad — 在
_init()阶段,AutoLoad并不一定已经存在。请改用_init()或_ready()。_enter_tree() - 绝对不要创建循环依赖 — 如果GameManager依赖SaveManager,同时SaveManager又依赖GameManager,会导致初始化死锁。请使用信号或依赖注入。
- 绝对不要在AutoLoad中存储场景特定状态 — AutoLoad会在场景切换后持续存在。存储临时状态(当前敌人数量、UI状态)会导致内存泄漏。请在中重置状态。
_ready() - 绝对不要什么都用AutoLoad — 过度依赖会产生“上帝对象”和紧耦合。最多限制使用5-10个AutoLoad。局部逻辑请使用场景树。
- 绝对不要假设AutoLoad的初始化顺序 — AutoLoad会按照项目设置中的从上到下顺序初始化。如果顺序很重要,请添加显式的调用或谨慎使用
initialize()。@onready
Available Scripts
可用脚本
MANDATORY: Read the appropriate script before implementing the corresponding pattern.
强制要求:在实现对应模式前,请先阅读相应的脚本。
service_locator.gd
service_locator.gd
Service locator pattern for decoupled system access. Allows swapping implementations (e.g., MockAudioManager) without changing game code.
用于解耦系统访问的服务定位器模式。无需修改游戏代码即可替换实现(例如MockAudioManager)。
stateless_bus.gd
stateless_bus.gd
Stateless signal bus pattern. Domain-specific signals (player_health_changed, level_completed) without storing state. The bus is a post office, not a warehouse.
无状态信号总线模式。提供领域特定信号(player_health_changed、level_completed)但不存储状态。总线仅作为邮局,而非仓库。
autoload_initializer.gd
autoload_initializer.gd
Manages explicit initialization order and dependency injection to avoid circular dependencies.
Do NOT Load service_locator.gd unless implementing dependency injection patterns.
管理显式初始化顺序和依赖注入,以避免循环依赖。
请勿加载 service_locator.gd,除非你要实现依赖注入模式。
When to Use AutoLoads
何时使用AutoLoad
Good Use Cases:
- Game Managers: PlayerManager, GameManager, LevelManager
- Global State: Score, inventory, player stats
- Scene Transitions: SceneTransitioner for loading/unloading scenes
- Audio Management: Global music/SFX controllers
- Save/Load Systems: Persistent data management
Avoid AutoLoads For:
- Scene-specific logic (use scene trees instead)
- Temporary state (use signals or direct references)
- Over-architecting simple projects
适用场景:
- 游戏管理器:PlayerManager、GameManager、LevelManager
- 全局状态:分数、背包、玩家属性
- 场景切换:用于加载/卸载场景的SceneTransitioner
- 音频管理:全局音乐/音效控制器
- 存档/读档系统:持久化数据管理
避免使用AutoLoad的场景:
- 场景特定逻辑(改用场景树)
- 临时状态(使用信号或直接引用)
- 对简单项目过度架构设计
Implementation Pattern
实现模式
Step 1: Create the Singleton Script
步骤1:创建单例脚本
Example: GameManager.gd
gdscript
extends Node示例:GameManager.gd
gdscript
extends NodeSignals for global events
全局事件信号
signal game_started
signal game_paused(is_paused: bool)
signal player_died
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
undefinedvar 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
undefinedStep 2: Register as AutoLoad
步骤2:注册为AutoLoad
Project → Project Settings → AutoLoad
- Click the folder icon, select
game_manager.gd - Set Node Name: (PascalCase convention)
GameManager - Enable if needed globally
- Click "Add"
Verify in :
project.godotini
[autoload]
GameManager="*res://autoloads/game_manager.gd"The prefix makes it active immediately on startup.
*项目 → 项目设置 → AutoLoad
- 点击文件夹图标,选择
game_manager.gd - 设置节点名称:(采用PascalCase命名规范)
GameManager - 根据需要启用全局访问
- 点击“添加”
在中验证:
project.godotini
[autoload]
GameManager="*res://autoloads/game_manager.gd"*Step 3: Access from Any Script
步骤3:在任意脚本中访问
gdscript
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)gdscript
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)Best Practices
最佳实践
1. Use Static Typing
1. 使用静态类型
gdscript
undefinedgdscript
undefined✅ Good
✅ 推荐
var score: int = 0
var score: int = 0
❌ Bad
❌ 不推荐
var score = 0
undefinedvar score = 0
undefined2. Emit Signals for State Changes
2. 状态变化时发送信号
gdscript
undefinedgdscript
undefined✅ Good - allows decoupled listeners
✅ 推荐 - 支持解耦的监听器
signal score_changed(new_score: int)
func add_score(points: int) -> void:
score += points
score_changed.emit(score)
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
undefinedfunc add_score(points: int) -> void:
score += points
ui.update_score(score) # 不要直接调用UI
undefined3. Organize AutoLoads by Feature
3. 按功能组织AutoLoad
res://autoloads/
game_manager.gd
audio_manager.gd
scene_transitioner.gd
save_manager.gdres://autoloads/
game_manager.gd
audio_manager.gd
scene_transitioner.gd
save_manager.gd4. Scene Transitioning Pattern
4. 场景切换模式
gdscript
undefinedgdscript
undefinedscene_transitioner.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)
undefinedextends 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)
undefinedCommon Patterns
常见模式
Game State Machine
游戏状态机
gdscript
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
passgdscript
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:
# 显示游戏结束界面
passResource Preloading
资源预加载
gdscript
undefinedgdscript
undefinedPreload 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
undefinedconst 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
undefinedTesting AutoLoads
测试AutoLoad
Since AutoLoads are always loaded, avoid heavy initialization in . Use lazy initialization or explicit init functions:
_ready()gdscript
var _initialized: bool = false
func initialize() -> void:
if _initialized:
return
_initialized = true
# Heavy setup here由于AutoLoad始终会被加载,请避免在中进行重型初始化。请使用延迟初始化或显式的初始化函数:
_ready()gdscript
var _initialized: bool = false
func initialize() -> void:
if _initialized:
return
_initialized = true
# 这里进行重型初始化操作Reference
参考资料
Related
相关内容
- Master Skill: godot-master
- 核心技能:godot-master