Setting position when changing scene?

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By MumaStudios
:warning: Old Version Published before Godot 3 was released.

Hello

Currently I’m working on a project where the player can collide with two different Area2d objects, I am trying to make it so that the player will enter the new scene at two different points depending on what area is collided with however right now both objects will take the player to the same point in the scene.

Please look at this code and tell me if I’m missing something obvious

func _fixed_process(delta):
if Input.is_key_pressed(82):
action = true
if (action and inside):
get_tree().change_scene(next_level1)
set_global_pos(Vector2(100,0))
action = false

I’m confused.

  1. Do you have the same script running in both Area2D nodes, or is this script running on the player?

  2. Either way, if you only have the one script and it’s using a vector2 literal like that, then it would clearly position the player at that same literal for any given instance of that script executing.

Perhaps give each of the Area2D nodes a variable that can be edited in the editor and the player script just grabs that variable from them?

# Area2D script
# The default may be (100,0), but you can set each one to use a different
# value in the editor or something
export (Vector2) var teleport_location = Vector2(100,0)

# player script
# grab the collided with Area2D's teleport location
# (assuming you've assigned to a collided_with variable in the player
# whatever Area2D node you collide with
func fixed_process(delta):
    set_global_pos(collided_with.teleport_location)
    action = false

…or something like that anyway.

It’s probably also better to use the get_collider() method within a is_colliding() if-block so that you can just directly use the collider to navigate through the tree to the part of the node that has the script with the extra teleportation data.

willnationsdev | 2017-06-06 19:21

1.Yes, the same script is running in both Area2D nodes.

Here is the complete code for both the player and Area2D nodes.
Doing the code gives a “Parser Error: Identifier not found: collided_with” error

Area2D

extends Area2D

export var next_level = “”
var inside = false
var action = false
export (Vector2) var teleport_location = Vector2(0,0)

func _ready():
set_fixed_process(true)

func _on_Area2D_body_enter( body ):
inside = true

func _on_Area2D_body_exit( body ):
inside = false

func _on_Area2D1_body_enter( body ):
inside = true

func _on_Area2D1_body_exit( body ):
inside = false

func _fixed_process(delta):
if Input.is_key_pressed(82):
action = true
if (action and inside):
get_tree().change_scene(next_level)
set_global_pos(collided_with.teleport_location)
action = false

player

extends RigidBody2D

var player
var feet
var jump_height = 300
var move_speed = 200
var Door
var inside = false
var action = false
var collided_with

export var next_level = ‘’
func _ready():
set_process(true)
feet = get_node(“Feet”)
feet.add_exception(self) # The player
set_mode(2)

set_contact_monitor(true)
set_max_contacts_reported(1)

func fixed_process(delta):
set_global_pos(collided_with.teleport_location)
action = false

MumaStudios | 2017-06-06 21:30

Well, you’re getting that parse error because you added…

collided_with

…to the Area2D script and it doesn’t know where that variable is coming from whereas you’ve properly defined it in the player script as a member variable.

It would also be a better idea to change your signals to be dynamically attached rather than attached from the editor using generated functions as you are currently doing. As it stands, you’ll have to create more identical pairs of on_body_enter/exit functions for every single Area2D you make in the whole game (not ideal). This would require you to go to your editor’s signals dock (for the node) and manually Disconnect them (so you don’t have a doubly subscribed signal).

It also might be better to work off of _input rather than using the _process method (which is relatively expensive). Presumably, the player would be using this “action” button to do a lot more than just teleport? More like an “interact” function? Using input instead of process would cut down on method calls and completely remove the “inside” flag. Much better to do the following:

# WARNING, this code is not comprehensive, nor tested at all
# teleporter.gd (Area2D)
const PlayerClass = preload("res://path/to/player.gd")
# define this separately for each teleporter area
export (PackedScene) var next_level = ""
# define this separately for each teleporter area
export (Vector2) var teleport_location = Vector2(0,0)

func _ready():
    # This will add a "signal" connection for EACH node that has this
    # script attached (attaches at runtime).
    connect("body_entered", self, "on_body_entered")
    connect("body_exited", self, "on_body_exited")

func on_body_entered(body):
    # returns true if has player.gd script attached
    if (body extends PlayerClass):
        # we add this to a list of interactables
        body.interactables.append(self)

func on_body_exited(body):
    # returns true if has player.gd script attached
    if (body extends PlayerClass):
        # we remove this from a list of interactables
        body.interactables.remove(self)

func interact(obj):
    # fail if interact is called with a non-Node2D parameter
    assert(obj extends Node2D)
    # move to the next scene
    get_tree().change_scene(next_level)
    # set the object's new location
    # not sure if this even actually works(?) since the same obj (player)
    # node needs to still exist in the new scene. Would need to look into
    # how to preserve an instance moving from one scene to another.
    # (I'm still relatively new to Godot myself)
    obj.set_global_pos(teleport_location)

# player.gd (RigidBody2D)
# a list of objects the player can currently interact with
var interactables = []

func _ready():
    # required for 2.1 Godot, but not for 3.x since it should
    # automatically detect the presence of the _input method
    set_process_input(true)

func _input(ev):
    # confirm your event was pressed (whichever key you want)
    # Using the 'f' key as an example here
    if ev.type == InputEvent.KEY && ev.scancode == KEY_F:
        request_interaction()

func request_interaction():
    var current_interactable = get_priority_interactable()
    if current_interactable != null: #if not null
        # The interactable decides what to do with the player trying to
        # interact with it
        current_interactable.interact(self)
    else:
    # Give the player feedback somehow
    # Example: play an "interaction failed" sound effect of some sort
    print("Nothing to interact with!")

func get_priority_interactable():
    # write an algorithm to decide which interactable to retrieve
    # for cases where player is on multiple interactable Area2Ds
    pass
    # example:
    # return null if interactables.empty() else interactables[0]

Note: you can look up the available signals and documentation for a class within the editor by ctrl-clicking on the name of a class in the script editor to bring up that class’s documentation.

Note: you can make your posts here easier to read by prepending any code lines with 4 spaces to generate a code block

willnationsdev | 2017-06-07 17:06

Through various sources I have been able to find a workaround to this problem however I have had trouble with getting the Vector2 of the teleport_location to be changed more easily with the editor.

Right now the teleport_location is set into a function and I do not understand how to change it to be changed from the editor and not the function, as it stands I would need to create a new script with a different transform_location for each door in the game which obviously become more problematic as the project gets bigger.

I tried to use a set_global_pos(teleport_location) within the fuction however I got errors about an “Invalid type in function” and that the system could not convert an argument from a String to a Vector2.

In order so that you better understand how the project works as I am not very articulate in explaining how the code works I am sending you a link to a Google Drive file with the project data so that you can inspect the code first hand in order to find a solution to this problem.

https://drive.google.com/file/d/0B_w8LqbAdMlhUkMzMHRRVlUxR2s/view?usp=sharing

MumaStudios | 2017-06-07 22:24

Ok, looking at your source code, there are a couple of things causing your problems.

You currently have three different “teleport_location” variables. One defined in each of your moveToWorld.gd files at the top…

export var teleport_location = ""

…and one in your global.gd file.

The first problem occurs with you not hinting to the editor what data type the moveToWorld teleport locations are, as in…

export (Vector2) var teleport_location = Vector2(0,0)

Without that hint in parentheses, the editor has to guess the type of the variable, and since you were initializing it with an empty string, it inferred that the type was string, hence the “Invalid type in function, can’t convert String to Vector2” error.

Part of your problem is that you want to edit a global variable from the editor, however, currently, there is no means of doing that using the singleton system. In order to edit a variable from the editor, it has to be selected in some manner for its properties to show up in the Inspector. Singleton (Autoloaded) variables like your global.gd don’t show up in the editor because they get added to the root node at runtime just before everything else loads.

What you CAN do is edit the exported property on your moveToWorld Area2Ds and then in a function connected to the “body_entered” signal, assign the variable to the global variable for passing between scenes.

# moveToWorld
export (Vector2) var teleport_location = Vector2(0,0)

func _ready():
    connect("body_entered", self, "on_body_entered")

# in order for this function to be called, you need to manually connect
# it in your ready function
func on_body_entered():
    send_teleport_data_to_global()

func send_teleport_data_to_global():
    # Take whatever was in this variable during design-time in the editor
    # and insert it into the global variable that NOW exists at runtime.
    global.teleport_location = teleport_location

This will allow you to sorta edit the global variable from the editor. Technically you’ll be editing an individual Area2D node’s property in the editor and then just before the game runs, it’ll initialize the global variable with that value. Whenever your character walks over an Area2D teleporter, it’ll then trigger the signal and assign its teleport location to the global one. It is this manual addition of signals that will prevent you from having to use multiple moveToWorld.gd scripts. You can just make the one and the individual script will add its own signal. Please check my last comment for more details as I already explained it to you in-depth there.

willnationsdev | 2017-06-08 15:38

Thank you for your help with all of this, I managed to fix this problem by changing the var to a Vector2 and not a string as well as changing the line.

global.teleport_location = (Vector2 (0,0))

to

global.teleport_location = teleport_location

As you told me to.

Apologies if it seemed like I was slow on the uptake of what you were trying to tell me but I haven’t tried coding something of this nature before and I’m really out of practise with my programming.

MumaStudios | 2017-06-08 18:14