Infinite loop in two way teleportation

Godot Version

Godot engine v4.2.2.stable official

Question

I have a portal scene, the portal scene is an area2D node with a collisionShape2D as its child. I have two portals in my game and I need the player to teleport back and forth between the two portals, the idea is if the player touches one of the portals then he’ll appear next to the other portal.

To solve this problem, I made this script:

extends Area2D

@export var portalPath : NodePath
var linkedPortal : Area2D

func _ready():
	linkedPortal = get_node(portalPath)

func _on_body_entered(body):
	if body.name == "Player":
		body.position = linkedPortal.global_position

But now I have another problem, when the player enters the portal it is indeed teleported to the other’s location, but then it starts to teleport back and forth without letting the player move

I would teleport the player some pixels to the right or to the left, to avoid immediate collision with the teleporter.
Or maybe activate the teleport with a key press (usually UP)?

1 Like

Similar to what @hr78 said, I would think of all portals as “one-way” and two “linked” portals merely teleporting adjacent to each other. Make linkedPortal take a Node2D instead of an Area2D so you may teleport to any defined location, like with a Marker2D.

Do I need to change the portal type from area2D to node2D?

it would help with typing. you also could export the linkedPortal directly.

@export var linkedPortal: Node2D

func _on_body_entered(body):
	if body.name == "Player":
		body.position = linkedPortal.global_position

There’s other options as well.

You could set something like just_teleported = true and never teleport if that’s true. Once they stop overlapping (e.g. the player moves away from it) you set it to false and that will allow them to teleport again.

You could also put it on a timer. If the player stands in one place for 5 more seconds maybe they want to teleport back.

4 Likes

If you add a teleporting variable to the player, you can prevent the back and forth teleporting like this:

func _on_body_entered(body):
    if body.name == "Player":
        body.teleporting = not body.teleporting
        if body.teleporting:
            body.position = linkedPortal.global_position

This example is based on the assumption, that every teleporter is connected to another teleporter. If a teleporter moves the player to a location without another teleporter, the teleporter variable will get stuck to true, and next teleporter won’t work properly. So this is a very simple but not the safest or most flexible solution.

edit: I managed to simplify the code even further.

2 Likes

I’ve solved it by checking which portal was the one linked and then applying an offset to the spawn point, but I think that’s not the best solution:

extends Area2D

@export var portalPath : NodePath
var linkedPortal : Node2D

var offset : int

func _ready():
	linkedPortal = get_node(portalPath)

func _on_body_entered(body):
	if(body.name == "Player"):
		if(linkedPortal.name == "PortalOne"):
			offset = 30
		elif(linkedPortal.name == "PortalTwo"):
			offset = -30
			
		body.global_position.x = linkedPortal.global_position.x + offset

The problem here is that the collision shape detects the player when it enters, but keeps executing the same code over and over again while the player is inside the collisionShape.
Is there a way to detect just the moment the user enters, and if the user stays inside the collisionShape stop executing the code

IMG! Thanks!!!