戈多 4.0。如何停止自动调用 SceneTreeTimer?
Godot 4.0. how stop a auto call SceneTreeTimer?
我有这个代码:
extends Area2D
func _ready() -> void : auto_call()
func auto_call() :
await get_tree().create_timer(1, false).timeout
print( "area autocall" )
auto_call()
func stop_auto_call() :
auto_call = null # how stop?
我知道,我可以使用两个选项:创建一个普通的 Timer
或将引用保存在全局数组中以供稍后停止 it/them,但是...可能需要 在几个节点中进行了大量重构。
我正在尝试 desactive 或 pause 节点和代码工作...减去 auto_call 调用。
我尝试使用 unreference()
、null
、停止:process
、input
和 physics
。但是什么也没有。我看到 get_tree().get_processed_tweens()
有点希望,但 SceneTreeTimer
不存在任何希望。任何人都知道该怎么做、替代方案或想法?
Godot 有一个暂停系统。您可以暂停 SceneTree
by setting its paused
到 true
:
get_tree().paused = true
暂停或不暂停时执行哪些脚本取决于 process_mode
。
此外,任何SceneTreeTimer
created with always_process
set to false
(which you specify as second argument of create_timer
),在场景树暂停时都不会运行。
但是,这种方法也会影响暂停系统的任何其他工作,并且不会为您提供仅暂停其中一些计时器的粒度。
您也可以使用 Timer
s:
var timer := Timer.new()
add_child(timer)
timer.wait_time = 1.0
timer.one_shot = true
timer.start()
await timer.timeout
timer.queue_free()
或者像这样:
var timer := Timer.new()
add_child(timer)
timer.wait_time = 1.0
timer.one_shot = true
timer.connect("timeout", timer.queue_free, [], CONNECT_ONESHOT)
timer.start()
await timer.timeout
然后暂停它们,你可以得到每个 Timer
children,没有排队等待删除,然后暂停它:
for node in get_children():
var timer:Timer = node as Timer
if !is_instance_valid(timer) or timer.is_queued_for_deletion():
continue
timer.paused = true
请注意,这将占用所有 children Timer
s。这可能比你想要的更多。
这将允许您仅暂停特定 Node
的计时器。
我们可以创建一个 Autoload。我称之为 TimerRoot
。使用具有上述类似代码的脚本:
extends Node
func delay(wait_time:float) -> void:
var timer := Timer.new()
add_child(timer)
timer.wait_time = 1.0
timer.one_shot = true
timer.start()
await timer.timeout
timer.queue_free()
func set_paused(pause:bool) -> void:
for node in get_children():
var timer:Timer = node as Timer
if !is_instance_valid(timer) or timer.is_queued_for_deletion():
continue
timer.paused = pause
您可以像这样使用 await TimerRoot.delay(1.0)
并像这样暂停它们 TimerRoot.set_paused(true)
。
或者您可以 return Timer
:
func delay(wait_time:float) -> Timer:
var timer := Timer.new()
add_child(timer)
timer.wait_time = 1.0
timer.one_shot = true
timer.connect("timeout", timer.queue_free, [], CONNECT_ONESHOT)
timer.start()
return Timer
并像这样使用它:await TimerRoot.delay(1.0).timeout
。
另一个变体是添加一个 parent
参数,并让 delay
添加定时器作为 child 到它。这让您恢复了仅暂停 Node
.
的 Timer
的粒度
顺便说一下,在这段代码中我们暂停了。你可以实现一个取消机制,你可以在你调用 delay
的地方添加一个 if
来检查它是否被取消。如果您正在 returning Timer
,您可以附加一个带有 cancelled
属性 的脚本(这也有助于我们识别哪些是正确的计时器)。这建议我们从自动加载更改为 class:
class_name OneShotTimer extends Timer
@export
var cancelled:bool:
get: return cancelled
set(_value): pass
func _ready() -> void:
one_shot = true
connect("timeout", queue_free, [], CONNECT_ONESHOT)
start()
func set_cancelled() -> void:
cancelled = true
emit_signal("timeout")
static func delay(parent:Node, time:float) -> bool:
if !is_instance_valid(parent):
push_error("Parent Node is not valid.")
if !parent.is_inside_tree():
push_error("Parent Node is not in the scene tree.")
var timer := OneShotTimer.new()
timer.wait_time = time
parent.add_child(timer)
await timer.timeout
return !timer.cancelled
static func set_paused(parent:Node, pause:bool) -> void:
for node in parent:
var timer:OneShotTimer = node as OneShotTimer
if !is_instance_valid(timer) or timer.is_queued_for_deletion():
continue
timer.paused = pause
static func cancel(parent:Node) -> void:
for node in parent:
var timer:OneShotTimer = node as OneShotTimer
if !is_instance_valid(timer) or timer.is_queued_for_deletion():
continue
timer.set_cancelled()
你可以这样使用:
if await OneShotTimer.delay(self, 1.0):
print("OK")
else:
print("Aborted operation")
并像这样暂停一个节点的所有计时器OneShotTimer.set_paused(self, true)
。要取消它们,您需要 OneShotTimer.cancel(self)
如果您既想要粒度又想要全局暂停或取消它们的能力,我们可以用不同的代码恢复自动加载 TimerRoot
:
extends Node
var _timers:Array
func register(timer:OneShotTimer) -> void:
if is_instance_valid(timer) and !timer.is_queued_for_deletion():
_timers.append(timer)
timer.connect("timeout", func(): _timers.erase(timer), [], CONNECT_ONESHOT)
func cancel() -> void:
for timer in _timers:
timer.set_cancelled()
func set_paused(pause:bool) -> void:
for timer in _timers:
timer.paused = pause
然后在OneShotTimer
上的_ready
上添加TimerRoot.register(self)
,这样他们就都注册好了。然后你可以用 TimerRoot.set_paused(true)
暂停所有或用 TimerRoot.cancel()
.
取消所有
但那是取消。这并没有真正停止。停止是一个问题,因为您有一些代码在等待它。如果你同意他们无限期地等待信号,你可以简单地用 queue_free
.
删除计时器
考虑只使用补间动画?如果计时器具有相同的功能会很好但是...
extends Node
var _tween: Tween
func _ready() -> void:
# every 1 second call _auto_call
_tween = create_tween().set_loops()
_tween.tween_callback(_auto_call).set_delay(1)
# cancel after 10 seconds
await get_tree().create_timer(10).timeout
_stop_auto_call()
func _auto_call():
print( "area autocall" )
func _stop_auto_call() :
_tween.kill()
我有这个代码:
extends Area2D
func _ready() -> void : auto_call()
func auto_call() :
await get_tree().create_timer(1, false).timeout
print( "area autocall" )
auto_call()
func stop_auto_call() :
auto_call = null # how stop?
我知道,我可以使用两个选项:创建一个普通的 Timer
或将引用保存在全局数组中以供稍后停止 it/them,但是...可能需要 在几个节点中进行了大量重构。
我正在尝试 desactive 或 pause 节点和代码工作...减去 auto_call 调用。
我尝试使用 unreference()
、null
、停止:process
、input
和 physics
。但是什么也没有。我看到 get_tree().get_processed_tweens()
有点希望,但 SceneTreeTimer
不存在任何希望。任何人都知道该怎么做、替代方案或想法?
Godot 有一个暂停系统。您可以暂停 SceneTree
by setting its paused
到 true
:
get_tree().paused = true
暂停或不暂停时执行哪些脚本取决于 process_mode
。
此外,任何SceneTreeTimer
created with always_process
set to false
(which you specify as second argument of create_timer
),在场景树暂停时都不会运行。
但是,这种方法也会影响暂停系统的任何其他工作,并且不会为您提供仅暂停其中一些计时器的粒度。
您也可以使用 Timer
s:
var timer := Timer.new()
add_child(timer)
timer.wait_time = 1.0
timer.one_shot = true
timer.start()
await timer.timeout
timer.queue_free()
或者像这样:
var timer := Timer.new()
add_child(timer)
timer.wait_time = 1.0
timer.one_shot = true
timer.connect("timeout", timer.queue_free, [], CONNECT_ONESHOT)
timer.start()
await timer.timeout
然后暂停它们,你可以得到每个 Timer
children,没有排队等待删除,然后暂停它:
for node in get_children():
var timer:Timer = node as Timer
if !is_instance_valid(timer) or timer.is_queued_for_deletion():
continue
timer.paused = true
请注意,这将占用所有 children Timer
s。这可能比你想要的更多。
这将允许您仅暂停特定 Node
的计时器。
我们可以创建一个 Autoload。我称之为 TimerRoot
。使用具有上述类似代码的脚本:
extends Node
func delay(wait_time:float) -> void:
var timer := Timer.new()
add_child(timer)
timer.wait_time = 1.0
timer.one_shot = true
timer.start()
await timer.timeout
timer.queue_free()
func set_paused(pause:bool) -> void:
for node in get_children():
var timer:Timer = node as Timer
if !is_instance_valid(timer) or timer.is_queued_for_deletion():
continue
timer.paused = pause
您可以像这样使用 await TimerRoot.delay(1.0)
并像这样暂停它们 TimerRoot.set_paused(true)
。
或者您可以 return Timer
:
func delay(wait_time:float) -> Timer:
var timer := Timer.new()
add_child(timer)
timer.wait_time = 1.0
timer.one_shot = true
timer.connect("timeout", timer.queue_free, [], CONNECT_ONESHOT)
timer.start()
return Timer
并像这样使用它:await TimerRoot.delay(1.0).timeout
。
另一个变体是添加一个 parent
参数,并让 delay
添加定时器作为 child 到它。这让您恢复了仅暂停 Node
.
Timer
的粒度
顺便说一下,在这段代码中我们暂停了。你可以实现一个取消机制,你可以在你调用 delay
的地方添加一个 if
来检查它是否被取消。如果您正在 returning Timer
,您可以附加一个带有 cancelled
属性 的脚本(这也有助于我们识别哪些是正确的计时器)。这建议我们从自动加载更改为 class:
class_name OneShotTimer extends Timer
@export
var cancelled:bool:
get: return cancelled
set(_value): pass
func _ready() -> void:
one_shot = true
connect("timeout", queue_free, [], CONNECT_ONESHOT)
start()
func set_cancelled() -> void:
cancelled = true
emit_signal("timeout")
static func delay(parent:Node, time:float) -> bool:
if !is_instance_valid(parent):
push_error("Parent Node is not valid.")
if !parent.is_inside_tree():
push_error("Parent Node is not in the scene tree.")
var timer := OneShotTimer.new()
timer.wait_time = time
parent.add_child(timer)
await timer.timeout
return !timer.cancelled
static func set_paused(parent:Node, pause:bool) -> void:
for node in parent:
var timer:OneShotTimer = node as OneShotTimer
if !is_instance_valid(timer) or timer.is_queued_for_deletion():
continue
timer.paused = pause
static func cancel(parent:Node) -> void:
for node in parent:
var timer:OneShotTimer = node as OneShotTimer
if !is_instance_valid(timer) or timer.is_queued_for_deletion():
continue
timer.set_cancelled()
你可以这样使用:
if await OneShotTimer.delay(self, 1.0):
print("OK")
else:
print("Aborted operation")
并像这样暂停一个节点的所有计时器OneShotTimer.set_paused(self, true)
。要取消它们,您需要 OneShotTimer.cancel(self)
如果您既想要粒度又想要全局暂停或取消它们的能力,我们可以用不同的代码恢复自动加载 TimerRoot
:
extends Node
var _timers:Array
func register(timer:OneShotTimer) -> void:
if is_instance_valid(timer) and !timer.is_queued_for_deletion():
_timers.append(timer)
timer.connect("timeout", func(): _timers.erase(timer), [], CONNECT_ONESHOT)
func cancel() -> void:
for timer in _timers:
timer.set_cancelled()
func set_paused(pause:bool) -> void:
for timer in _timers:
timer.paused = pause
然后在OneShotTimer
上的_ready
上添加TimerRoot.register(self)
,这样他们就都注册好了。然后你可以用 TimerRoot.set_paused(true)
暂停所有或用 TimerRoot.cancel()
.
但那是取消。这并没有真正停止。停止是一个问题,因为您有一些代码在等待它。如果你同意他们无限期地等待信号,你可以简单地用 queue_free
.
考虑只使用补间动画?如果计时器具有相同的功能会很好但是...
extends Node
var _tween: Tween
func _ready() -> void:
# every 1 second call _auto_call
_tween = create_tween().set_loops()
_tween.tween_callback(_auto_call).set_delay(1)
# cancel after 10 seconds
await get_tree().create_timer(10).timeout
_stop_auto_call()
func _auto_call():
print( "area autocall" )
func _stop_auto_call() :
_tween.kill()