1. Game Development
  2. Programming
Gamedevelopment

Using Torque and Thrusters to Move and Rotate a Player-Designed Spaceship

by
Difficulty:IntermediateLength:MediumLanguages:

While working on a game in which the spaceships are designed by players and can be partially destroyed, I encountered an interesting problem: moving a ship around using thrusters is not an easy task. You could simply move and rotate the ship around like a car, but if you want ship design and structural damage to affect ships' movement in a believable way, actually simulating thrusters could be a better approach. In this tutorial, I'll show you how to do this.

Assuming a ship can have multiple thrusters in various configurations, and that the ship's shape and physical properties can change (for example, parts of the ship could be destroyed), it is necessary to determine which thrusters to fire in order to move and rotate the ship. That's the main challenge we need to tackle here.

The demo is written in Haxe, but the solution can easily be implemented in any language. A physics engine similar to Box2D or Nape is assumed, but any engine that provides the means to apply forces and impulses and query the physical properties of bodies will do.

Try the Demo

Click the SWF to give it focus, then use the arrow keys and the Q and W keys to activate different thrusters. You can switch to different spaceship designs using the 1-4 number keys, and you can click any block or thruster to remove it from the ship.


Representing the Ship

This diagram shows the classes that represent the ship, and how they relate to each other:

Gamedev Maths and Physics: Using Torque and Thrusters to Correctly Maneuver a Player-Designed Spaceship

BodySprite is a class that represents a physical body with a graphical representation. It allows display objects to be attached to shapes, and makes sure that they move and rotate correctly with the body.

The Ship class is a container of modules. It manages the structure of the ship and deals with attaching and detaching modules. It contains a single ModuleManager instance.

Attaching a module attaches its shape and display object to the underlying BodySprite, but removing a module requires a bit more work. First the module's shape and display object are removed from the BodySprite, and then the structure of the ship is checked so that any modules not connected to the core (the module with the red circle) are detached. This is done using an algorithm similar to flood fill that takes into account the way each module can connect to other modules (for example, thrusters can only connect from one side, depending on their orientation).

Detaching modules is somewhat different: their shape and display object are still removed from the BodySprite, but are then attached to an instance of ShipDebris.

This way of representing the ship is not the simplest, but I found it to work very well. The alternative would be to represent each module as a separate body and "glue" them together with a weld joint. While this would make breaking the ship apart much easier, it would also cause the ship to feel rubbery and elastic if it had a large number of modules.

The ModuleManager is a container that keeps the modules of a ship in both a list (allowing easy iteration) and a hash map (allowing easy access via local coordinates).

The ShipModule class obviously represents a ship module. It's an abstract class that defines some convenience methods and attributes that each module has. Each module subclass is responsible for constructing its own display object and shape, and for updating itself if needed. Modules are also updated when they're attached to ShipDebris, but in that case the attachedToShip flag is set to false.

So a ship is really just a collection of functional modules: building blocks whose placement and type defines the behavior of the ship. Of course, having a pretty ship just floating around like a pile of bricks would make for a boring game, so we need to figure out how to make it move around in a way that is fun to play and yet convincingly realistic.


Simplifying the Problem

Rotating and moving a ship by selectively firing thrusters, varying their thrust either by adjusting throttle or by turning them on and off in quick succession, is a difficult problem. Fortunately, it is also an unnecessary one.

If you wanted to rotate a ship precisely around a point, for example, you could do that simply by telling your physics engine to rotate the whole body. In this case, however, I was looking for a simple solution that isn't perfect, but is fun to play. To make the problem simpler, I'll introduce a constraint:

Thrusters can only be on or off and they can't vary their thrust.

Now that we've abandoned perfection and complexity, the problem is a lot simpler. We need to determine, for each thruster, whether it should be on or off, depending on its position on the ship and the player's input. We could assign a different key for each thruster, but we'd end up with an interstellar QWOP, so we'll use the arrow keys for turning and moving, and Q and W for strafing.


The Simple Case: Moving the Ship Forwards and Backwards

The first order of business is to move the ship forwards and backwards, as this is the simplest possible case. To move the ship, we'll simply fire the thrusters facing in the direction opposite to the one we want to go. For example, if we wanted to go forward, we'd fire all the thrusters that face backwards.

Obviously, this will not always produce the desired effect. Due to the above constraint, if the thrusters aren't placed evenly, moving the ship could cause it to rotate. On top of that, it is not always possible to choose the right combination of thrusters to move a ship as needed. Sometimes, no combination of thrusters will move the ship the way we want. This is a desirable effect in my game, as it makes ship damage and bad ship design very obvious.

Gamedev Maths and Physics: Using Torque and Thrusters to Correctly Maneuver a Player-Designed Spaceship

A ship configuration that can't move backwards

Rotating the Ship

Gamedev Maths and Physics: Using Torque and Thrusters to Correctly Maneuver a Player-Designed Spaceship

In this example, it is obvious that firing thrusters A, D and E will cause the ship to rotate clockwise (and also drift somewhat, but that's a different problem altogether). Rotating the ship boils down to knowing in what way a thruster contributes to the rotation of the ship.

It turns out that what we're looking for here is the equation of torque - specifically the sign and magnitude of torque.

So let's take a look at what torque is. Torque is defined as a measure of how much a force acting on an object causes that object to rotate:

Gamedev Maths and Physics: Using Torque and Thrusters to Correctly Maneuver a Player-Designed Spaceship

Because we want to rotate the ship around its center of mass, our [latex]r[/latex] is the distance vector from the position of our thruster to the center of mass of the whole ship. The center of rotation could be any point, but the center of mass is probably the one a player would expect.

The force vector [latex]F[/latex] is a unit direction vector that describes the orientation of our thruster. In this case we don't care about the actual torque, only its sign, so it's okay to use just the direction vector.

Since cross product isn't defined for two dimensional vectors, we'll simply work with three dimensional vectors and set the [latex]z[/latex] component to 0, making the math simplify beautifully:

[latex]
\tau = r \times F \\
\tau = (r_x,\quad r_y,\quad 0) \times (F_x,\quad F_y,\quad 0) \\
\tau = (-0 \cdot F_y + r_y \cdot 0,\quad 0 \cdot F_x - r_x \cdot 0,\quad -r_y \cdot F_x + r_x \cdot F_y) \\
\tau = (0,\quad 0,\quad -r_y \cdot F_x + r_x \cdot F_y) \\
\tau_z = r_x \cdot F_y - r_y \cdot F_x \\
[/latex]

Gamedev Maths and Physics: Using Torque and Thrusters to Correctly Maneuver a Player-Designed Spaceship

The colored circles describe how the thruster affects the ship: green indicates the thruster causes the ship to rotate clockwise, red indicates it causes the ship to rotate counter-clockwise. The size of each circle indicates how much that thruster affects the ship's rotation.

With this in place, we can calculate how each thruster affects the ship individually. A positive return value indicates that the thruster will cause the ship to rotate clockwise, and vice-versa. Implementing this in code is very straightforward:


Conclusion

The demonstrated solution is easy to implement and works well for a game of this type. Of course, there is room for improvement: this tutorial and the demo don't take into consideration that a ship might be piloted by something other than a human player, and implementing an AI pilot that can actually fly a half-destroyed ship would be a very interesting challenge (one I'll have to face at some point, anyway).

Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.