# Asteroids-style Physics (using RigidBody2D)

## Problem

You want to use a `RigidBody2D` to create a semi-realistic spaceship, a la Asteroids.

## Solution

Using `RigidBody2D` can be tricky. Because it’s controlled by Godot’s physics engine, you need to apply forces rather than moving it directly. Before doing anything with rigid bodies, I highly recommend looking at the RigidBody2D API doc, and we’ll refer to it as we work through this example.

For this example, we’ll use the following node setup:

`````` RigidBody2D (Ship)
Sprite2D
CollisionShape2D
``````

We’ll use the following inputs in the Input Map:

InputKey
`thrust`w or ↑
`rotate_right`d or →
`rotate_left`a or ←

Add a script to the body, and let’s define some variables:

``````extends RigidBody2D

@export var engine_power = 800
@export var spin_power = 10000

var thrust = Vector2.ZERO
var rotation_dir = 0
``````

The first two variables are how we’ll control the ship’s “handling”. `engine_power` is going to affect acceleration and top speed. `spin_power` controls how fast the ship rotates.

`thrust` and `rotation_dir` are going to be set by pressing the inputs. Let’s do that next:

``````func get_input():
thrust = Vector2.ZERO
if Input.is_action_pressed("thrust"):
thrust = transform.x * engine_power
rotation_dir = Input.get_axis("rotate_left", "rotate_right")
``````

If we’re pressing the `"thrust"` input, we’ll set the `thrust` vector to the ship’s forward direction, while `rotation_dir` will be `+/-1` based on the rotate inputs.

We can start flying by applying those values in `_physics_process()`:

``````func _physics_process(_delta):
get_input()
constant_force = thrust
constant_torque = rotation_dir * spin_power
``````

It works, but you’ll notice that it’s very hard to control. The rotation is too fast, and it accelerates to a high speed before going offscreen. This is where we want to break from “real” space physics. In space, there’s no friction, but our Asteroids-style ship will be a lot easier to control if it coasted to a stop when we’re not thrusting. We can control this with damping.

In the `RigidBody2D` properties, you’ll find Linear/Damp and Angular/Damp. Set these to `1` and `2` respectively, and they’ll slow the movement/rotation as well as causing them to stop.

Feel free to experiment with these values and how they interact with the `engine_power` and `spin_power`

### Screen wrapping

Wrapping around the screen is really teleportation: when the ship goes off the right side of the screen, you teleport it to the left side. However, if you just tried to change the `position`, you’d find that it instantly snapped back. This is because the physics engine is trying to control the position as well.

The solution to this is to use the `_integrate_forces()` callback of the rigid body. In this function, you can safely update the physics properties of the object without conflicting with what the physics engine is doing.

Let’s get the screensize at the top of the script:

``````@onready var screensize = get_viewport_rect().size
``````

``````func _integrate_forces(state):
var xform = state.transform
xform.origin.x = wrapf(xform.origin.x, 0, screensize.x)
xform.origin.y = wrapf(xform.origin.y, 0, screensize.y)
state.transform = xform
``````

As you can see, the `_integrate_forces()` function includes a parameter called `state`. This object is the PhysicsDirectBodyState2D of our body. It contains all of the current physics properties such as the forces, velocity, position, etc.

From the state, we grab the current transform, modify it to wrap around the screen using `wrapf()`, and then set it back to the current state.

Here’s how it looks:

### Warping

Let’s look at one more example of using `_integrate_forces()` to alter the body’s state without issues. Let’s add a “warp” mechanic - when the player presses the `"warp"` input, the ship will teleport to a random spot on the screen.

First, we’ll add a new variable for this:

``````var teleport_pos = null
``````

Then, in `get_input()`, we’ll set a random position:

``````    if Input.is_action_just_pressed("warp"):
teleport_pos = Vector2(randf_range(0, screensize.x), randf_range(0, screensize.y))
``````

Finally, in `_integrate_forces()`, if there’s a `teleport_position` set, we’ll use it and then clear it:

``````    if teleport_pos:
physics_state.transform.origin = teleport_pos
teleport_pos = null
``````