I'm making an enemy in my game that teleports the player back to the start of the level on contact.

Note: the player has a reference of itself stored in an auto-loaded singleton, called singleton. This is used throughout the game with no issue so far.

When the code the enemy uses is singleton.player.global_position = start_position, the screen flashes briefly (possibly because the camera follows the player to the starting position of the level) and then the player is right back to where it was before--the place where it made contact with the enemy.

However, this problem doesn't happen when I use this psychotic work around:

singleton.player.global_position *= 0
singleton.player.global_position += start_position
#this code teleports the player to the starting position with no problems at all.

Also, after some debugging, I found that setting the position in a _process() function will also work:

#in enemy:
func _process():
    #touching_player is set to true when the body enters the area
    if touching_player: 
        singleton.player.global_position = start_position #this works
        touching_player = false

Some things worth noting:
-Game is 2D
-Player is a rigidBody2D with code that changes its linear velocity for movement
-Enemy is an area2D, it sends a signal to itself for "body_entered", and if the body entered is the player, it does the teleport

Since these work-arounds exist, I guess this is less of a question on what I should do to solve the problem than a bug report of a sort. It'd give me peace of mind if someone can explain why the engine is behaving like this.

  • Correct. You cannot manually change position/rotation values when an object is under control of the physics engine. It will let you do it, but likely result in some weird behavior. You need to change the mode to kinematic, then wait until the next physics frame, move it, and then set it back. You can use:

    singleton.player.mode = MODE_KINEMATIC
    yield(get_tree(), "physics_frame")
    singleton.player.global_position = start_position
    singleton.player.mode = MODE_RIGID

If player is a RigidBody2D, its position and velocity are managed by the physics engine. Changing those manually is not reliable, unless you override the physics engine. The normal way to make changes to a RigidBody2D is by applying forces.

    I don't know. You could try changing its mode property to MODE_KINEMATIC while doing the teleportation. That might require delaying a physics frame so that the physics engine doesn't interfere with the manual movement.

    Correct. You cannot manually change position/rotation values when an object is under control of the physics engine. It will let you do it, but likely result in some weird behavior. You need to change the mode to kinematic, then wait until the next physics frame, move it, and then set it back. You can use:

    singleton.player.mode = MODE_KINEMATIC
    yield(get_tree(), "physics_frame")
    singleton.player.global_position = start_position
    singleton.player.mode = MODE_RIGID