I will assume that you have physically correct motion for your ship, as otherwise this analysis will not hold. You need something stronger than efficiency to solve this problem properly.
Each thruster will produce two effects on the motion of the ship: linear and angular. These can be considered independently. If the thruster produces a force f
in a direction dir
, and is offset from the center of mass by a vector r
(not geometric center or the center of the sprite!), then the effect on the linear component is:
t = f * dir // f is a scalar, dir is unit length
The effect on angular velocity is given by the torque:
tau = f * <dir.x, dir.y, 0> CROSS <r.x, r.y, 0> // cross product
t
is an force vector (i.e. the linear thrust). tau
is a signed scalar which, when divided by the mass moment of inertia, will give the angular acceleration. It is important that dir
and r
are both in the same coordinate space, i.e. both in local coordinates or both in world coordinates.
The overall linear acceleration of the ship is given by the sum of the t
's for each thruster divided by the ship's mass. Similarly, the angular acceleration is just the sum of the torques divided by the mass moment of inertia (which is another scalar). The ship will not turn if the total torque is zero. Similarly, it will not move if the total thrust is zero. Recall torque is a scalar but thrust (the sum of the t
's) is a 2D vector.
The point of this exposition is that now we can write our problem as a Linear Program. Say first we want our ship to turn without moving. We have a variable for each thruster, $x_1, x_2, ...$, which is the amount of thrust that thruster will provide. One set of constraints is:
0 <= x_i < fmax_i //for each i
where fmax
is the maximum force for that thruster (this lets us have stronger or weaker ones). Next, we say that both equalities:
0 = Sum_i x_i * dir_i.x
0 = Sum_i x_i * dir_i.y
This encodes the constraint that we will not apply a linear acceleration, by saying the total thrust is zero (thrust is a vector, so we just say each part is zero).
Now we want our ship to turn. Presumably we want to do so as quickly as possible, so we want to:
max (Sum_i x_i * c_i)
where c_i = <dir_i.x, dir_i.y, 0> CROSS <r_i.x, r_i.y, 0>
Solving for the x_i
's while satisfying the inequalities and equalities above, while maximizing the summation above, will give us the desired thrust. Most programming languages have a LP library available for them. Just put the above problem into it and it will produce your answer.
A similar problem will let us move without turning. Say we re-write our problem in a coordinate system in which we want to move in the positive x direction. Then the constraints are:
0 <= x_i < fmax_i //for each i
max Sum_i x_i * dir_i.x
0 = Sum_i x_i * dir_i.y
0 = (Sum_i x_i * c_i)
where c_i = <dir_i.x, dir_i.y, 0> CROSS <r_i.x, r_i.y, 0> // as before
With the constraint that the thrusters can only produce thrust in a single direction, there are going to be limits to the kind of rotations and linear velocities that you will be able to achieve. This will manifest as the solution being 0 = x_1 = x_2 = ... = x_n
, which means you won't ever get anywhere. To mitigate this, I suggest adding a pair of small, weak (say 5%, or 10%) thrusters for each player placed thruster at 45-degrees on either side. This will give the solution more flexibility, because these can be used to counteract weak secondary effects of the main thrusters.
Finally, for up through maybe 100 thrusters, the solution to the LP is fast enough to be done per frame. However, because the solution does not depend on location or the current state, you can precompute the solution for each reasonable controller input combination whenever the shape changes (this includes adding non-thrusters which change the moment of inertia or the mass of the ship, because then the thrusters are in a different location relative to the center of mass!). This is 24 possibilities (i.e. 8 directions times {left spin, no rotation, right spin}).