4
\$\begingroup\$

I'm making a top-down 2D space ship game in which you rotate and thrust. I'd like to impose a maximum velocity for different engine types, meaning that a certain engine can only get you going up to a certain speed.

But I'd also like the ship to be able to travel faster than that maximum velocity, say if it were sent flying by a collision with a fast-moving asteroid.

I can't naively have the engines only apply impulse if the ship is moving at less than its maximum speed -- otherwise if an asteroid sent you flying to the right, you couldn't deliberately slow down because that would involve applying an impulse to the left.

But I'm not restricted to cardinal directions. If the ship is moving too fast to the right, I don't want it to accelerate to the right, but I would like it to be able to curve upward, which might involve applying some kind of impulse at say a 15 degree angle.

My other thought was to cap the maximum velocity of the ship at the maximum velocity of the engine, but then an asteroid collision could send you flying no faster than you engine's maximum speed, and that's undesirable as well.

How might I go about designing a system that allows one to reach velocities no higher than a set maximum from their own impulse, but still be able to steer their ship at higher velocities and still actually attain those higher velocities from external means?

Edit

Let's say my maximum velocity is 50 m/s. I'm traveling at 50 m/s at a 15 degree angle, which means my x velocity is 48.3 and my y velocity is 12.9. The ship is facing at a 30 degree angle (even though it's traveling along a 15 degree vector).

When I press the thrust button, I want the ship to trend toward that 30 degree angle of travel. Which means that its current angle of travel must be altered in some fashion. Right now I do so by applying impulses. But if I apply an impulse at a 30 degree angle, the ship's velocity will exceed is maximum (a good portion of that impulse is in the same direction as we're already going).

If I don't allow that impulse to be applied, the player can't adjust their course at all while they're moving too fast. And I'm not sure how to determine which components of an impulse would change a ship's velocity's angle without increasing its length.

\$\endgroup\$
  • \$\begingroup\$ You could restrict the impulse if below maximum speed. If the asteroid knocks you to the left and you try to move to the right, then your speed_going_right is absolutely less than the maximum. In fact, it's negative! \$\endgroup\$ – Draco18s Jan 18 '16 at 17:05
  • \$\begingroup\$ if speed > maximum speed = speed - whatever number that fits your needs also, if the speed is higher than the maximum speed don't increase the speed by the acceleration amount \$\endgroup\$ – DH. Jan 18 '16 at 17:13
  • \$\begingroup\$ @Draco18s I'm not traveling in cardinal directions, so I can't conceive of how to ignore the component of movement that's going too fast. Does my edit better explain my position? \$\endgroup\$ – Hammer Bro. Jan 18 '16 at 17:26
  • \$\begingroup\$ @DarkHyudrA I tried clamping the speed initially. By that logic, if you're moving too fast from a collision and try to thrust, you'll immediately slow down to whatever your engine's maximum speed was. That's like being able to stop on a dime when you're supposed to be out of control. \$\endgroup\$ – Hammer Bro. Jan 18 '16 at 17:28
  • 1
    \$\begingroup\$ @HammerBro. Its called Vector math. It's really quite simple to add two vectors together and check if the magnitude is greater than the maximum allowed. (-3,2) + (0.5,0.5) has a different magnitude than (3,2) + (0.5,0.5). The first is 3.53 the other is ~4.30, if your maximum speed is 4, then the former is allowable and the latter is not. \$\endgroup\$ – Draco18s Jan 18 '16 at 18:03
2
\$\begingroup\$

This problem is a bit difficult to think about because it's unphysical -- engines have a thrust which equates to acceleration and not top speed. So I think to fully answer this question, you need to remove it from a physical framework and just think of it in terms of vectors.

To be clear, I will use "speed" whenever I am talking about the overall quantity of movement of the ship, and "velocity" when there is some directionality associated with it.

You can use the directionality of velocity to your advantage here. Your engine impulse can only add velocity in the direction of impulse if the velocity in the direction of impulse is below the "engine speed" rating. You need to consider vector addition here for this to work the way you want it to. If the player impulses in a direction that is not orthogonal to direction of motion, then ONLY the amount of impulse applied in the orthogonal direction will actually increase the velocity in that direction, which means that you can change direction. To disallow the player from actually increasing speed in this situation, you need to remove velocity from the previous direction of motion so that the speed stays the same (using that you can calculate speed by adding the orthogonal velocities in quadrature).

This would allow for maneuvers at higher than engine speed given that your original speed is over engine speed due to some external mechanism, but it would not allow for acceleration past engine speed in any given direction.

EDIT

Here's an example of what I mean (using the conditions you included in your edit):

You would presumably have an acceleration value of some sort from your engine specifications. Let's just say it's 1 m/s/s for the sake of argument. Let's say you press the thrust key and hold it down for 1s at 30 degrees while your ship is travelling at 50 m/s at 15 degrees (giving vx = 48.296 m/s and vy = 12.941 m/s). You would then need to apply a 1 m/s change somehow, while still maintaining 50 m/s overall speed. So, first we must figure out what part of that 1m/s is orthogonal to your original direction and then decompose the 1 m/s along that basis.

If you draw it out (I'm not sure how to draw here) you can see that you just need to undergo a 15 degree rotation, so the direction of thrust along your current vector is 1 m/s * cos(15)=0.966 m/s and the direction orthogonal is 1 m/s * sin(15)=0.259 m/s. Here comes the "difficult" part: you now need to calculate the new direction based on these values. We already determined that the thrust along the vector will not affect anything, so we can discard the 0.966 m/s change and just apply a 0.259 m/s change in the orthogonal direction.

Since we have forced acceleration to only be orthogonal, we know that this is a "circular" change in velocity (it only changes direction and NOT speed -- exactly what you want!). In this case, the orthogonal direction is 15 + 90 = 105 degrees, so decompose that into your original x-y basis and you get that dvx = 0.259 m/s * cos(105) = -0.067 m/s and dvy = 0.259 m/s * sin(105) = 0.250 m/s. It's important that you get your angles right, because that negative sign in dvx is how the magnitude stays the same! Put this together and your resulting velocity after 1s of thrust at 1 m/s/s is vx = 48.296 m/s - 0.067 m/s = 48.229 m/s and vy = 12.941 m/s + 0.259 m/s = 13.200 m/s which gives you vt = sqrt(48.229*48.229 + 13.200*13.200) m/s = 50.003 m/s which is 50 up to the precision we calculated (3 decimal places). The computer will maintain float or double precision, but you still might want to cap it at previous speed manually or those rounding errors will accumulate.

\$\endgroup\$
  • \$\begingroup\$ Yeah, that's I think what I want to do. Think you could put up a more concrete example? \$\endgroup\$ – Hammer Bro. Jan 18 '16 at 18:00
0
\$\begingroup\$

This is how I would do that (pseudo-code):

class Vector {
    var float x;
    var float y;

    function Vector (float angle, float magnitude); // constructor
    function add(Vector);                           // adds new vector to current vector
    function angle(float new_angle);                // returns or sets angle
    function magnitude(float new_magnitude);        // returns or sets magnitude
}

class Ship {
    position = new Vector(0, 0); 
    velocity = new Vector(0, 0); 
    max_speed = 10; 
}

function update() {
    acceleration = impulse_key_pressed ? new Vector (ship.position.angle(), engine_power) : new Vector(0, 0);
    ship.position.add(acceleration);
    ship.position.add(asteroid_collision);
    if (ship.velocity.magnitude() > ship.max_speed)
        ship.velocity.magnitude(ship.velocity.magnitude() - 0.1);
}

Getting hit with an asteroid is just a one time addition of new acceleration to the current position. This acceleration is big enough to influence the current ship's velocity and increase it over its max speed. Afterwards the ship slows down gradually (by 0.1 of magnitude each update).

You can read up more on vectors e.g. in this answer.

\$\endgroup\$
  • \$\begingroup\$ That approach leads to a subtle trap: either you can still control your ship while it's moving too fast, which means that the deceleration needs to be more powerful than the ship's thrust (or you could maintain faster-than-desirable speeds), or the ship can't accelerate while moving too fast, which means you can't alter course until you've decelerated adequately. Or you crank up the deceleration, but then you don't really glide anymore. \$\endgroup\$ – Hammer Bro. Jan 19 '16 at 17:14
  • \$\begingroup\$ I assume the ship turnes left and right and can thrust only forward? I'll add turning when I'm at the keyboard. There shouldn't be a problem. When the ship is being hit by an asteroid the player has to overcome uncontrolled turning and then add thrust on the direction opposed to current velocity. Will that achieve what you want? \$\endgroup\$ – cprn Jan 19 '16 at 17:27
  • \$\begingroup\$ dannuic's answer has the math that I'm looking for (I believe; it'll be a bit before I can implement it). Reality is a bit more complicated, but it sounds like you're going down the path I first did that ended up being unsatisfactory. Calculating the orthogonal thrust and only applying that sounds like the appropriate general solution. \$\endgroup\$ – Hammer Bro. Jan 19 '16 at 17:54
  • \$\begingroup\$ Hey, give me a shout if you've managed. If not, I can make a working example in python just for fun. Vector based physics lets you implement any solution, really. Deceleration is just an effect of adding another vector. It's magnitude doesn't have to be greater than the ship's thrust because it influences the ship's current velocity with every frame / tick. \$\endgroup\$ – cprn Jan 5 '17 at 0:46

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.