godot-scene-management
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseScene Management
场景管理
Async loading, transitions, instance pooling, and caching define smooth scene workflows.
异步加载、过渡、实例池和缓存是实现流畅场景工作流的关键。
Available Scripts
可用脚本
async_scene_manager.gd
async_scene_manager.gd
Expert async scene loader with progress tracking, error handling, and transition callbacks.
专业的异步场景加载器,包含进度跟踪、错误处理和过渡回调功能。
scene_pool.gd
scene_pool.gd
Object pooling for frequently spawned scenes (bullets, godot-particles, enemies).
针对频繁生成的场景(子弹、godot-particles、敌人)实现对象池功能。
scene_state_manager.gd
scene_state_manager.gd
Preserves and restores scene state across transitions using "persist" group pattern.
MANDATORY - For Smooth Transitions: Read async_scene_manager.gd before implementing loading screens.
使用"persist"组模式在场景过渡期间保存和恢复场景状态。
实现流畅过渡的强制要求:在实现加载界面之前,请先阅读async_scene_manager.gd的代码。
NEVER Do in Scene Management
场景管理中的禁忌操作
- NEVER use load() in gameplay code — blocks entire game until loaded. Use
var scene = load("res://level.tscn")ORpreload().ResourceLoader.load_threaded_request() - NEVER forget to check THREAD_LOAD_FAILED — Async loading without status check? Silent failure = black screen. MUST handle state.
THREAD_LOAD_FAILED - NEVER change scenes without cleaning up — Active timers/tweens persist across scenes = memory leak + unexpected behavior. Stop timers, disconnect signals before transition.
- NEVER use get_tree().change_scene_to_file() during _ready() — Changing scene in = crash (scene tree locked). Use
_ready().call_deferred("change_scene") - NEVER instance scenes without null check — if scene load failed? Crash. Check scene != null first.
var obj = scene.instantiate() - NEVER forget queue_free() on dynamic instances — Spawned 1000 enemies, all dead, but not freed? Memory leak. Use OR instance pooling.
queue_free()
gdscript
undefined- 绝对不要在游戏运行代码中使用load() —— 会阻塞整个游戏直到加载完成。请使用
var scene = load("res://level.tscn")或preload()。ResourceLoader.load_threaded_request() - 绝对不要忘记检查THREAD_LOAD_FAILED状态 —— 异步加载却不检查状态?静默失败会导致黑屏。必须处理状态。
THREAD_LOAD_FAILED - 绝对不要不做清理就切换场景 —— 活跃的计时器/补间会在场景间残留,导致内存泄漏和意外行为。切换前请停止计时器、断开信号。
- 绝对不要在_ready()中使用get_tree().change_scene_to_file() —— 在中切换场景会导致崩溃(场景树已锁定)。请使用
_ready()。call_deferred("change_scene") - 绝对不要在实例化场景前做空值检查 —— 如果场景加载失败,会导致崩溃。请先检查scene != null。
var obj = scene.instantiate() - 绝对不要忘记对动态实例调用queue_free() —— 生成了1000个敌人,全部死亡但未释放?会造成内存泄漏。请使用或实例池。
queue_free()
gdscript
undefinedInstant scene change
Instant scene change
get_tree().change_scene_to_file("res://levels/level_2.tscn")
get_tree().change_scene_to_file("res://levels/level_2.tscn")
Or with packed scene
Or with packed scene
var next_scene := load("res://levels/level_2.tscn")
get_tree().change_scene_to_packed(next_scene)
undefinedvar next_scene := load("res://levels/level_2.tscn")
get_tree().change_scene_to_packed(next_scene)
undefinedScene Transition with Fade
带淡入淡出效果的场景过渡
gdscript
undefinedgdscript
undefinedscene_transitioner.gd (AutoLoad)
scene_transitioner.gd (AutoLoad)
extends CanvasLayer
signal transition_finished
func change_scene(scene_path: String) -> void:
# Fade out
$AnimationPlayer.play("fade_out")
await $AnimationPlayer.animation_finished
# Change scene
get_tree().change_scene_to_file(scene_path)
# Fade in
$AnimationPlayer.play("fade_in")
await $AnimationPlayer.animation_finished
transition_finished.emit()extends CanvasLayer
signal transition_finished
func change_scene(scene_path: String) -> void:
# Fade out
$AnimationPlayer.play("fade_out")
await $AnimationPlayer.animation_finished
# Change scene
get_tree().change_scene_to_file(scene_path)
# Fade in
$AnimationPlayer.play("fade_in")
await $AnimationPlayer.animation_finished
transition_finished.emit()Usage:
Usage:
SceneTransitioner.change_scene("res://levels/level_2.tscn")
await SceneTransitioner.transition_finished
undefinedSceneTransitioner.change_scene("res://levels/level_2.tscn")
await SceneTransitioner.transition_finished
undefinedAsync (Background) Loading
异步(后台)加载
gdscript
extends Node
var loading_status: int = 0
var progress := []
func load_scene_async(path: String) -> void:
ResourceLoader.load_threaded_request(path)
while true:
loading_status = ResourceLoader.load_threaded_get_status(
path,
progress
)
if loading_status == ResourceLoader.THREAD_LOAD_LOADED:
var scene := ResourceLoader.load_threaded_get(path)
get_tree().change_scene_to_packed(scene)
break
# Update loading bar
print("Loading: ", progress[0] * 100, "%")
await get_tree().process_framegdscript
extends Node
var loading_status: int = 0
var progress := []
func load_scene_async(path: String) -> void:
ResourceLoader.load_threaded_request(path)
while true:
loading_status = ResourceLoader.load_threaded_get_status(
path,
progress
)
if loading_status == ResourceLoader.THREAD_LOAD_LOADED:
var scene := ResourceLoader.load_threaded_get(path)
get_tree().change_scene_to_packed(scene)
break
# Update loading bar
print("Loading: ", progress[0] * 100, "%")
await get_tree().process_frameLoading Screen Pattern
加载界面模式
gdscript
undefinedgdscript
undefinedloading_screen.gd
loading_screen.gd
extends Control
@onready var progress_bar: ProgressBar = $ProgressBar
func load_scene(path: String) -> void:
show()
ResourceLoader.load_threaded_request(path)
var progress := []
var status: int
while true:
status = ResourceLoader.load_threaded_get_status(path, progress)
if status == ResourceLoader.THREAD_LOAD_LOADED:
var scene := ResourceLoader.load_threaded_get(path)
get_tree().change_scene_to_packed(scene)
break
elif status == ResourceLoader.THREAD_LOAD_FAILED:
push_error("Failed to load scene: " + path)
break
progress_bar.value = progress[0] * 100
await get_tree().process_frame
hide()undefinedextends Control
@onready var progress_bar: ProgressBar = $ProgressBar
func load_scene(path: String) -> void:
show()
ResourceLoader.load_threaded_request(path)
var progress := []
var status: int
while true:
status = ResourceLoader.load_threaded_get_status(path, progress)
if status == ResourceLoader.THREAD_LOAD_LOADED:
var scene := ResourceLoader.load_threaded_get(path)
get_tree().change_scene_to_packed(scene)
break
elif status == ResourceLoader.THREAD_LOAD_FAILED:
push_error("Failed to load scene: " + path)
break
progress_bar.value = progress[0] * 100
await get_tree().process_frame
hide()undefinedDynamic Scene Instances
动态场景实例
Add Scene as Child
将场景作为子节点添加
gdscript
undefinedgdscript
undefinedSpawn enemy at runtime
Spawn enemy at runtime
const ENEMY_SCENE := preload("res://enemies/goblin.tscn")
func spawn_enemy(position: Vector2) -> void:
var enemy := ENEMY_SCENE.instantiate()
enemy.global_position = position
add_child(enemy)
undefinedconst ENEMY_SCENE := preload("res://enemies/goblin.tscn")
func spawn_enemy(position: Vector2) -> void:
var enemy := ENEMY_SCENE.instantiate()
enemy.global_position = position
add_child(enemy)
undefinedInstance Management
实例管理
gdscript
undefinedgdscript
undefinedKeep track of spawned enemies
Keep track of spawned enemies
var active_enemies: Array[Node] = []
func spawn_enemy(pos: Vector2) -> void:
var enemy := ENEMY_SCENE.instantiate()
enemy.global_position = pos
add_child(enemy)
active_enemies.append(enemy)
# Clean up when enemy dies
enemy.tree_exited.connect(
func(): active_enemies.erase(enemy)
)func clear_all_enemies() -> void:
for enemy in active_enemies:
enemy.queue_free()
active_enemies.clear()
undefinedvar active_enemies: Array[Node] = []
func spawn_enemy(pos: Vector2) -> void:
var enemy := ENEMY_SCENE.instantiate()
enemy.global_position = pos
add_child(enemy)
active_enemies.append(enemy)
# Clean up when enemy dies
enemy.tree_exited.connect(
func(): active_enemies.erase(enemy)
)func clear_all_enemies() -> void:
for enemy in active_enemies:
enemy.queue_free()
active_enemies.clear()
undefinedSub-Scenes
子场景
gdscript
undefinedgdscript
undefinedLoad UI as sub-scene
Load UI as sub-scene
@onready var ui := preload("res://ui/game_ui.tscn").instantiate()
func _ready() -> void:
add_child(ui)
undefined@onready var ui := preload("res://ui/game_ui.tscn").instantiate()
func _ready() -> void:
add_child(ui)
undefinedScene Persistence
场景持久化
gdscript
undefinedgdscript
undefinedKeep scene loaded when changing scenes
Keep scene loaded when changing scenes
var persistent_scene: Node
func make_persistent(scene: Node) -> void:
persistent_scene = scene
scene.get_parent().remove_child(scene)
get_tree().root.add_child(scene)
func restore_persistent() -> void:
if persistent_scene:
get_tree().root.remove_child(persistent_scene)
add_child(persistent_scene)
undefinedvar persistent_scene: Node
func make_persistent(scene: Node) -> void:
persistent_scene = scene
scene.get_parent().remove_child(scene)
get_tree().root.add_child(scene)
func restore_persistent() -> void:
if persistent_scene:
get_tree().root.remove_child(persistent_scene)
add_child(persistent_scene)
undefinedReload Current Scene
重新加载当前场景
gdscript
undefinedgdscript
undefinedRestart level
Restart level
get_tree().reload_current_scene()
undefinedget_tree().reload_current_scene()
undefinedScene Caching
场景缓存
gdscript
undefinedgdscript
undefinedCache frequently used scenes
Cache frequently used scenes
var scene_cache: Dictionary = {}
func get_cached_scene(path: String) -> PackedScene:
if not scene_cache.has(path):
scene_cache[path] = load(path)
return scene_cache[path]
var scene_cache: Dictionary = {}
func get_cached_scene(path: String) -> PackedScene:
if not scene_cache.has(path):
scene_cache[path] = load(path)
return scene_cache[path]
Usage:
Usage:
var enemy := get_cached_scene("res://enemies/goblin.tscn").instantiate()
undefinedvar enemy := get_cached_scene("res://enemies/goblin.tscn").instantiate()
undefinedBest Practices
最佳实践
1. Use SceneTransitioner AutoLoad
1. 使用SceneTransitioner自动加载
gdscript
undefinedgdscript
undefinedCentralized scene management
Centralized scene management
All transitions go through one system
All transitions go through one system
Consistent fade effects
Consistent fade effects
undefinedundefined2. Preload Common Scenes
2. 预加载常用场景
gdscript
undefinedgdscript
undefined✅ Good - preload at compile time
✅ Good - preload at compile time
const BULLET := preload("res://projectiles/bullet.tscn")
const BULLET := preload("res://projectiles/bullet.tscn")
❌ Bad - load at runtime
❌ Bad - load at runtime
var bullet := load("res://projectiles/bullet.tscn")
undefinedvar bullet := load("res://projectiles/bullet.tscn")
undefined3. Clean Up Before Transition
3. 过渡前进行清理
gdscript
func change_level() -> void:
# Clear timers, tweens, etc.
for timer in get_tree().get_nodes_in_group("timers"):
timer.stop()
SceneTransitioner.change_scene("res://levels/next.tscn")gdscript
func change_level() -> void:
# Clear timers, tweens, etc.
for timer in get_tree().get_nodes_in_group("timers"):
timer.stop()
SceneTransitioner.change_scene("res://levels/next.tscn")4. Error Handling
4. 错误处理
gdscript
func load_scene_safe(path: String) -> bool:
if not ResourceLoader.exists(path):
push_error("Scene not found: " + path)
return false
get_tree().change_scene_to_file(path)
return truegdscript
func load_scene_safe(path: String) -> bool:
if not ResourceLoader.exists(path):
push_error("Scene not found: " + path)
return false
get_tree().change_scene_to_file(path)
return trueReference
参考资料
Related
相关内容
- Master Skill: godot-master
- 核心技能:godot-master