---
name: godot-autoload-patterns
description: Godot 4 Autoload单例模式最佳实践，包含全局管理、事件总线、信号通信。
---

# Godot Autoload 单例模式

## 概述

Autoload（自动加载）是 Godot 中的单例功能，允许脚本在游戏开始时自动加载并在整个游戏周期中存在。正确使用 Autoload 可以有效管理全局状态和系统服务。

## 1. 全局数据管理

### 1.1 GameData 单例

```gdscript
# game_data.gd (Add to Project Settings > Autoload)
extends Node

enum GameState { MENU, PLAYING, PAUSED, GAME_OVER }

var state: GameState = GameState.MENU
var score: int = 0:
    set(value):
        score = value
        score_changed.emit(score)

var high_score: int = 0
var level: int = 1
var player_health: int = 100

func _ready() -> void:
    process_mode = Node.PROCESS_MODE_ALWAYS
    _load_high_score()

func _input(event: InputEvent) -> void:
    if event.is_action_pressed("pause") and state == GameState.PLAYING:
        toggle_pause()

func start_game() -> void:
    score = 0
    state = GameState.PLAYING
    game_started.emit()

func toggle_pause() -> void:
    var is_paused := state != GameState.PAUSED

    if is_paused:
        state = GameState.PAUSED
        get_tree().paused = true
    else:
        state = GameState.PLAYING
        get_tree().paused = false

    game_paused.emit(is_paused)

func end_game(won: bool) -> void:
    state = GameState.GAME_OVER

    if score > high_score:
        high_score = score
        _save_high_score()

    game_over.emit(won)

func add_score(points: int) -> void:
    score += points

func _load_high_score() -> void:
    if FileAccess.file_exists("user://high_score.save"):
        var file := FileAccess.open("user://high_score.save", FileAccess.READ)
        high_score = file.get_32()

func _save_high_score() -> void:
    var file := FileAccess.open("user://high_score.save", FileAccess.WRITE)
    file.store_32(high_score)
```

## 2. 事件总线模式

### 2.1 EventBus 单例

```gdscript
# event_bus.gd (Autoload)
extends Node

# Player events
signal player_spawned(player: Node2D)
signal player_died(player: Node2D)
signal player_health_changed(health: int, max_health: int)

# Enemy events
signal enemy_spawned(enemy: Node2D)
signal enemy_died(enemy: Node2D, position: Vector2)
signal wave_started(wave_number: int)

# Item events
signal item_collected(item_type: StringName, value: int)
signal powerup_activated(powerup_type: StringName)

# Level events
signal level_started(level_number: int)
signal level_completed(level_number: int, time: float)
signal checkpoint_reached(checkpoint_id: int)

# Game state events
signal game_paused(is_paused: bool)
signal game_over(won: bool)
```

### 2.2 使用示例

```gdscript
# 在任意脚本中连接信号
class_name Enemy
extends CharacterBody2D

func die() -> void:
    EventBus.enemy_died.emit(self, global_position)
    queue_free()
```

```gdscript
# 监听信号
class_name GameManager
extends Node

func _ready() -> void:
    EventBus.enemy_died.connect(_on_enemy_died)
    EventBus.level_started.connect(_on_level_started)

func _on_enemy_died(enemy: Node2D, position: Vector2) -> void:
    spawn_drops(position)
    add_score(10)

func _on_level_started(level_number: int) -> void:
    prepare_waves(level_number)
```

## 3. 服务管理器

### 3.1 AudioManager

```gdscript
# audio_manager.gd (Autoload)
extends Node

@export var bgm_bus: int = 0
@export var sfx_bus: int = 1

var _bgm_player: AudioStreamPlayer
var _sfx_player: AudioStreamPlayer

func _ready() -> void:
    _bgm_player = AudioStreamPlayer.new()
    _sfx_player = AudioStreamPlayer.new()
    add_child(_bgm_player)
    add_child(_sfx_player)

func play_bgm(stream: AudioStream, volume_db: float = 0.0) -> void:
    _bgm_player.stream = stream
    _bgm_player.volume_db = volume_db
    _bgm_player.play()

func play_sfx(stream: AudioStream, volume_db: float = 0.0) -> void:
    _sfx_player.stream = stream
    _sfx_player.volume_db = volume_db
    _sfx_player.play()

func stop_bgm() -> void:
    _bgm_player.stop()
```

### 3.2 SceneManager

```gdscript
# scene_manager.gd (Autoload)
extends Node

signal scene_loading_progress(progress: float)
signal scene_loaded(scene: Node)

var _current_scene: Node
var _loader: ResourceLoader

func _ready() -> void:
    _current_scene = get_tree().current_scene

func change_scene(scene_path: String) -> void:
    scene_loading_progress.emit(0.0)

    if ResourceLoader.has_cached(scene_path):
        var scene := load(scene_path) as PackedScene
        _swap_scene(scene.instantiate())
        return

    ResourceLoader.load_threaded_request(scene_path)

    while true:
        var progress := []
        var status := ResourceLoader.load_threaded_get_status(scene_path, progress)

        match status:
            ResourceLoader.THREAD_LOAD_IN_PROGRESS:
                scene_loading_progress.emit(progress[0])
                await get_tree().process_frame
            ResourceLoader.THREAD_LOAD_LOADED:
                var scene := ResourceLoader.load_threaded_get(scene_path) as PackedScene
                _swap_scene(scene.instantiate())
                return
            _:
                push_error("Failed to load scene: %s" % scene_path)
                return

func _swap_scene(new_scene: Node) -> void:
    if _current_scene:
        _current_scene.queue_free()

    _current_scene = new_scene
    get_tree().root.add_child(_current_scene)
    get_tree().current_scene = _current_scene
    scene_loaded.emit(_current_scene)
```

## 4. 场景结构

```
Project Settings > Autoload
├── GameData          # 全局游戏数据
├── EventBus         # 事件总线
├── AudioManager     # 音频管理
├── SceneManager     # 场景管理
├── SaveManager      # 存档管理
└── InputManager     # 输入管理
```

## 5. 设计原则

| 原则 | 说明 |
|------|------|
| **最小化原则** | 只将真正需要全局访问的对象设为 Autoload |
| **单一职责** | 每个 Autoload 只负责一个功能 |
| **信号解耦** | 优先使用 EventBus 信号通信 |
| **避免滥用** | 过多 Autoload 会使代码耦合度增加 |

## 6. 注意事项

| 注意事项 | 说明 |
|----------|------|
| **初始化顺序** | Autoload 按配置顺序初始化 |
| ** PROCESS_MODE_ALWAYS** | 需要在暂停时运行的脚本设置此模式 |
| **避免循环依赖** | Autoload 之间不要相互引用形成循环 |
| **线程安全** | 多线程场景注意信号线程安全 |
