i make loop, but it stop main loop. Help plz
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()
I spawn thread. I dont know how fix that. I trying all thats i know.
Signals are dispatched on the main thread, and executed synchronously.
When you await
as signal※, the method returns (and thus stops executing) an object that represents the position inside the code it was.
Then when the awaited signal is emitted, Godot will take that object, try to find the position in the code it came from, and executes from there... Again: synchronously.
In consequence, when you await
, the execution of the code will end up resuming in the main thread.
By the way, you know some signals signal
will pass some arguments, well, when you await
a signal, it returns the value of the first argument passed.
※: yes, you await
signals. When you call create_timer
, you get an SceneTreeTimer
, and then you await
the timeout
signal from it. You could await
any other signal.
I also see you await
the disable
method, which makes that method a co-routine... And you are giving the result to the Thread
? I don't think that is what you meant.
The crux of your problem seems to be this line:
while !(player in get_overlapping_areas()): pass
# REST OF YOUR CODE HERE
You are making a loop waiting for the player
to no longer be overlapping the Area2D
. Except the list from get_overlapping_areas
is updated on the physics frame. And if the main thread is here in this loop, it will never get to execute the physics frame. Which makes this loop an infinite loop.
Thus what we will be trying to solve is how to wait for the player
to no longer be overlapping the Area2D
.
And don't use a secondary Thread
, nor a co-routine, for this.
An orthodox approach to solve this problem would be to check in _physics_frame
if the player
is still being overlapped by the Area2D
. By the way, instead of checking player in get_overlapping_areas()
, check overlaps_area(player)
.
Since you do not care about the check right away, we need a second bool
flag (aside of rock
). I'll call it waiting_exit
, so you can do this:
func _physics_process(_delta:float) -> void:
if waiting_exit and not overlaps_area(player):
waiting_exit = false
# REST OF YOUR CODE HERE
It might be worth noting that get_overlapping_areas()
(and overlaps_area
) might give you slightly outdated results (if I recall correctly, they are updated after _physics_process
).
area_exited
signalYou have a signals for when an object is no longer overlapping the Area2D
: body_exited
(if the object is a physics body or similar), or area_exited
(if the object is another area).
Since you are checking with get_overlapping_areas
, I'm assuming that player
is an Area2D
, and thus you need area_exited
.
So you could connect a method to the area_exited
signal (see Using signals), and in that method you place the second part of your code:
func _on_area_exited(area:Area2D) -> void:
if waiting_exit and player == area:
waiting_exit = false
# REST OF YOUR CODE HERE
area_exited
signalWhile I can't recommend this approach (too much effort), it is a solution to connect an anonymous method to the area_exited
signal:
# 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)
And yes, you could reuse the anonymous functions. However, at that point using a traditional named function (such as in the prior solution) is better.
area_exited
signalIt might be tempting to await
the area_exited
signal, so I'm going to mention it here. However, I can't recommend this either (not thread safe).
I believe would be like this:
while player != await area_exited:
pass
# REST OF YOUR CODE HERE
The issue is that might not work correctly when using multi-threaded physics (as your code would not be constantly awaiting signals).
await
)And finally, if you are going to solve this with await
, I would suggest to await
the physics frame instead:
while overlaps_area(player):
await get_tree().physics_frame
# REST OF YOUR CODE HERE
There should be no issues with this approach (edit: it might take an extra frame compared to having the signal always connected, but that is a non-issue in this case), and it is the minimal change from what you have.