I'm a beginner using v3.4.4.stable.mono.official [419e713a2] on Windows, and I'm working on a project structured around Davide Pesce's Simple RPG Godot tutorial series. I decided to add a standard JRPG-style room transition feature, and it mostly works, but I run into a problem when a room's scene transition zone (an Area2D) leads into another room with a scene transition zone placed in the same spot. The player is moved away from the position before the new room loads in (I have confirmed this through testing), but once the room loads, the newly-loaded transition zone treats it as if the player is still in the same spot it was when it triggered the first transition zone.
I've found that the issue can be sidestepped with a built-in timer delay after the room's instancing that sets a period before the transition zones can be triggered again, but I'd like to know if there's some way to fix this without adding more delays, or if this is simply a bug or limitation of Godot.
Here's the related code. (I apologize if some of the code is badly-written or messy--I'm very new to Godot and GDScript!)
Code for SceneController (used to load and unload scenes):
extends Node2D
# Signal and variables for getting all objects to save their data in StoredData
signal save_data
signal SceneLoaded
var first_load = true
var objects_storing_data = 0 # Set by rooms when they load
var set_data_count = 0
# Scene-setting variables
export var current_scene : String setget set_current_scene
export var current_scene_num : int setget set_current_scene_num
export var use_int_scenes : bool # true = use current_scene_num instead of current_scene when loading scenes
var scene_numbers = {0 : "StartRoom", 1 : "SolemnGrovePath", 2 : "SolemnGrove", 3 : "SolemnGrovePath2"}
var scene
var zone_gone = false
var zone_path
var scene_loaded = false # For if the scene is loaded, makes this script wait for scene to fully load
var currently_loading = false # For if the loading process is ongoing (stops certain processes [currently, load zones])
# Load player in right spot
onready var player = get_node("/root/SceneController/Player")
var spawn_area = Vector2(0, 0)
var start_spawn_area = Vector2(0, 0)
# All variables that need to be saved! To be written to save file on game save (program this in!)
# Fiona
var Fiona_quest_status
var Fiona_dialogue_state
var Fiona_necklace_found = false # All these should later be set by loading a save file. (not implemented)
var StartRoom_Items_itemspresent
# Black fade
onready var BlackFade = get_node("/root/SceneController/GUI/BlackFade/AnimationPlayer")
# Music
var scene_music : String # Set by rooms when they load
var music_volumes = {"mountain-trials.ogg" : -6, "falling-petals.ogg" : 6, "golden-forest-stroll.ogg" : 3}
onready var Music = get_node("/root/SceneController/Player/Music")
func _ready():
if use_int_scenes:
current_scene = scene_numbers[current_scene_num]
else:
current_scene_num = scene_numbers.keys()[scene_numbers.values().find(current_scene)]
LoadScene()
func set_current_scene(new_scene):
current_scene = new_scene
current_scene_num = scene_numbers.keys()[scene_numbers.values().find(current_scene)]
func set_current_scene_num(new_scene):
current_scene_num = new_scene
current_scene = scene_numbers[current_scene_num]
func LoadScene():
currently_loading = true
scene = load("res://Scenes/" + current_scene + ".tscn").instance()
emit_signal("save_data")
if first_load: #Only true during first room load after startup
first_load = false
add_child(scene)
while not scene_loaded:
pass
emit_signal("SceneLoaded")
# Music setup
Music.stop()
if not scene_music == "":
var new_music : AudioStream = load("res://Sounds/" + scene_music)
Music.stream = new_music
Music.volume_db = music_volumes[scene_music]
Music.play()
player.position = start_spawn_area
scene_loaded = false
currently_loading = false
else:
while set_data_count < objects_storing_data: # Waits for any important data to save
pass
BlackFade.play("FadeIn")
$FadeTimer.start() # Starts a timer for the time it takes to fade to black
get_tree().paused = true
func _on_FadeTimer_timeout():
get_child(4).queue_free() # 4
$FadeTimer2.start() # Starts a timer for the time it takes to fade from black to transparent
func _on_FadeTimer2_timeout():
player.pause_mode = Node.PAUSE_MODE_PROCESS
player.position = spawn_area
player.pause_mode = Node.PAUSE_MODE_STOP
yield(VisualServer, 'frame_post_draw')
add_child(scene)
while not scene_loaded:
pass
emit_signal("SceneLoaded")
# Music setup
if scene_music == "":
Music.stream = null
Music.stop()
else:
var new_music : AudioStream = load("res://Sounds/" + scene_music)
if not Music.stream == new_music:
Music.stop()
Music.stream = new_music
Music.volume_db = music_volumes[scene_music]
Music.play()
get_tree().paused = false
BlackFade.play("FadeOut")
scene_loaded = false
yield(VisualServer, 'frame_post_draw')
currently_loading = false
func _on_TransitionZone_change_scene():
zone_gone = true
func _on_TransitionZone_tree_exited():
if zone_gone == true:
LoadScene()
zone_gone = false
Code for transition zones (the Area2Ds):
extends Area2D
signal change_scene
export var scene : int
export var isSpawnZone : bool # Potentially obsolete
var hasBeenExited = true
var SceneController
export var spawn_area : Vector2 # Area the player should spawn in the *next* room
func _ready():
# Tell SceneController setup is happening
SceneController = get_node("/root/SceneController")
SceneController.connect("SceneLoaded", self, "_on_Scene_Loaded")
self.connect("change_scene", SceneController, "_on_TransitionZone_change_scene")
self.connect("tree_exited", SceneController, "_on_TransitionZone_tree_exited")
func _on_Scene_Loaded():
if isSpawnZone:
hasBeenExited = false
func _on_TransitionZone_body_exited(body):
if body.name == "Player":
hasBeenExited = true
func _on_TransitionZone_body_entered(body):
if body.name == "Player" and not SceneController.currently_loading:
SceneController.current_scene_num = scene
SceneController.spawn_area = spawn_area
emit_signal("change_scene")
queue_free()
The process that these scripts should do together (with help from some other scripts) is the following:
- TransitionZone detects player entering it.
- TransitionZone notifies SceneController that it has been entered and sends the room info (room number and coordinates the player should spawn at).
- TransitionZone deletes itself so it isn't triggered over and over.
- SceneController goes through the process of loading a new scene. After the old scene is gone and the screen has faded to black but right before the new scene is instanced, the player is moved to the spot they're supposed to spawn at in the new scene.
- A new scene is instanced, and it knows the player isn't in the previous spot.
The process runs correctly until step 5--when the scene is instanced, the new TransitionZone detects the player as being in the spot it was in before the transition, even though the player has already moved (which I have confirmed through testing). This makes it detect the player as triggering the Area2D, even though the player is not in the same spot it was in before, leading to another scene transition.
Let me know if you need additional code snippets, descriptions, screenshots, or videos of the issue. I've spent a lot of time pulling my hair out over this issue, so I hope there's a simple fix I've missed, but hopefully I can at least learn if this is something unfixable I'll have to work around. Any help is appreciated! :>