7

Im building a small space shooter game. I have how ever stubbed on a math problem when it come to the space physics.

Describing this with words is following: There is a max speed. So if you give full full speed you ship will move with this over the screen over and over again like in the old asteroids games. If then release the rocket boost you ship should be keep moving with that speed over the screen.

Then the tricky part where Im stuck right now.

If you rotate the ship ANY angle and gives boost again the ship should try to get to this direction and NEVER surpas the max speed when it comes to how fast it is moving. so my question is. anyone have a good idea formula for this issue? feels like it has been done before if you know what to look for. :)

Ill add this small image to illustrate what is tried to be done with some vector calculations. Max speed movement with force

Red ring: Max speed

Green line: current ship direction.

Black line: direction(s) and how fast the ship is moveing in x and y.

Black ring: origin of movement.

Can illustrate it but hard to find a good math solution for this. :)

EDIT

This is the code Im using right now in every frame. It gives movement to the ship but does not give the force of movement the user has to counter-react with its rocket boosters to get the ship to stop or slow down. With this it stops the instant you release the accelerating speed for the ship.

    //Calculates ship movement rules
var shipVelocityVec = GetVectorPosByAngle(shipMoveSpeed, shipRotationAngle);
var shipUnitVec =$V([Math.cos(shipRotationAngle),Math.sin(shipRotationAngle),0]);
var rawAccel = shipAccelSpeed / shipMass;
var scale = (shipUnitVec.dot(shipVelocityVec))/(shipTopSpeed * shipTopSpeed);
var v1 = shipUnitVec.subtract(shipVelocityVec.multiply(scale));
var finalAccelVec = v1.multiply(rawAccel);
console.log(finalAccelVec);

//move ship according to rules
var shipPosVector = $V([shipxPos, shipyPos, 0]);
var movementVector =  shipPosVector.add(finalAccelVec);
shipxPos = movementVector.elements[0];
shipyPos = movementVector.elements[1];

To give the acceleration speed the user has to keep the button pressed. the instance the user releases the button the acceleration is set to zero and have to boost over again to give maximum acceleration throttle.

Solution found! Posted it here how it was done.

PaulR
  • 3,587
  • 14
  • 24
Jonas Lindahl
  • 752
  • 10
  • 24
  • 2
    I take it this is a 2d thing? Keep separate x and y velocity indicators. If you're thrusting at an angle, decompose that angled thrust into x and y components, allowing you to adjust the ship's x/y velocity components separately. At each speed adjustment, check the ship's speed and stop adjusting if you've reached max. – Marc B Feb 15 '12 at 14:28
  • Please correct me I'm misundestanding, but in image #3 isn't the combined vector for the speed larger than the red ring allows? – pimvdb Feb 15 '12 at 14:29
  • No, as both x and y added togher thay equals the circels max movement. the green line could be longer. In #3 the green line should be the full length of the diameter of the circle. – Jonas Lindahl Feb 15 '12 at 14:33
  • Separating X and Y movement is not a bad idea! – Jonas Lindahl Feb 15 '12 at 14:39
  • +1 for making diagrams with your question – Arend Feb 15 '12 at 14:45

4 Answers4

3

You seem to be confusing something - there is no issue capping the velocity to a maximum speed, even when the acceleration is at a different angle, if you are using vectors correctly.

Your setup should look something like this:

  1. Your ship should have a position, a velocity, and an acceleration. Each of these can be represented as a 2D vector (with separate x and y components).
  2. Every frame, add the velocity to the position, and the acceleration to the velocity.
  3. Every frame, check that the speed does not exceed some maximum. If it does, cap the speed by normalizing the velocity vector and multiplying it by the max speed.

That's it! There are no special cases to consider - that's the magic of vector algebra!

BlueRaja - Danny Pflughoeft
  • 84,206
  • 33
  • 197
  • 283
  • Add a quantity *proportional* to the velocity to the position, *proportional* to the acceleration to the velocity. – Michael J. Barber Feb 15 '12 at 15:09
  • @Michael: That depends on the units (pixels/second vs. pixels/frame). :) – BlueRaja - Danny Pflughoeft Feb 15 '12 at 15:15
  • Not really. Unless your frames are all evenly spaced in time, you can't absorb the constants into the units. This is a calculus problem - it's about integration, not addition. – Michael J. Barber Feb 15 '12 at 15:26
  • I think I actually have to go with Michaels concept here as there should be a force from previous actions here that has to be dealt with to get the resulting direction of travel and speed. – Jonas Lindahl Feb 15 '12 at 18:04
  • @Jonas: Michael is talking about using a [variable-step framerate rather than a fixed-step](http://gamedev.stackexchange.com/questions/1589/fixed-time-step-vs-variable-time-step). Variable-rate is more beneficial, but fixed-step is easier to implement, so more common for smaller games. I would recommend starting out with fixed-step, due to its ease-of-use. This has nothing to do with drag-forces (as mentioned in your comments to Michael's answer). – BlueRaja - Danny Pflughoeft Feb 15 '12 at 18:34
  • @Michael: Yes, in fixed-step framerate games (see above comment), the frames are all evenly-spaced in time, and every frame rendered corresponds to exactly one "tick" of the game/physics engine. The setup I have in my answer is how nearly all fixed-step games work. – BlueRaja - Danny Pflughoeft Feb 15 '12 at 18:37
  • @BlueRaja-DannyPflughoeft I'm talking about physics, actually. What you're missing is that you *can't add a velocity to a position.* These are solutions to dynamical equations; handwaving that away as if you can just casually add any vectors is not a good way to understand the concept. There are so many assumptions needed to make that work -- none that clearly hold from the question -- that it really demands explication. – Michael J. Barber Feb 15 '12 at 21:36
  • @Michael: I understand you're a physicist, but we're talking about game programming here, and this is how it's done in game programming. There is no need to understand vector calculus - with a fixed-step time increment, it is perfectly valid to add the velocity to the position *(the velocity is measured in units-per-tick, and you are updating the position once per tick)*. – BlueRaja - Danny Pflughoeft Feb 15 '12 at 21:43
  • @BlueRaja-DannyPflughoeft With the units you've stated, you're still not adding a velocity to a position. It's pretty clear that the OP lacks the background to know what assumptions are needed to make that work; your answer would be much improved if you made clear that the representation of the state vectors can be chosen to simplify the handling of time (under the assumption of constant dt). Then your operational description wouldn't need to depend on an incorrect conceptual description. – Michael J. Barber Feb 15 '12 at 22:00
1

Rather than just imposing an ad hoc maximum speed, you could use some actual physics and impose a drag force. This would be an extra force acting on the spaceship, directed opposite to the velocity vector. For the magnitude of the drag force, it's simplest to just take it proportional to the velocity vector.

The overall effect is that the drag force increases as the spaceship moves faster, making it harder to accelerate in the direction of motion when the ship moves faster. It also makes acceleration easier when it is opposed to the direction of motion.

One point where this diverges from your description is that the spaceship won't continue at maximum speed forever, it will slow down. It won't, however, come to a halt, since the drag force drops as the ship slows down. That matches my memory of asteroids better than the ship continuing forever at constant velocity, but it has been quite a while since I've played.

Michael J. Barber
  • 24,518
  • 9
  • 68
  • 88
  • Yes, it correct. The drag force is also what Im after and intend to simulate. I just made this with out the dropping drag force to make it easier to try to explain what I was after to start with. – Jonas Lindahl Feb 15 '12 at 18:09
  • This dragforce must however be able to be slowly modified by the ships current directional force. Right? – Jonas Lindahl Feb 15 '12 at 18:16
  • @Jonas: It appears you need to brush up on your [vector math](http://channel9.msdn.com/coding4fun/articles/2D-Game-Primer-Visual-C). There is again no special case to consider here - vectors add independently, so you can simply add drag-acceleration to the velocity every frame, then immediately add the acceleration due to user input, without any issues. – BlueRaja - Danny Pflughoeft Feb 15 '12 at 18:32
  • @Jonas The drag force is not modified by any other force, it's a function of velocity. I've really no idea what you mean by "the ships current directional force" - thrust, maybe? Anyway, this is just basic Newtonian mechanics: the acceleration of a body is proportional to the sum of all the forces acting on the body. So you add up the forces and wind up with a total force vector, which is handled the same as you would the total force vector if it were just the thrust from the spaceship. – Michael J. Barber Feb 15 '12 at 21:25
1

@BlueRaja's solution should work, although you will get an abrupt change in behavior when you hit the max speed.

If you want a solution with no "seams", I believe you can do what you're looking for by adding the right kind of adjustment to the acceleration, as follows:

ship_unit_vec = [cos(ship_angle), sin(ship_angle)]
raw_accel = (engine_thrust / ship_mass)

scale = dot_product(ship_unit_vec, ship_velocity_vec) / max_speed^2

final_accel_vec = raw_accel * (ship_unit_vec - scale * ship_velocity_vec)

Notes:

  • If |ship_velocity_vec|<<max_speed, the scale * ship_velocity_vec component is negligable.
  • If |ship_velocity_vec|==max_speed, the scale * ship_velocity_vec component cancels all additional acceleration in the "wrong" direction, and aids acceleration in the "right" direction.
  • I've never tried this out, so I don't know how it will feel to the player...

More generally, if there are more sources of acceleration than just the ship thrusters, you can add them all together (say, as raw_accel_vec), and perform the above operation all at once:

scale_forall = dot_product(raw_accel_vec, ship_velocity_vec) / max_speed^2
final_accel_vec = raw_accel_vec - scale_forall * ship_velocity_vec
comingstorm
  • 25,557
  • 3
  • 43
  • 67
  • Excellent example! Never thought of giving the ship a mass. Only thought of the forces that was applied for it at the moment and use that as base for the changes. Will do some modification to my code tomorrow morning see how something like this could work. – Jonas Lindahl Feb 15 '12 at 20:25
  • This answer introduces a drag force exactly as Michael suggested. Though, I'm not sure why you added a mass *(it only affects the acceleration by a constant multiple, so can be left out)*, and for some reason you decided to turn the single, beautiful vector equation into multiple, more convoluted equations. – BlueRaja - Danny Pflughoeft Feb 15 '12 at 21:54
  • There is a difference: the "drag" here is proportional to the acceleration and the *square* of the velocity, and is scaled appropriately to yield a given `max_speed` regardless of the "force" applied. – comingstorm Feb 15 '12 at 23:11
  • I added the mass (and the separation between magnitude and direction of thrust) to try and make the presentation more concretely appropriate to the question -- but I will take your suggestion and add a simpler and more abstract equation. – comingstorm Feb 15 '12 at 23:12
0

I did it! Thank you for your help.

Finally found the solution. the problem was I was trying to modify the ships current movement when it comes to speed and then of this calculate the "drag" forces that would be the product of this movement when the user tried to go another direction. The solution was like @BlueRaja and @Comingstorm mentioned. All forces should be added together when it comes to the movement. This should be what then alter the ships position. Should not be added to the ships current movement. You might be able to effect a current movement to but then you have to do this differently. So I thought I share my solution for this how it looks.

This function is run each time the user accelerates the ship.

function CalcShipMovement() {
//Calculates ship movement rules
shipPosVector = $V([shipxPos, shipyPos, 0]);
var shipVelocityVec = GetVectorPosByAngle(shipAccelSpeed, shipRotationAngle);
var shipUnitVec = $V([Math.cos(shipRotationAngle), Math.sin(shipRotationAngle), 0]);

if(currentShipMoveVector != null && Get2DVectorLength(currentShipMoveVector) > 0) {
    var nextMove = currentShipMoveVector.add(shipVelocityVec);
    var nextSpeed = Get2DVectorLength(nextMove);
    //check if topspeed of movement should be changed
    if(nextSpeed > shipTopSpeed) {
        var scale = nextSpeed / shipTopSpeed;
        currentShipMoveVector = DevideVector(nextSpeed, scale);
    } else {
        currentShipMoveVector = currentShipMoveVector.add(shipVelocityVec);
    }
}
if(currentShipMoveVector != null && Get2DVectorLength(currentShipMoveVector) == 0) {
    currentShipMoveVector = currentShipMoveVector.add(shipVelocityVec);
}}

This code is run in every frame the graphics for the ship is generated to alter its position.

function SetShipMovement() {
if(currentShipMoveVector != null && Get2DVectorLength(currentShipMoveVector) > 0) {
    shipMoveSpeed = Get2DVectorLength(currentShipMoveVector);
    shipPosVector = shipPosVector.add(currentShipMoveVector);
    shipxPos = shipPosVector.elements[0];
    shipyPos = shipPosVector.elements[1];
    //Makes the ship slow down if no acceleration is done for the ship
    if(shipAccelSpeed == 0) {
        currentShipMoveVector = currentShipMoveVector.subtract(DevideVector(currentShipMoveVector, 50));
    }
} else {
    currentShipMoveVector = $V([0, 0, 0]);
}}
Jonas Lindahl
  • 752
  • 10
  • 24