我们的 Godot 游戏只在高配电脑上崩溃(原因如下)

发布: (2026年1月17日 GMT+8 05:02)
4 min read
原文: Dev.to

Source: Dev.to

实际问题

我们追踪到四个相互叠加的独立问题。

1. 在游戏过程中加载资源

这看起来无害:

func start_boss_fight():
    var boss_scene = load("res://scenes/InspectionBossFight.tscn")
    var boss = boss_scene.instantiate()

load() 调用会在读取磁盘时冻结 100‑500 ms。再加上着色器编译,就会触发 Windows TDR(超时检测与恢复)。Windows 会在 GPU 驱动在约 2 秒内没有响应时终止进程。没有崩溃报告,只有卡死。

解决方案:在启动时预加载。

const InspectionBossFightScene = preload("res://scenes/InspectionBossFight.tscn")

func start_boss_fight():
    var boss = InspectionBossFightScene.instantiate()  # instant

对着色器材质也同样处理:在启动时创建模板,运行时复制,而不是实时创建。


2. 永久等待的 await

func _on_achievement_unlocked():
    await EventBus.game_saved
    show_notification()

如果保存失败且从未发出该信号,协程会永远等待,游戏卡死。

解决方案:发送后不等待。

func _on_achievement_unlocked():
    EventBus.request_save.emit()
    show_notification()  # don't wait for confirmation

3. Tween 累积

每个 UI 动画都会创建一个新 tween,却从未销毁旧的。几小时后,成千上万的无用 tween 引用占据内存。

解决方案:跟踪并在创建新 tween 前销毁旧的。

var _active_tweens: Dictionary = {}

func fade_ambient_light():
    if _active_tweens.has("ambient_fade") and _active_tweens["ambient_fade"].is_valid():
        _active_tweens["ambient_fade"].kill()

    var tween = create_tween()
    _active_tweens["ambient_fade"] = tween
    tween.tween_property(light, "energy", 0.5, 1.0)

4. 没有安全限制的循环

我们的 fighter‑cleanup 循环可能在单帧内遍历数千个无效条目:

func cleanup_fighters():
    for i in range(fighters.size() - 1, -1, -1):
        if not is_instance_valid(fighters[i]):
            fighters.remove_at(i)

解决方案:添加迭代上限。

const MAX_ITERATIONS_PER_FRAME = 100

func cleanup_fighters():
    var iterations = 0
    for i in range(fighters.size() - 1, -1, -1):
        iterations += 1
        if iterations > MAX_ITERATIONS_PER_FRAME:
            break
        if not is_instance_valid(fighters[i]):
            fighters.remove_at(i)

对于更大的操作(例如一次对 100+ 单位施加增益),将工作分块到多帧执行:

func apply_cascade_inspiration(units: Array):
    var index = 0
    while index = units.size():
                break
            units[index].apply_inspiration()
            index += 1
        await get_tree().process_frame

TL;DR

  • 在启动时预加载资源。 在 Windows 上运行时的 load() 可能导致 GPU 驱动超时。
  • 不要 await 可能永远不会触发的信号。 适当时使用“发送后不等待”模式。
  • 跟踪你的 tweens。 在创建新 tween 前销毁旧的。
  • 给循环加上上限。 尤其是处理动态数组的循环。
  • 将大型操作分块到多帧。 这样可以保持主线程的响应性。

关键点:高端 PC 反而更快触发这些 bug,因为它们每秒执行的游戏循环更多。有时最好的硬件会暴露最糟糕的问题。

我们是 Lost Rabbit Digital,位于 GitHub。Starbrew Station 使用 Godot 4.5 构建。

Back to Blog

相关文章

阅读更多 »

Python:Tprof,目标分析器

Python:介绍 tprof,一款 targeting profiler。Profilers 测量整个程序的性能,以识别大部分时间消耗的位置。但一旦你…