In a game that I'm making, the idea is that the mouse cursor is representing a center of mass and there are objects orbiting around it.
The cursor itself is not an object and there is no collision between the cursor and the objects.
The ideal method would be the one where I can enter x and y coordinates of the target, and maybe the orbit radius, and the object would go into orbit and would be accelerating constantly.
When I move the mouse, the object would follow; and when I stop it, it would fall back into the same orbit radius.
The objects acceleration should also be able to change so it gives an effect that the mouse has more or less mass.
I thought the best way to achieve this would probably be with vectors.
In the Projectile class I gave each projectile 4 fields: x coordinate, y coordinate, PVector speed and PVector acc (acceleration).
The first thing I tried is giving the object constant acceleration. Speed vector starts as zero, the acceleration vector is calculated, its magnitude is then set to some value and it is added to the speed vector.
This produced very large orbits, slow acceleration and when the mouse is moved, the orbit just became larger.
Next I tried making the acceleration increase when it is closer to the center of mass inspired by the actual planets. This did not work either. The orbits were still too large and not controllable. Kind o disappointed I tried making the acceleration constantly increasing which gave the best results. The orbit is now getting smaller, but it just keeps decreasing to some radius and then stops. This would be perfect if the radius at which it stops decreasing would be configurable, but no matter the parameters the orbit is always too large and the orbit size keeps decreasing frustratingly slowly. My guess is that the acceleration and speed somehow end up in an equilibrium, and then the orbit stops decreasing.
void move(float x,float y,float accm)
{
PVector target = new PVector(x, y);
PVector ball = new PVector(this.x, this.y);
acc = PVector.sub(target, ball);
acc.setMag(accm);
speed.add(acc);
println(acc.mag() + " " + speed.mag());
this.x = this.x + speed.x;
this.y = this.y + speed.y;
}
This is the function that gave the best results. The function is called in draw():
if(mousePressed == true)
{
for(i=0;i<nproj;i++)
{
a[i].move(mouseX, mouseY,k);
}
k += n;
}
k is the number given to the function and n is the rate at which acceleration increases. I tried many different acceleration magnitudes and different rates of acceleration and just couldn't figure it out. Nproj is the number of projectiles and a is the name of the projectile array.
I found that for k = 0 and n = 0.002 it gave the most reliable and stable results. If n is larger (around 0.01 or larger), the objects sometimes randomly flies off way further than expected, and it actually increases the orbit radius. As the acceleration increases this happens more often, and the object sometimes ends up off the screen and then it never comes back. I know that orbit radius doesn't exist because the orbits are elliptical, but it just makes it easier to explain.
Update:
This is what I came up with so far
void move(float x,float y,float accm)
{
PVector target = new PVector(x, y);
PVector ball = new PVector(this.x, this.y);
acc = PVector.sub(target, ball);
acc.setMag(accm);
vel.add(acc);
this.x = this.x + vel.x;
this.y = this.y + vel.y;
vel.limit(15);
}
and in draw()
if(mousePressed == true)
{
for(i=0;i<nproj;i++)
{
a[i].move(mouseX, mouseY,k);
}
k += n;
if(k > 25)
n = 0;
}
By limiting the velocity vector and constantly increasing acceleration vector, magnitude difference begins to increase and since the acceleration vector is increasing and the velocity vector is constant and acceleration vector points towards the target the sum of the vectors starts slowly pointing more and more towards the target. The condition if(k < 25) is there to limit the acceleration and by changing the limit of the acceleration you can change the orbit radius.
I think you probably would have gotten a quicker answer over at https://physics.stackexchange.com/ and framing your question as "how do I simulate gravity in a game?"
Anyway I will give it a try...
In the class Projectile I gave each projectile 4 fields: x coordinate, y coordinate PVector speed and PVector acc
Sounds good so far, except you don't need to store the acceleration vector with the object. You'll calculate a new acceleration vector for each object on each "tick" of your simulation.
I would use standard physics terminology here and call each object's vector velocity instead of speed.
Next I tried making the acceleration increase when it is closer to the center of mass inspired by the actual planets.
This behavior should just happen naturally if you use the right algorithm for calculating an orbit.
Your "sun" and the "planets" each have a mass. Masses attract each other with a gravitational force, which is F = G * m1 * m2 / d^2 from Wikipedia.
Remember that Force = mass * acceleration, and you have the acceleration as a = m * G / d^2 where m is the mass of the other object (in this case the sun), and d is the distance between the two objects. So the important part is that the acceleration of an object due to gravity varies with the distance squared between the two objects.
So your basic algorithm for a single tick of your simulation looks like this:
For each planet, calculate an acceleration vector
Use the distance to the "sun" squared as the magnitude of the acceleration vector (times a constant value that depends on your "gravitational constant" and your sun's mass), and the direction is of course the direction from the planet towards the sun
Add this acceleration vector to the velocity vector for the planet
Add the velocity vector to the (x, y) position of the planet to move it forward.
With appropriate values for the length of each simulation tick, the gravitational constant, and the sun's mass, this should start to look pretty reasonable.
Since you're assuming that the planets are all independent and cannot collide, you can just do the calculation independently for each planet.
However, I think the game might be more interesting with a more accurate simulation. In reality, every object has a gravitational force on every other object. This would mean looping over all pairs of objects and doing the same kind of acceleration/velocity/position calculation.
The point of the game could then be to get the "planets" from one side of the screen to the other, in increasingly difficult starting configurations as they are all affecting each other and moving in different directions.
In order to make all of this work well, it would be important to set the mass of the "sun" many times higher than the mass of the planets, and to choose a reasonable value for your gravitational constant based on the timing of your simulation. You'll have to play with these constants to find the best results.
Related
I am writing an aimbot script for my AI, I am a bit confused about how to get the vertical projectile motion formulas to help me find the time and initial velocity required to fire the projectile at so it hits its target, For the target, I have its velocity and vertical displacement, I thought a Quadratic formula to find the intersection of its displacement over time intersecting with the projectiles displacement over time would be the solution but now I am stuck because I am missing the initial velocity as well as the time, Any help would be appreciated :)
Orient your coordinate plane such that the y-axis is up/down from your perspective, and the z-axis passes through you and your target (+ being towards the target). We do not care about x. Center the origin on you.
We need to first talk about finding the time it takes a projectile to travel from you to the target. Assuming there's no air drag, the projectile will move at a constant velocity along the z axis (it may accelerate along the y-axis, however). This gives us:
df_pz = a_pz * t**2 / 2 + vi_pz * t + di_pz
df_pz : final displacement - projectile's z component
di_pz : initial displacement - projectile's z component
a_pz : acceleration - projectile's z component
vi_pz : initial velocity - projectile's z component
t : time
If we assume there's no air resistance, a_pz is zero, and since the origin is centered on you, di_pz is zero. That leaves:
df_pz = vi_pz * t
We know df_pz is the distance to the target along the z-axis, and can solve for time t for the projectile to travel there. t will be defined in terms of vi_pz, which is fine.
Now, lets talk about your target's motion. Your target will follow a trajectory:
df_ty = a_ty * t**2 / 2 + vi_ty * t + di_ty
df_ty : final displacement - target's y component
di_ty : initial displacement - target's y component
a_ty : acceleration - target's y component
vi_ty : initial velocity - target's y component
t : time
We know t however (or, have an equation we can substitute in anyways), so we can plug in and solve for the final displacement (in terms of vi_pz).
With the df_ty value, we can now determine the initial y velocity of the projectile. We'll be using a similar formula:
df_py = a_py * t**2 / 2 + vi_py * t + di_py
df_py : final displacement - projectile's y component
di_py : initial displacement - projectile's y component
a_py : acceleration - projectile's y component
vi_py : initial velocity - projectile's y component
t : time
We have time t, the a_py may or may not be equal to a_ty, depending on the game engine, but I'll assume you know that value regardless. We know di_py is zero since we're centered at the origin. That just leaves vi_py and df_py. Since we want to intercept the target, df_py equals df_ty.
We then plug in those values and solve for vi_py. This gives us the initial velocity of the projectile along the y axis, in terms of vi_pz.
Now, with this newest equation, we'll actually solve for vi_py and vi_pz. Take the equation:
|V|**2 = vi_py**2 + vi_pz**2
|V| - magnitude of the initial velocity vector
The value |V| should be constant for any shot (assuming you can't "charge" you shot to shoot the projectile faster / further).
Take whatever this value is, substitute the value for vi_py (defined in terms of vi_pz) and solve for vi_pz. This will give you a concrete value for vi_pz. Take this, plug it back into the equation for vi_py, and obtain a concrete value for vi_py. Do the same for t.
Now, there may be one issue- df_ty we used earlier may be wrong. If df_ty is so low that it places the target below the ground, we need to clamp it to ground-level. Plug in vi_pz and check. This is just a simple if statement, I won't be writing a formula for this. You do need to know what y coordinate is "ground-level" below the target.
If this happens, also recompute a new vi_py, vi_pz, and t using this new df_ty. The process will be much the same, except you already have a concrete value for df_ty now, so you can skip the part involving |V| to get vi_py.
And there you have it: vi_py, vi_pz, and t - the velocity and time necessary to intercept the target. You may have to take this one step further and calculate the angle to shoot at, which would just be arctan(vi_py/vi_pz).
Finding the time tmin where the missile and target are at the closest approach is fairly straightforward.
Consider at the time of launch t=0 the target missile is at the origin, with an initial velocity vector v and being tracked as a function of time as
p(t) = v t + 0.5 g t2
where g is the gravity vector (usually pointing downwards).
At the time of launch, the target is at position vector o, moving with velocity vector u and being tracked over time with
r(t) = o + u t + 0.5 g t2
The time of minimum distance is
tmin = (o·(v-u)) / ((v-u)·(v-u))
Here · is the vector dot product and it results in a number.So both the numerator and denominators are numbers.
The closest approach distance is
dmin = √( (o·o) - (o·(v-u))2 / ((v-u)·(v-u)) )
The next part is harder. To optimize the problem, in order to make the minimum distance zero, and find the velocity vector that fits the constraints (max. speed, or time to impact).
The insight here is that the launch velocity vector must be on the plane defined by the target velocity vector and gravity. The simplest approach here is to employ a numerical method to estimate the minimum distance as a function of the launch angle if the launch speed is fixed.
There is an analytical solution here but is very complex and it depends on some assumptions.
Furthermore, once the optimal trajectory is found, you can play around to see which launch speed works best. With some random testing I did I noticed the required speed at the optimal angle as a function of a delay from detection jumps to a really high value at some point, and then gradually decreases. I suppose this corresponds to the time the target is mostly overhead. Optimally you want to detect the target as early as possible.
I want to simulate particles driven by wind on a three.js globe. The data I have is a Vector3 for the position of a particle and a Vector2 indicating wind speed and direction, think North/East. How do I get the new Vector3?
I've consulted numerous examples and read the documentation and believe the solution involves quaternions, but the axis of rotation is not given. Also, there are thousands of particles, it should be fast, however real-time is not required.
The radius of the sphere is 1.
I would recommend you have a look at the Spherical class provided by three.js. Instead of cartesian coordinates (x,y,z), a point is represented in terms of a spherical coordinate-system (θ (theta), φ (phi), r).
The value of theta is the longitude and phi is the latitude for your globe (r - sphereRadius would be the height above the surface). Your wind-vectors can then be interpreted as changes to these two values. So what I would try is basically this:
// a) convert particle-location to spherical
const sphericalPosition = new THREE.Spherical()
.setFromVector3(particle.position);
// b) update theta/phi (note that windSpeed is assumed to
// be given in radians/time, but for a sphere of size 1 that
// shouldn't make a difference)
sphericalPosition.theta += windSpeed.x; // east-direction
sphericalPosition.phi += windSpeed.y; // north-direction
// c) write back to particle-position
particle.position.setFromSpherical(sphericalPosition);
Performance wise this shouldn't be a problem at all (maybe don't create a new Spherical-instance for every particle like I did above). The conversions involve a bit of trigonometry, but we're talking just thousands of points, not millions.
Hope that helps!
If you just want to rotate a vector based on an angle, just perform a simple rotation of values on the specified plane yourself using trig as per this page eg for a rotation on the xz plane:
var x = cos(theta)*vec_to_rotate.x - sin(theta)*vec_to_rotate.z;
var z = sin(theta)*vec_to_rotate.x + cos(theta)*vec_to_rotate.z;
rotated_vector = new THREE.Vector3(x,vec_to_rotate.y,z);
But to move particles with wind, you're not really rotating a vector, you should be adding a velocity vector, and it 'rotates' its own heading based on a combination of initial velocity, inertia, air friction, and additional competing forces a la:
init(){
position = new THREE.Vector(0,0,0);
velocity = new THREE.Vector3(1,0,0);
wind_vector = new THREE.Vector3(0,0,1);
}
update(){
velocity.add(wind_vector);
position.add(velocity);
velocity.multiplyScalar(.95);
}
This model is truer to how wind will influence a particle. This particle will start off heading along the x axis, and then 'turn' eventually to go in the direction of the wind, without any rotation of vectors. It has a mass, and a velocity in a direction, a force is acting on it, it turns.
You can see that because the whole velocity is subject to friction (the multscalar), our initial velocity diminishes as the wind vector accumulates, which causes a turn without performing any rotations. Thought i'd throw this out just in case you're unfamiliar with working with particle systems and maybe were just thinking about it wrong.
So I am making a program, where you can have two objects (circles). I want them to orbit like planets around each other, but only in 2D.
I know that using Newtons Universal Law of Gravitation I can get the force between the two objects. I also know A = F / M. My question is how would I take the A from the previous equation and change it into a vector?
You need to use vector equations:
// init values (per object)
double ax=0.0,ay=0.0,az=0.0; // acceleration [m/s^2]
double vx=0.0,vy=0.0,vz=0.0; // velocity [m/s]
double x=0.0, y=0.0, z=0.0; // position [m]
double m=1.0; // mass [kg]
// iteration inside some timer (dt [seconds] period) ...
int i; double a,dx,dy,dz; // first compute acceleration
for (ax=0.0,ay=0.0,az=0.0,i=0;i<obj.num;i++)
if (obj[i]!=this) // ignore gravity from itself
{
dx=obj[i].x-x;
dy=obj[i].y-y;
dz=obj[i].z-z;
a=sqrt((dx*dx)+(dy*dy)+(dz*dz)); // a=distance to obj[i]
a=6.67384e-11*(obj[i].m*m)/(a*a*a); // a=acceleration/distance to make dx,dy,dz unit vector
ax+=a*dx; // ax,ay,az = actual acceleration vector (integration)
ay+=a*dy;
az+=a*dz;
}
vx+=ax*dt; // update speed via integration of acceleration
vy+=ay*dt;
vz+=az*dt;
x+=vx*dt; // update position via integration of velocity
y+=vy*dt;
z+=vz*dt;
Code is taken from here
obj[] is list of all your objects
obj.num is the count of them
I recommend to create object class with all the variables inside (ax,ay,az,...m), init them once and then continuously update (iteration) in some timer. If you want more accuracy then you should compute ax,ay,az for all objects first and only after update speed and position (to avoid change of position of objects during gravity computation). If you want to drive an object (like with truster) then just add its acceleration to ax,ay,az vector)
Now to setup an orbit just:
place planet object
must be massive enough and also set its position / velocity to what you want
place satellite
Initial position should be somewhere near planet. It should not be too massive. Init also speed vector with tangent direction to orbiting trajectory. If speed is too low it will collapse into planet and if speed is too high it will escape from planet otherwise will be orbiting (circle or ellipse)
timer
lower the interval better the simulation usually 10ms is OK but for massive and far objects is also 100ms and more OK. If you want particles or something then use 1ms (very dynamic sceene).
I strongly recommend to read this related QA:
Is it possible to make realistic n-body solar system simulation in matter of size and mass?
especially [edit3] about the integration precision and creating orbital data.
With two objects you are probably best using an ellipse which is the path the objects will follow about their common center of mass. Read Kepler's laws of planetary motion which gives the background.
If one object is a much greater mass than the other, i.e. a sun and a planet you can have one stationary and the other taking an elliptical path. The equation of the ellipse is given by
r = K e / ( 1 + e cos(theta))
K is a constant giving the size and e is the eccentricity. If you want an elliptical orbit have 0 < e < 1 the smaller it is the more circular the orbit. To get x, y coordinates from this use, x = r cos(theta), y = r sin(theta). The missing bit is time and how the angle is dependant on time. This is where the second and third laws come in. If a and b are the semi-major and semi-minor lengths of the ellipse, and P is the period then
0.5 * P * r^2 theta'= pi a b
theta' is the rate of change of angle with respect to time (d theta/d t). You can use this to get how much theta will change given a increase in time. First work out the current radius r0 given the current angle th0 if the time increment is δt then the angle increment δtheta is
δtheta = 2 pi * a * b / r^2 * δt
and the next angle is th0 + δtheta.
If the masses are of similar magnitude then see two body problem. Both objects will have elliptical orbits, there are two patterns which you can see in animations on that page. The ellipses will follow the same formula as above with the focus at the common center of mass.
If you have three object things get considerably harder and there are not generally neat solutions. See three body problem for this.
So I have a ship, that has thrusters at the bottom and that can only use these to move forward. It can also rotate itself around its center. Its thrusters gives it acceleration, so it doesn't move at a constant velocity. What I want to do is to tell it "move to point B".
I have come up with a solution but it doesn't work very well and it doesn't rotate smoothly, it moves jerkily and it doesn't end up exactly where it should be, so I have to have a big margin of error.
Is this a normal problem, and if so is there a "standard" way of doing it? Is this an easy problem? I want to make it look like the ship is steering itself to that point, using the constraints (thrusters, rotation) the player has. This excludes just lerping it from point A to B. Or does it?
I'd love some help in solving this problem. Positions are stored in vectors, and it's a 2D problem. Just for reference I'm including my solution, which basically is accelerating the ship until and rotating it to point to the point. I think my implementation of this idea is the problem:
Vector diff = vector_sub(to_point, pos);
float angle = vector_getangle(diff);
float current_angle = vector_getangle(dir);
float angle_diff = rightrange(angle) - rightrange(current_angle);
float len = vector_getlength(diff);
// "Margin of error"
float margin = 15.0;
// Adjust direction, only if we're not stopping the next thing we do (len <= margin)
if ( len > margin && fabs(angle_diff) > 2.0 )
{
dir = vector_setangle(dir, current_angle + (angle_diff)*delta*(MY_PI) - MY_PI/2);
}
else if ( len > margin )
{
dir = vector_normalize(diff);
}
// accelerate ship (if needed)
acc.x = acc.y = speed;
acc = vector_setangle(acc, vector_getangle(dir));
if ( len <= margin )
{
// Player is within margin of error
}
If you are not looking for a very general solution that works online, then there is a simple solution. What I mean by online is continuously re-calculating the actions along the complete trajectory.
Assuming the ship is at rest at start, simply rotate it towards your target point (while still at rest). Now, your ship can reach the target by accelerating for t seconds, rotating back while in motion (for 0.5 seconds as per your constraint), and decelerating for another t seconds. If the distance between current point and destination is d, then the equation you need to solve is:
d = 0.5*a*t^2 + 0.5*a*t + 0.5*a*t^2
The first term is distance traveled while accelerating. The second term is distance traveled while rotating (v*t_rot, v=a*t, t_rot=0.5). The final term is the distance traveled while decelerating. Solve the above for t, and you have your trajectory.
If the ship is moving at start, I would first stop it (just rotate in opposite direction of its speed vector, and decelerate until at rest). Now we know how to reach destination.
The problem with offline trajectory calculation is that it is not very accurate. There is a good chance that you will end up in the vicinity of the target, but not exactly on top of it.
Let's make the problem a little more interesting: the ship cannot rotate without acceleration. Let's call this acceleration vector a_r, a vector that is at a certain angle against the ship's direction (somewhat like having a thruster at an angle at the back). Your task now is to rotate the ship and accelerate in such a direction that the speed component perpendicular to the vector connecting the current position to the target is canceled out. Instead of trying to calculate the vectors offline, I would go with an online approach with this.
The easiest thing to do would be to add the following algorithm calculated at every time interval:
Calculate the vector pointing from ship to destination.
Split your current speed vector into two components: towards the destination, and perpendicular to it.
If perpendicular speed is zero, skip 4
Start rotating towards the negative of the perpendicular vector's direction. If already looking away from it (not exact opposite, but just looking away), also fire main thruster.
This will oscillate a bit, I suspect it will also stabilize after a while. I must admit, I don't know how I would make it stop at destination.
And the final approach is to model the ship's dynamics, and try to linearize it. It will be a non-linear system, so the second step will be necessary. Then convert the model to a discrete time system. And finally apply a control rule to make it reach target point. For this, you can change your state-space from position and speed to error in position and (maybe) error in speed, and finally add a regulation control (a control loop that takes the current state, and generates an input such that the state variables will approach zero).
This last one is fairly difficult in the maths compartment, and you'd probably need to study control engineering a bit to do it. However, you'll get much better results than the above simplistic algorithm - which admittedly might not even work. In addition, you can now apply various optimization rules to it: minimize time to reach target, minimize fuel consumption, minimize distance traveled, etc.
I'm trying to make a top-down spaceship game and I want the movement to somewhat realistic. 360 degrees with inertia, gravity, etc.
My problem is I can make the ship move 360° with inertia with no problem, but what I need to do is impose a limit for how fast the engines can go while not limiting other forces pushing/pulling the ship.
So, if the engines speed is a maximum of 500 and the ship is going 1000 from a gravity well, the ship is not going to go 1500 when it's engines are on, but if is pointing away from the angle is going then it could slow down.
For what it's worth, I'm using Construct, and all I need is the math of it.
Thanks for any help, I'm going bald from trying to figure this out.
Take a page from relative physics, where objects cannot exceed the speed of light:
(See below for my working C++ code snippet and running demo [Windows only].)
Set the constant c to the maximum speed an object can reach (the "speed of light" in your game).
If applying a force will increase the speed of the object, divide the acceleration (change in velocity) by the Lorentz factor. The if condition is not realistic in terms of special relativity, but it keeps the ship more "controllable" at high speeds.
Update: Normally, the ship will be hard to maneuver when going at speeds near c because changing direction requires an acceleration that pushes velocity past c (The Lorentz factor will end up scaling acceleration in the new direction to nearly nothing.) To regain maneuverability, use the direction that the velocity vector would have been without Lorentz scaling with the magnitude of the scaled velocity vector.
Explanation:
Definition of Lorentz factor, where v is velocity and c is the speed of light:
This works because the Lorentz factor approaches infinity as velocity increases. Objects would need an infinite amount of force applied to cross the speed of light. At lower velocities, the Lorentz factor is very close to 1, approximating classical Newtonian physics.
Graph of Lorentz factor as velocity increases:
Note: I previously tried to solve a similar problem in my asteroids game by playing with friction settings. I just came up with this solution as I read your question^^
Update: I tried implementing this and found one potential flaw: acceleration in all directions is limited as the speed of light c is approached, including deceleration! (Counter-intuitive, but does this happen with special relativity in the real world?) I guess this algorithm could be modified to account for the directions of the velocity and force vectors... The algorithm has been modified to account for directions of vectors so the ship does not "lose controllability" at high speeds.
Update: Here is a code snippet from my asteroids game, which uses the Lorentz factor to limit the speed of game objects. It works pretty well!
update:* added downloadable demo (Windows only; build from source code for other platforms) of this algorithm in action. I'm not sure if all the dependencies were included in the zip; please let me know if something's missing. And have fun^^
void CObject::applyForces()
{
// acceleration: change in velocity due to force f on object with mass m
vector2f dv = f/m;
// new velocity if acceleration dv applied
vector2f new_v = v + dv;
// only apply Lorentz factor if acceleration increases speed
if (new_v.length() > v.length())
{
// maximum speed objects may reach (the "speed of light")
const float c = 4;
float b = 1 - v.length_squared()/(c*c);
if (b <= 0) b = DBL_MIN;
double lorentz_factor = 1/sqrt(b);
dv /= lorentz_factor;
}
// apply acceleration to object's velocity
v += dv;
// Update:
// Allow acceleration in the forward direction to change the direction
// of v by using the direction of new_v (without the Lorentz factor)
// with the magnitude of v (that applies the Lorentz factor).
if (v.length() > 0)
{
v = new_v.normalized() * v.length();
}
}
Well, lets consider the realistic problem first and see why this doesn't work and how we have to differ from it. In space as long as your engines are firing, you will be accelerating. Your speed is only limited by your fuel (and in fact you can accelerate faster once you've spent some fuel because your moving less mass).
To give this model an effective maximum speed, you can consider particles in space slowing you down and causing friction. The faster you go, the more particles you're hitting and the faster you're hitting them, so eventually at some fast enough speed, you will be hitting enough particles the amount of decelerating they do exactly cancels out the amount of accelerating your engine is doing.
This realistic model does NOT sound like what you want. The reason being: You have to introduce friction. This means if you cut your engines, you will automatically start to slow down. You can probably count this as one of the unintended forces you do not want.
This leaves us with reducing the effective force of your engine to 0 upon reaching a certain speed. Now keep in mind if your going max speed in the north direction, you still want force to be able to push you in the east direction, so your engines shouldn't be cut out by raw velocity alone, but instead based on the velocity your going in the direction your engines are pointing.
So, for the math:
You want to do a cross dot product between your engine pointing vector and your velocity vector to get the effective velocity in the direction your engines are pointing. Once you have this velocity, say, 125 mph (with a max speed of 150) you can then scale back the force of your engines is exerting to (150-125)/150*(Force of Engines).
This will drastically change the velocity graph of how long it will take you to accelerate to full speed. As you approach the full speed your engines become less and less powerful. Test this out and see if it is what you want. Another approach is to just say Force of Engines = 0 if the dot product is >=150, otherwise it is full force. This will allow you to accelerate linearly to your max speed, but no further.
Now that I think about it, this model isn't perfect, because you could accelerate to 150 mph in the north direction, and then turn east and accelerate to 150 mph going in that direction for a total of 212 mph in the north east direction, so not a perfect solution.
I really do like Wongsungi's answer (with the Lorentz factor), but I wanted to note that the code can be simplified to have fewer floating-point operations.
Instead of calculating the Lorentz factor (which itself is a reciprocal) and then dividing by it, like this:
double lorentz_factor = 1/sqrt(b);
dv /= lorentz_factor;
simply multiply by the reciprocal of the Lorentz factor, like this:
double reciprocal_lorentz_factor = sqrt(b);
dv *= reciprocal_lorentz_factor;
This eliminates one floating-point operation from the code, and also eliminates the need to clamp b to DBL_MIN (it can now be clamped to 0 because we're not dividing anymore). Why divide by the reciprocal of x when you can just multiply by x?
Additionally, if you can guarantee that the magnitude of v will never exceed c, then you can eliminate the testing of b being less than zero.
Finally, you can eliminate two additional sqrt() operations by using length_squared() instead of length() in the outer if statement:
if (new_v.length_squared() > v.length_squared())
{
const float c = 4;
float b = 1 - v.length_squared()/(c*c);
if (b < 0) b = 0;
double reciprocal_lorentz_factor = sqrt(b);
dv *= reciprocal_lorentz_factor;
}
This may only make a 0.1% difference in speed, but I think the code is simpler this way.
You need to have three variables for your ship, which you update at each physics time step based on the forces that are acting on it. These will be mass, position, and velocity. (note that position and velocity are single numbers but vectors). At each physics time step you update the position based on the velocity, and the velocity based on the acceleration. you calculate the acceleration based on the forces acting on the ship (gravity, friction, engines)
Newton's equation for force is F = M*A We can rearrange that to A = F/M to get Acceleration. Basically you need to figure out how much the ship should accelerate, and in which direction (vector), then add that acceleration to the ship's velocity, and add the ship's velocity to its position.
Here is the code you should execute each physics time step (I hope you can fill in the blanks) please ask if this is not enough detail
gravity = //calculate force of gravity acting on ship from Newton's law of universal gravitation
friction = //ten percent of the ship's velocity vector, in the opposite direction
engines = 0
if (engines_are_firing)
engines = 500
forces = gravity + friction + engines
acceleration = forces / ship.mass
ship.velocity += acceleration
ship.position += velocity
redraw()
Your question is difficult for me to understand but it seems like you're not using real physics for this game. Have you considered using real physics equations such as velocity, acceleration, force, etc?
Edit:
After your edits, I think I have a better understanding. You are simply keeping track of the current velocity (or something similar) but you don't keep track of the force where that velocity comes from. The ship should not be storing any of that information (other than engine thrust) -- it should come from the environment the ship is in.
For instance, the environment has a gravity vector (directional force) so you would need to take that into account when calculating the directional force provided by the engine.
Your ship should be storing its own engine force, acceleration, and velocity.