Reputation: 13
I'm having issues with signals, I'm trying to get my character to adopt a new position when they change scenes, I'm using an Area2D to detect if the player is changing scenes. Here is the code:
extends Area2D
@export var scene_to_load: String = ""
#initial character position
var character_position: Vector2 = Vector2(0, 0)
@export var change_character_position: Vector2
@export var direction_facing: String = ""
signal scene_changed(change_character_position: Vector2)
signal direction(direction_facing)
func _on_body_entered(body):
if body is CharacterBody2D:
if scene_to_load != "":
character_position = body.global_position
get_tree().change_scene_to_file(scene_to_load)
emit_signal("scene_changed", change_character_position):
#getters
func get_character_position() -> Vector2:
return character_position
extends CharacterBody2D
func _ready():
var scene_change_detection = get_node("/root/SceneChangeDetection")
scene_change_detection.scene_changed.connect(_on_scene_changed)
func _on_scene_changed(new_character_position: Vector2):
print("Received scene_changed signal")
$Dahlia.global_position = get_node("/root/SceneChangeDetection").new_character_position
No matter what I do _on_scene_changed is never called. I tested with print statements if the emit was called, and it was. Only the connect is giving me issues.
I've tried creating signals in one script and trying to get them to call _on_signal_name() , none of them accomplish that. My Area2D is connected to _on_body_entered My CharacterBody2D isn't connected to anything (I'm not sure if I'm supposed to connect it to something)
I've been stuck on this problem all day, please help.
Upvotes: 1
Views: 1092
Reputation: 40295
When a node is freed, all its connections will be disconnected. When the scene changes, all the nodes in the prior scene will be freed.
You, of course, use an autoload to address that, since they survive the scene change, by virtue of not being part of it.
If neither the Area2D
or the CharacterBody2D
are in autoloads (autoloads can be either scripts or scenes), they won't be able to communicate. As the prior scene is freed before the new one is connected. Yes, even for signals defined in an autoload.
I'll start by assuming your Area2D
is an autoload. That leaves two cases about the CharacterBody2D
:
CharacterBody2D
is not an autoload.
So the CharacterBody2D
in the prior scene, and it will be freed before it can react to the signal.
And the CharacterBody2D
in the new scene won't be connected yet when the signal is emitted.
Either of those, the answer is that you do not need a signal: When Godot calls _ready
in CharacterBody2D
which is part of a scene, it means said scene is loaded.
If you need to keep information from one scene to the other, you can store it in the autoload, and then have the CharacterBody2D
read it on _ready
.
CharacterBody2D
is in an autoload.
So it also survives the scene change. And in that case, the signal makes sense, and should work.
A similar argument would go if the Area2D
is not an autoload.
Thus, the way I see it, either: they are both autoloads, and the signal makes sense. Or you do not use signals for this communication at all (instead store the information you need to preserve in an autoload, and read it on _ready
).
It is also viable to store information in a preloaded custom Resource
, or in static variables.
You can have multiple instances of an autoload (they are not singletons, despite the documentation: Singletons (Autoload)).
Whatever you put as an autoload will be loaded before the main scene, and you do not need to write code for that. But nothing precludes the creation of other instances. So when you add an instance of something you have as autoload in your scene... That is another instance, not the same one.
So you might have the character script as an autoload, and also the character in the scene, and those are two different instances. And what happens to one of them does not affect the other (for example, the one on the scene might interact with an area, but not the autoloaded one).
Whatever you autoload will remain available regardless of scene changes (unless you write code to free it). And it will be available using the name you give the autoload in every Node in the scene tree. So any Node of any scene can write code that uses it (to call method, read or write variables, or to emit signals or connect to them).
Thus, it makes sense to have an Area2D
in your scene that has an script that sets variables of an autoload, calls a methods of an autoload, or even emits signals of an autoload. And this does not require having an instance of whatever you have autoloaded in the scene.
In fact, if you have instances of whatever you have autoloaded in the scenes, they would be freed with the prior scene, and loaded with the new one, so they do not help. You want to be using the autoloaded instance, not other instances of the same thing but inside the scenes.
You could autoload your character, which will have it load before the main scene (which might not be desirable). You might:
_ready
.If you are not having the character inside the scene, you will need to define where you place it. To do that, I suggest using a Marker2D
, either with a particular unique name, or as part of a particular node group, so you can quickly find it from code, and then teleport the character to its position. That way you do not have to hardcode the positions in code.
If there are multiple entrances that should take the player to different positions inside the destination scene, you store in an autoload which entrance is being used before changing scene, and then use that to pick which Marker2D
you will teleport the character to.
I know I'm not giving you a recipe. You have plenty of options. Once you are familiar with how the autoload works, picking whatever is your preference would not be a problem.
We have been assuming you use the API Godot has to change scenes. But it is all nodes.
I'll also reiterate that preloaded custom Resource
and static variables would also remain loaded.
Upvotes: 0