How to manually set the position of a KinematicBody2D?

:information_source: Attention Topic was automatically imported from the old Question2Answer platform.
:bust_in_silhouette: Asked By i_love_godot

Godot version: 3.2 Beta 2

Hey folks,

I have a conundrum. I’ve been using a KinematicBody2D for a player character and using Area2D for obstacles that need to be avoided. When there is a collision, the obstacle emits a signal which in turn triggers the game over sequence.

Now, to move the player I have been using move_and_collide inside _physics_process. This works great, but when I want to reset the player, I’ve been setting the position manually to get the player back to the spawn point. Here is where the problem starts…

After, resetting the player position, it still collides in the position it was in when it collided originally and the signal is emitted again. Does setting the position directly not affect the physics engine underneath? Does anyone know of a way I could instantly change the position of the object? I’d rather not create a new instance each time if I can help it.

Any thoughts would be appreciated. Thanks :slight_smile:

That should work fine. Sounds like something else strange is going on. Are you somehow moving the body without moving its attached collision shape?

kidscancode | 2019-12-01 06:23

No, I’m definitely moving the KinematicBody2D. The player character is the only Node that I keep between respawns. The level (with the obstacles) is recreated each time by way of a new instance. It is the new obstacles and new signals that fire AFTER the player has been reset. It is very strange.

i_love_godot | 2019-12-01 15:14

Ok so I’ve narrowed it down to having something to do with instancing. This “double” signal emission only happens when a new obstacle is instanced. If I simply leave the original obstacle there, and move the player, one signal is emitted as expected.

I’m still at a loss as to why a fresh Area2D would fire after the player has already been moved out of the way though. I’m either doing something incredibly dumb, or it’s a bug, but I don’t know how to tell the difference XD

i_love_godot | 2019-12-03 02:45

:bust_in_silhouette: Reply From: selamba

This really should work fine. Try changing the physics engine if you can. Or try to, upon collision, disable the collision area of the player, move the player to the spawn point, and then enable it.

I’ve tried changing the collision layer bits, but it doesn’t affect the outcome. The newly added Area2D nodes still emit their signal as if the KinematicBody2D was still there.

I updated my code to remove the player node and create a new instance each time. It works, but I would prefer to figure out why this isn’t working because I am certainly going to run into this problem again at some point.

It is almost like there is a physics ghost. Does anyone know the gritty inner workings of the physics engine? Does it work on a “copy” or “virtual” version of the node, or the node directly?

I’ll try and investigate it further with a minimal project. If I discover anything, I’ll add my findings here.

i_love_godot | 2019-12-01 22:23

:bust_in_silhouette: Reply From: miyanokomiya

I got same problem probably, and executing empty move_and_slide inside _physics_process fixed it.

position = same_position
move_and_slide(Vector2())
:bust_in_silhouette: Reply From: borkox

I had the same problem, but my solution is to have a timer that calls the set_global_position of my player and it works. The times is one shot.

:bust_in_silhouette: Reply From: dmitriy_shmilo

I had a slightly different scenario (detecting level completion), but I was also using instancing to progress to the next level. What helped me was using yield(get_tree(), "idle_frame") between disabling and enabling player collision. Like this:

func _next_level():
    _player.position = Vector2.ZERO
    _player.set_collision_layer_bit(0, false)

    # do some level loading and instancing

    yield(get_tree(), "idle_frame")
    _player.set_collision_layer_bit(0, true)