While 循环停止主循环

问题描述 投票:0回答:1

我做了循环,但它停止了主循环。请帮忙

extends Area2D


@onready var sprite = $Sprite
@onready var audio = $Audio
@onready var body = $Body
var rock = false


func disable_stone(player, stone_thread):
    stone_thread.start(await disable(player, stone_thread))

func disable(player, stone_thread):
    if !rock:
        print("super")
        rock = true
        await get_tree().create_timer(0.5).timeout
        body.colbox.disabled = true
        sprite.modulate.a8 = 100
        audio.play()
        await get_tree().create_timer(2).timeout
        while !(player in get_overlapping_areas()): pass
        body.colbox.disabled = false
        sprite.modulate.a8 = 255
        rock = false
    stone_thread.wait_to_finish()

我生成线程。我不知道如何解决这个问题。我尝试了我所知道的一切。

multithreading asynchronous thread-safety godot4
1个回答
0
投票

信号和等待如何工作的总结

信号在主线程上调度,并同步执行。

当您

await
作为信号※时,该方法将返回(并因此停止执行)一个对象,该对象表示其在代码中的位置。

然后,当发出等待的信号时,Godot 将获取该对象,尝试找到它来自的代码中的位置,并从那里执行......再次:同步。

因此,当您

await
时,代码的执行最终将在主线程中恢复。

顺便说一句,你知道有些信号

signal
会传递一些参数,那么,当你
await
一个信号时,它会返回传递的第一个参数的值。

※:是的,你

await
发出信号。当您拨打
create_timer
时,您会收到
SceneTreeTimer
,然后您会
await
从中收到
timeout
信号。您可以
await
任何其他信号。

我还看到你

await
disable
方法,这使得该方法成为一个协同例程......而你正在将结果提供给
Thread
?我觉得你不是这个意思。


问题的根源

你的问题的症结似乎是这一行:

while !(player in get_overlapping_areas()): pass
# REST OF YOUR CODE HERE

您正在循环等待

player
不再与
Area2D
重叠。除了
get_overlapping_areas
中的列表在物理框架上更新之外。如果主线程在这个循环中,它永远不会执行物理帧。这使得这个循环成为无限循环。

因此我们要解决的是如何等待

player
不再与
Area2D
重叠。

为此,不要使用辅助

Thread
,也不要使用协同例程。


检查每个物理框架的解决方案

解决此问题的正统方法是检查

_physics_frame
是否仍与
player
重叠。
顺便说一下,不要检查
Area2D,而是检查
player in get_overlapping_areas()

由于您不立即关心检查,因此我们需要第二个

overlaps_area(player)

标志(除了

bool
之外)。我称之为
rock
,所以你可以这样做:
waiting_exit

将方法连接到 
func _physics_process(_delta:float) -> void: if waiting_exit and not overlaps_area(player): waiting_exit = false # REST OF YOUR CODE HERE

信号的解决方案
当对象不再与 

area_exited

重叠时,您会收到信号:

Area2D
(如果对象是物理体或类似物体),或
body_exited
(如果对象是另一个区域)。
既然您正在检查 

area_exited

,我假设

get_overlapping_areas
player
,因此您需要
Area2D
因此,您可以将一个方法连接到 

area_exited

信号(请参阅

使用信号
),并在该方法中放置代码的第二部分: area_exited

将匿名方法连接到 
func _on_area_exited(area:Area2D) -> void: if waiting_exit and player == area: waiting_exit = false # REST OF YOUR CODE HERE

信号的解决方案
虽然我不能推荐这种方法(太费力),但它是一种将匿名方法连接到 

area_exited

信号的解决方案:

area_exited

是的,您可以重用匿名函数。然而,此时使用传统的命名函数(例如在之前的解决方案中)会更好。

等待
# Declare a continuation variable # Initialize it with an empty callable, so we can reference it var continuation := Callable() # Set the continuation to an anonymous method continuation = func(area:Area2D) -> void: # Make sure the area leaving is the player if area != player: return # REST OF YOUR CODE HERE # Safely disconnect if continuation.is_valid() and area_exited.is_connected(continuation): area_exited.disconnect(continuation) # Release the reference to this anonymous method continuation = Callable() # Connect the signal to the continuation area_exited.connect(continuation)

信号
的解决方案

area_exited

await
信号可能很诱人,所以我要在这里提一下。但是,我也不推荐这样做(不是线程安全的)。
我相信会是这样的:

area_exited

问题是,使用多线程物理时可能无法正常工作(因为您的代码不会不断等待信号)。

检查每个物理框架的解决方案(但使用
while player == await area_exited: pass # REST OF YOUR CODE HERE

最后,如果您要使用 

await

解决此问题,我建议改为

await
物理框架:
await

这种方法应该没有问题,而且这是对你所拥有的最小的改变。

© www.soinside.com 2019 - 2024. All rights reserved.