Godot中await循环触发多次导致脚本丢失错误求助
我需要遍历所有帝国,依次通知它们进行回合,等待当前回合完成后再触发下一个帝国的回合。玩家的第一个回合可以正常运行,但当AI调用endTurn()时,出现如下错误:
E 0:01:08:0911 Map.gd:419 @ endTurn(): Resumed function 'beginRound()' after await, but script is gone. At script: res://scripts/HostGameMenu.gd:197 <C++ Error> Method/function failed. Returning: Variant() <C++ Source> modules/gdscript/gdscript_function.cpp:197 @ resume() <Stack Trace> Map.gd:419 @ endTurn() Tribe.gd:221 @ takeTurn() Map.gd:414 @ beginNewTurn() HostGameMenu.gd:196 @ beginRound() Map.gd:419 @ endTurn() Map.gd:1912 @ endEmpireTurn()
相关脚本代码
脚本一(HostGameMenu.gd)
func beginRound(): for i in range(empireTurnOrder.size()): empireTurnIndex = i # 仅用于外部调试时查看当前回合所属帝国 gameMap.beginNewTurn.rpc(empireTurnOrder[empireTurnIndex]) # empireTurnOrder是PackedStringArray类型 await gameMap.beginNextTurn gameMap.endRound.rpc() # gameMap指向脚本二所在节点
脚本二(Map.gd)
signal beginNextTurn @rpc("any_peer", "call_local", "reliable") func beginNewTurn(nextEmpire: String) -> void: var empire = getEmpire(nextEmpire) if empire.playerId != -1 and empire.playerId != userEmpire.playerId: # 仅用于多人模式,测试时未启用。userEmpire是玩家控制的帝国节点(脚本三实例) pass else: if empire.playerId == userEmpire.playerId: # UI修改,无关逻辑 empire.takeTurn() # empire指向脚本三的节点实例 func endTurn(): if not serverHandler.inServer or multiplayer.get_unique_id() == 1: # serverHandler指向脚本一所在节点,单人测试时inServer始终为true print(is_instance_valid(serverHandler)) emit_signal("beginNextTurn") func endRound(): if not serverHandler.inServer or multiplayer.get_unique_id() == 1: serverHandler.beginRound() func endEmpireTurn(): # 绑定到按钮点击事件 # 少量UI修改 endTurn()
脚本三(Tribe.gd)
func takeTurn(): if playerId == -1: map.endTurn() # map指向脚本二所在节点 else: # 玩家回合由按钮点击触发endTurn,此处无逻辑
打印输出
True
True
我已查找相关帖子,但大多是_process()中await计时器的问题,解决方案不适用于当前场景。
编辑1
在脚本一中添加如下函数,并将beginNextTurn信号连接到该函数:
func turnEnded() -> void: print(empireTurnOrder[empireTurnIndex] + "'s turn is over.")
信号触发时能正确打印内容,但错误依旧存在。
编辑2
尝试修改beginNewTurn和beginRound函数:
脚本二修改后的beginNewTurn
@rpc("any_peer", "call_local", "reliable") func beginNewTurn(nextEmpire: String) -> bool: var empire = getEmpire(nextEmpire) if empire.playerId != -1 and empire.playerId != userEmpire.playerId: pass else: if empire.playerId == userEmpire.playerId: loadedData.get_node("ImportantMenuHub/EndTurn").set_disabled(false) loadedData.get_node("ResourceHUD/RaiseAll").set_disabled(false) empire.takeTurn() await beginNextTurn return true
脚本一修改后的beginRound
func beginRound(): for i in range(empireTurnOrder.size()): empireTurnIndex = i await gameMap.beginNewTurn(empireTurnOrder[empireTurnIndex]) gameMap.endRound.rpc()
此时在emit_signal("beginNextTurn")行出现错误:Parameter "function" is null,测试输出如下:
true
<empire 1>'s turn is over.
true
<empire 2>'s turn is over.
问题分析与解决方法
核心问题拆解
第一个错误Resumed function 'beginRound()' after await, but script is gone本质是await挂起的函数上下文被销毁,信号触发时仍尝试恢复执行;第二个错误Parameter "function" is null是因为await beginNextTurn时,信号没有绑定有效回调。
具体修复步骤
确保脚本实例生命周期稳定
检查HostGameMenu节点是否在回合流程中被意外销毁/隐藏(比如UI切换时误删)。可以在beginRound()开头添加print(is_instance_valid(self)),回合过程中持续验证实例有效性。修正rpc与await的配对逻辑
单人测试场景下无需使用rpc,直接调用函数避免远程上下文干扰:# 脚本一的beginRound修改 func beginRound(): for i in range(empireTurnOrder.size()): empireTurnIndex = i # 替换rpc为直接调用 gameMap.beginNewTurn(empireTurnOrder[empireTurnIndex]) await gameMap.beginNextTurn gameMap.endRound()确保信号绑定始终有效
在脚本二的_ready()中显式绑定信号,避免动态绑定失效:# 脚本二的_ready函数 func _ready(): connect("beginNextTurn", serverHandler, "turnEnded")调整AI回合触发流程
AI回合直接调用endTurn()会导致信号立即触发,可添加短暂延迟确保逻辑完成:# 脚本三的takeTurn修改 func takeTurn(): if playerId == -1: # 等待AI逻辑执行完成(示例用0.1秒延迟,可根据实际逻辑调整) await get_tree().create_timer(0.1).timeout map.endTurn() else: pass
内容的提问来源于stack exchange,提问作者Frasher Gray

