Attention | Topic was automatically imported from the old Question2Answer platform. | |
Asked By | Seanfy | |
Old Version | Published before Godot 3 was released. |
Hi guys, I need help on a problem when switching scenes through Area2D, I got two scenes setup:
Scene A has just a big ‘Start’ button that will switch to scene B when pressed.
Scene B has a big image as background, a player node(KinematicBody2D) added from a singleton script, an Area2D, let’s say ‘Portal’, and a UI button ‘Back’ will switch back to scene A.
When using ‘Start’ or ‘Back’ button to switch between scene A and B, it works all good, it also works when I control the player node step onto ‘Portal’ area and switch back to scene A, but when I click ‘Start’ button at this time, it will take me to scene B and then switch back to scene A immediately, then it work properly again if I click ‘Start’ button, this only happens one time, after I switch scene from B to A through Area2D
here is the output window with my comments:
-move player to the ‘Portal’ area in scene B-
body enter
player current pos: (1541.038696, 857.218872)
-after this I move player position out of area2D to prevent it from triggering again-
change scene to res://scenes/sceneA.tscn
-click ‘Start’ button in scene A-
change scene to res://scenes/sceneB.tscn
-HERE IS THE PROBLEM: body enter was triggered again immediately -
*I printed the player position in body enter event and it’s not in the ‘Portal’ positon, see the last triggered position above (1541.038696, 857.218872)
body enter
player current pos: (874, 490)
change scene to res://scenes/sceneA.tscn
I’ve looked into this problem for some time but still no clue at the moment, thanks in advance!
I assume you use change_scene
, right? How do you make the player appear in the scene from your singleton code?
Zylann | 2016-11-08 13:32
I checked my script and seems there is no change_scene were used, I followed the exact steps from official doc here: Singletons (AutoLoad) to change between scenes. Just add a variable to indicate if the target scene need to display the player:
func goto_scene(path, showplayer=false)
For my player, it’s in a player scene, it’s instanced in the singleton script as a variable and kept there, it will be removed by it’s parent before freeing the current scene(if player node exists in the scene) and will be re-positioned and added to the new scene’s tileset map node as it’s child, after the scene was loaded.
Seanfy | 2016-11-08 13:59
Well the code from the doc uses something similar to change_scene
(manually) but that’s not really relevant in fact.
I suspect that you are setting the position of the player after adding it to the tree, which could trigger an event if there is an Area2D at the spawn point before you move it away.
And even in that case, it would be normal for the Area2D to trigger if the player spawns directly on it. I would solve this problem like Minecraft does with portals, by adding a flag that is true until you leave the area, preventing you from loop-teleporting.
Now even if after checking these you still have a problem when the player spawns back in the map, then you have a problem I don’t see so far so I would need to see the code.
Zylann | 2016-11-08 14:10
I actually move the player away from the area2d position to (0,0) once it enters the area2d, just to see if it’s because of loop-teleporting but it seems not working, I will try the flag way you described:
here is the code:
from sceneA to sceneB, by click the ‘start’ button:
func _on_Button_pressed():
get_node("/root/global").goto_scene("res://scenes/sceneB.tscn")
from sceneB to sceneA, by entering the area2d:
func _on_Area2D_body_enter( body ):
print("body enter")
print(body.get_pos())
body.set_pos(Vector2(0,0))
get_node("/root/global").goto_scene("res://scenes/sceneA.tscn")
switch scene, the whole singleton code:
extends Node
var current_scene = null
var playerscene = preload("res://player/test1/player.tscn")
var player = null
func _ready():
var root = get_tree().get_root()
current_scene = root.get_child(root.get_child_count()-1)
player = playerscene.instance()
func goto_scene(path, showplayer=false):
# remove player node from current_scene
var playernode = current_scene.find_node("player",true,false)
if playernode != null:
playernode.set_pos(Vector2(0,0))
playernode.get_parent().remove_child(playernode)
call_deferred("_deferred_goto_scene",path,showplayer)
func _deferred_goto_scene(path,showplayer):
print("change scene start")
current_scene.free()
# Load new scene
var s = ResourceLoader.load(path)
print(path)
# Instance the new scene
current_scene = s.instance()
# Add it to the active scene, as child of root
get_tree().get_root().add_child(current_scene)
# optional, to make it compatible with the SceneTree.change_scene() API
get_tree().set_current_scene( current_scene )
# put player on map
if showplayer == true:
var pos = current_scene.get_node("startpos").get_pos()
player.set_pos(pos)
current_scene.find_node("obstacles",true,false).add_child(player)
Seanfy | 2016-11-08 14:40
Try setting your player’s position before switching the scene. As far as I can see you are doing it after you have switched the scene.
timoschwarzer | 2016-11-08 15:00
I set the player position to (0,0) twice, first when it enter the area2d and triggered body_enter event, second is when change scene is about to happen and before player is removed from the current scene, I think they are all happened before switch the scene…
The last part to set the player’s new position needs to be at this point because I need to get the position from the new scene(a Position2D node), but the player should be at position (0,0) already.
Seanfy | 2016-11-08 15:09
I think the problem is that physics are running one frame behind the renderer, so you have to set the player’s position, wait one frame and then load the scene.
timoschwarzer | 2016-11-08 15:12
I see you store player
in a variable, but why are you trying to find playernode
in goto_scene
while you don’t use it in _deferred_goto_scene
?
Zylann | 2016-11-08 15:19
this playernode is a temp variable, used to check if the current scene has a player node in it.
Seanfy | 2016-11-08 15:29
Be careful then, because you use the playernode
temporary variable to move the player, remove it from the tree, and forget it after… so what you do with it is useless in the end, and causes a potential node leak. But I think by plain luck that player
is actually the same node, because only your singleton is instancing it, am I right? If it is, then you don’t need to find it, you have it already.
I’ll try to do the same things at home to see if I get the same problem, because it’s a bit confusing.
Zylann | 2016-11-08 18:39
Thanks @Zylann, Yes i’m aware of the node leak case you pointed out, I’m actually don’t understand the parenting structure quite well, by the time I wrote the code I was not sure if the variable ‘player’ has the correct parenting relationship in the scene, that’s why I used a temp var. But yes you are right it’s confusing, I should use ‘player’ at the actual set_pos code, even it’s the same as the temp ‘playernode’
Seanfy | 2016-11-08 18:57