如何在Godot中使用GDScript实现按自定义帧率运行函数的自适应定时器
让Godot代码按自定义帧率稳定执行的解决方案
我来帮你搞定这个需求——要让指定代码按自定义帧率(比如60FPS)稳定运行,核心是根据设备实际的时间流逝动态调整执行时机,而不是依赖固定的等待时间。毕竟不同设备的帧率波动会导致固定wait_time的Timer执行频率不准,下面给你两种实用的实现方案:
方案一:用_process结合时间差判断(精度最高,推荐)
这种方法直接在每帧中检查时间差,能抵消帧率波动的影响,保证执行频率尽可能贴近目标帧率。
代码示例
# 自定义目标帧率(比如60FPS) var target_fps = 60 # 目标执行间隔(秒):1秒除以帧率 var target_interval = 1.0 / target_fps # 记录上一次执行代码的时间(单位:秒) var last_exec_time = 0.0 func _ready(): # 初始化上次执行时间为当前时间 last_exec_time = OS.get_ticks_msec() / 1000.0 func _process(delta): # 获取当前时间(转换为秒) var current_time = OS.get_ticks_msec() / 1000.0 # 计算距离上次执行的时间差 var time_since_last = current_time - last_exec_time # 当时间差达到或超过目标间隔时,执行代码 if time_since_last >= target_interval: # -------------------------- # 这里放你要按目标帧率执行的代码 print("按60FPS执行的代码触发") # -------------------------- # 更新上次执行时间,注意要扣除超出目标间隔的部分,避免累积误差 # 比如如果卡顿导致时间差超过0.02秒,下次会从正确的时间点开始计算 last_exec_time = current_time - (time_since_last % target_interval)
关键细节
- 用
OS.get_ticks_msec()获取系统时间比单纯累加delta更准确,因为delta在卡顿帧会突然变大,容易导致误差累积。 - 更新
last_exec_time时的取余操作,能保证即使某次执行延迟了,后续的执行节奏也能回到目标帧率上,不会出现“越慢越卡”的情况。
方案二:改进Timer节点的动态等待时间(适合异步场景)
如果你更习惯用Timer节点,也可以动态调整它的wait_time来适配帧率波动。不过这种方法的精度略低于方案一,适合不需要严格帧同步的异步触发场景。
代码示例
var target_fps = 60 var target_interval = 1.0 / target_fps var timer:Timer var last_timeout_time = 0.0 func _ready(): # 创建并初始化Timer timer = Timer.new() timer.one_shot = false # 设置为循环触发 timer.wait_time = target_interval add_child(timer) timer.connect("timeout", self, "_on_timer_timeout") timer.start() last_timeout_time = OS.get_ticks_msec() / 1000.0 func _on_timer_timeout(): # -------------------------- # 这里放你要执行的代码 print("Timer触发执行") # -------------------------- # 计算实际触发间隔,微调下一次的等待时间,抵消误差 var current_time = OS.get_ticks_msec() / 1000.0 var actual_interval = current_time - last_timeout_time last_timeout_time = current_time # 调整wait_time,让下一次触发更贴近目标间隔 timer.wait_time = target_interval - (actual_interval - target_interval)
方案选择建议
- 如果需要严格控制执行频率(比如动画更新、物理模拟),优先选方案一,它的精度更高,能适配各种帧率波动。
- 如果是异步触发的单次/循环任务(比如延迟提示、周期性检查),方案二更简洁,不需要每帧处理逻辑。
内容的提问来源于stack exchange,提问作者Joe




