How to Teleport an Object with Physics in Godot

Hello there, I hope all is well on your gamedev journey. I have recently started learning Godot, and coming off of Unity, it seemed like it should be an easy enough switch. I had heard that their Physics system was a little hard to use and wasn’t the best, however, I really was surprised at the impact, when I was simply trying to teleport a physics object from one part of the scene, to another.

I’m not 100% clear that this is the best way to teleport an object, however, looking online I couldn’t find any solutions that worked for me well. I wanted to talk about Godot, the physics process and the solution here.

Godot has a physics process, that is separate from the scene tree state. These work separately on the physics tick and standard game tick.

To have a Physics based object, you will want to utilize a RigidBody, there are 3D and 2D varieties. You must use the one that corresponds to your movement style. I’ll use Rigidbody2D in these examples.

To set the state of your RigidBody2D in the scene graph, you will use the following methods, and when looking online these are the methods you are most likely to see, and this was what I used in my first attempt.

func _teleport(object: RigidBody2D, position: Vector2, velocity: Vector2,  angularVelocity: float, gravityScale: float, isSleeping:bool) -> void:
    object.global_transform = Transform2D.IDENTITY.translated(position)	
    object.linear_velocity = velocity
    object.angular_velocity = angularVelocity
    object.sleeping = isSleeping
    object.gravity_scale = gravityScale;

You can then call your code with the RigidBody2D that you’d like to teleport, a position to teleport to, and set their other physics properties like if you want them to have initial velocity, or torque, how gravity effects them at this new location, or if they are meant to be off-screen and “asleep.”

This however, doesn’t work very well with the Physics system, in my limited experience. Even when called in the physics timestep, it doesn’t seem to work, it will work sometimes, and then it will fail to move the object, or move it to the wrong location, as the scene view and the physics view are out of sync.

Because that didn’t work, I continued to do research online and found that I should be using the PhysicsServer2D. This is a way to send requests directly into the physics state to be processed during the physics tick. You can set properties like the ones above through certain methods. Feeling very excited to get this working I implemented them right away like so.

func _teleport(object: RigidBody2D, position: Vector2, velocity: Vector2,  angularVelocity: float, gravityScale: float, isSleeping:bool) -> void:
    
    var id = object.get_rid()
    PhysicsServer2D.body_set_state(id, PhysicsServer2D.BODY_STATE_TRANSFORM, Transform2D.IDENTITY.translated(position))
    PhysicsServer2D.body_set_state(id,   PhysicsServer2D.BODY_STATE_LINEAR_VELOCITY, velocity)
    PhysicsServer2D.body_set_state(id,     PhysicsServer2D.BODY_STATE_ANGULAR_VELOCITY, angularVelocity)
    PhysicsServer2D.body_set_state(id, PhysicsServer2D.BODY_STATE_SLEEPING, isSleeping)
    PhysicsServer2D.body_set_param(id, PhysicsServer2D.BODY_PARAM_GRAVITY_SCALE, gravityScale)

Using the RID, we send these instructions into the Physics Server for them to handle and all should be good. Except however, it still didn’t work for me, it worked a little differently, but it was very similar in that even during the process_physics step, these instructions would leave my particles stuttering and not teleporting properly.

Out of desperation, I attempted to simply combine the two methods, and do both. This seemed fruitless at first, but after learning about how these stutters are caused when the physics state and scene tree state are out of sync, it made more sense to me that updating both of these at once would be the best course of action.

Now, the physics was teleporting properly, the only problem is that you have to perform any updates to the scene and the physics state at the same time, quite annoying it seems. This is why I decided to create a teleport function that I included as a Global function that I can access anywhere in my codebase, this way, there would be no need to duplicate this teleportation code across to many different scripts. To create a Globals script, simply create a new script in your folder view, and add your function. I am extending Node2D here so that it can be used in any class that extends Node2D.

extends Node2D

func _teleport(
object: RigidBody2D, 
position: Vector2, 
velocity: Vector2 = Vector2.ZERO, 
angularVelocity:float = 0.0, 
isSleeping:bool = false) -> void:
		
    var id = object.get_rid()
    object.global_transform = Transform2D.IDENTITY.translated(position)
    PhysicsServer2D.body_set_state(id, PhysicsServer2D.BODY_STATE_TRANSFORM, Transform2D.IDENTITY.translated(position))
	
    object.linear_velocity = velocity
    PhysicsServer2D.body_set_state(id, PhysicsServer2D.BODY_STATE_LINEAR_VELOCITY, velocity)
    object.angular_velocity = angularVelocity
    PhysicsServer2D.body_set_state(id, PhysicsServer2D.BODY_STATE_ANGULAR_VELOCITY, angularVelocity)
    object.sleeping = isSleeping
    PhysicsServer2D.body_set_state(id, PhysicsServer2D.BODY_STATE_SLEEPING, isSleeping)

func _setGravity(object: RigidBody2D, gravityScale:float) -> void:
    var id = object.get_rid()
    object.gravity_scale = gravityScale;
    PhysicsServer2D.body_set_param(id, PhysicsServer2D.BODY_PARAM_GRAVITY_SCALE, gravityScale)

Then, you must add this “Global.gd” script into your Project settings as a script to “Auto-load.” To do this, navigate to “Project->Project Settings” on the top bar. Then navigate to the “Global” tab. Finally, under the “Autoload” section, click the “Add” button and select your “Global.gd” script.

From here, when you are working on an object of Node2D, or any children, you’ll have access to simply call your Global functions, you can use this technique for other things like variables too. For example:

flake = disabledFlakes.pop_back()
Global._teleport(flake, _initialPos, _initialVel, _initialTorque, false)
Global._setGravity(flake, 0.1)

Once you get your object’s RigidBody (here I’m getting one from my disabledFlakes list), you can teleport it, and set properties on it like it’s gravity through your global wrappers! This way, I don’t have to call the function in two separate ways anytime I want to teleport an object.

Conclusion

I hope that this was helpful! I know this was a rather small and simple post. I really want to get back into game development more consistently. My game development has taken a backseat as I’ve been getting used to my new schedule with a new job. Even still, I hope that you found enjoyment, and will follow with me, as I learn Godot and develop games!


If you found this tutorial helpful and want to help support me in creating this content that I host and publish for free, please consider contributing to my Patreon or Ko-fi!