At the time of this writing, Godot 3.0 is nearing release, and many new users are trying it out. Recently I’ve seen a lot of confusion around physics and physics bodies, especially rigid bodies, which are Godot’s “true” physics nodes.
In this tutorial, I’ll explain when (and when not) to use rigid bodies, how they work, and demonstrate a few handy tricks to bend them to your will. The examples will use RigidBody2D, but the lessons apply equally to 3D.
is the physics body in Godot that provides simulated physics. This means that you
don’t control a RigidBody2D directly. Instead you apply forces to it (gravity, impulses,
etc.) and Godot’s built-in physics engine calculates the resulting movement, including
collisions, bouncing, rotating, etc.
You can modify a RigidBody2D’s behavior via properties such as “Mass”, “Friction”, or “Bounce”, which can be set in the Inspector:
The body’s behavior is also affected by the world, via the Project Settings -> Physics
properties, or by entering an
Area2D that is overriding the global physics properties.
One of the benefits of using a rigid body is that a lot of behavior can be gotten “for free” without writing any code. For example, let’s look at making a rudimentary “Angry Birds”-style game with falling blocks. You only need to create RigidBody2Ds for the blocks and projectile, and set their properties. Stacking, falling, and bouncing will automatically be handled by the physics engine.
Add a texture to the Sprite and a rectangular collision shape. IMPORTANT: Do not
change the Scale of the collision shape! In general this is a bad idea, and will
result in unexpected collision behavior. Always use the shape’s inner size handles and
not the outer
Node2D-derived scale handles.
NOTE: For the textures in this example, I’m using the Physics Asset pack from Kenney.nl. It contains a wide variety of blocks in different shapes and materials.
Press “Play” and you’ll see the block fall slowly downward. This is due to the
default global gravity. You can find this setting in “Project Settings” under
Physics -> 2d. You can also try changing the Block’s
Gravity Scale property in the
Inspector. I’m using a value of
Instance a Block, and then duplicate it (
Ctrl-D on Windows and
Cmd-D on MacOS)
so you can make a nice stack. Something like this:
Create another scene with the same node setup as your Block, but name this one “Ball”. Use one of the round textures and a circular collision shape. Instance this in your Main scene and place it somewhere to the side of the stack of blocks.
To cause a rigid body to move, it must have some velocity. You can give the body
an initial velocity using the
Linear -> Velocity property. Try setting this
You can also tinker with the ball’s
Bounce properties. Both of
these properties can range from zero to one. I like a bounce of around
IMPORTANT: NEVER scale a physics body! If you try, a warning will appear, and when the scene runs, the physics engine will automatically set the scale back to
Reset the linear velocity to
(0, 0). Now what if you want to be able to toss
the ball? You should never set a rigid body’s velocity or position manually -
remember, these are simulating “real-world” style physics. In the real world,
objects can’t instantly jump from place to place or from a standstill to a high
speed. If you try and do so, the physics engine will resist it, and unexpected
movement can occur. Instead, we must apply forces which create an acceleration in a certain
direction (also known as Newton’s Second Law). Godot physics objects work in
the same way.
To add force to a rigid body, you have two functions to choose from:
Adds a continuous force to the body. Imagine a rocket’s thrust, steadily pushing it faster and faster. Note that this adds to any already existing forces. The force continues to be applied until removed.
Adds an instantaneous “kick” to the body. Imagine hitting a baseball with a bat.
apply_impulse() to kick the ball when we click, drag, and release
the mouse button.
Open “Project Settings” and in the “Input Map” tab, add a new action called “click”. Connect it to the left mouse button.
Next, add a script to the Ball, and add the following code:
extends RigidBody2D var dragging var drag_start = Vector2() func _input(event): if event.is_action_pressed("click") and not dragging: dragging = true drag_start = get_global_mouse_position() if event.is_action_released("click") and dragging: dragging = false var drag_end = get_global_mouse_position() var dir = drag_start - drag_end apply_impulse(Vector2(), dir * 5)
This script toggles
dragging on when the mouse button is pressed and records
the location of the click. When the button is released, we find the vector from
the click point to the release point and use that to apply the impulse (multiplying
5 to scale it up).
apply_impulse() also takes an
offset as its first
parameter. This lets you “hit” the body off center, if you wish. For instance,
try setting it to
Vector2(25, 0) and you’ll add some spin to the ball when
There are cases where you need more direct control of a rigid body. For example, imagine you’re trying to make a version of the classic game “Asteroids”. The player’s spaceship needs to rotate using the left/right arrow keys, and to move forward when the up arrow is pressed.
Here’s the image I’m using for my ship:
I recommend you also go to OpenGameArt and search for a nice space background (but this is totally optional).
Create a new scene for the ship as we did above with the following node structure:
Note: In Godot 3.0,
0degrees points to the right (along the x axis). This means you need to add a
Spriteso it will match the body’s direction.
By default, the physics settings provide some damping, which reduces a body’s
velocity and spin. In space, there’s no friction, so there shouldn’t be any
damping at all. However, for the “Asteroids” feel, we want the ship to stop rotating
when we let go of the keys, so set the ship’s
Angular -> Damp to
extends RigidBody2D export (int) var engine_thrust export (int) var spin_thrust var thrust = Vector2() var rotation_dir = 0 var screensize func _ready(): screensize = get_viewport().get_visible_rect().size func get_input(): if Input.is_action_pressed("ui_up"): thrust = Vector2(engine_thrust, 0) else: thrust = Vector2() rotation_dir = 0 if Input.is_action_pressed("ui_right"): rotation_dir += 1 if Input.is_action_pressed("ui_left"): rotation_dir -= 1 func _process(delta): get_input() func _physics_process(delta): set_applied_force(thrust.rotated(rotation)) set_applied_torque(rotation_dir * spin_thrust)
Let’s walk through what this script is doing. The two variables,
spin_thrust control how fast the ship can accelerate and turn. In the
Inspector, set them to
25000 respectively (the units of torque make
for large numbers).
thrust will represent the ship’s engine state:
when coasting, or a vector with the length of
engine_thrust when powered on.
rotation_dir will represent what direction the ship is turning. The
variable will capture the size of the screen, which we’ll be using later.
input() function captures the keystates and sets the ship’s
on or off, and the rotation direction (
rotation_dir) positive or negative. This
function is called every frame in
Finally, physics-related functions should be called in
set_applied_force() to apply the
thrust in whatever
ship is facing. Then we use
set_applied_torque() to cause the ship to rotate.
Play the scene - you should be able to fly around freely.
Another feature of “Asteroids” is that the screen “wraps around”. If the player goes off one side, it teleports to the other side. But we already talked above about how you can’t change a rigid body’s position without breaking the physics engine. This presents a huge problem when working with rigid bodies.
A common mistake is to try adding something like this to
func _physics_process(delta): if position.x > screensize.x: position.x = 0 if position.x < 0: position.x = screensize.x if position.y > screensize.y: position.y = 0 if position.y < 0: position.y = screensize.y set_applied_force(thrust.rotated(rotation)) set_applied_torque(rotation_dir * spin_thrust)
This fails spectacularly, trapping the player on the edge of the screen (with
occasional glitches). So why doesn’t this work? The docs say
is for physics-related stuff, right?
_physics_process() is synced to the physics timestep, but that
doesn’t make it OK to use for just anything. Hope is not lost, however, the answer
is in the docs.
To quote the RigidBody2D docs:
You should not change a RigidBody2D’s position or linear_velocity every frame or even very often. If you need to directly affect the body’s state, use
_integrate_forces, which allows you to directly access the physics state.
And the description for _integrate_forces:
Allows you to read and safely modify the simulation state for the object. Use this instead of
_physics_processif you need to directly change the body’s position or other physics properties.
So there’s our answer. Instead of using
_physics_process() we need to use
which gives us access to the Physics2DDirectBodyState. I highly recommend you take a look at the
linked document, there is a lot of really useful data provided in the physics state object.
For our purposes, the key piece of information is the body’s Transform2D.
(Explaining transforms is beyond the scope of this document - see Matrices and transforms
for more information.)
The body’s position is contained in the transform’s
_integrate_forces() and add the following code:
func _integrate_forces(state): set_applied_force(thrust.rotated(rotation)) set_applied_torque(rotation_dir * spin_thrust) var xform = state.get_transform() if xform.origin.x > screensize.x: xform.origin.x = 0 if xform.origin.x < 0: xform.origin.x = screensize.x if xform.origin.y > screensize.y: xform.origin.y = 0 if xform.origin.y < 0: xform.origin.y = screensize.y state.set_transform(xform)
We grab the current transform, change it as necessary, and set it back as the new transform. The physics engine stays happy, and everything works as expected:
When used properly, rigid bodies are a powerful tool in your Godot toolkit. Many users get in trouble, however, when they use them for the wrong purposes, or fail to understand exactly how they work.