Advertisement

Newton's laws help

Started by December 12, 2015 09:13 AM
12 comments, last by Scottehy 9 years, 2 months ago

Hey guys, just trying to work out exactly how to apply Newton's laws of motion etc, to a Spaceship. I've made games where I've faked it all before, however this time I want to use something a bit more realistic. But to also include another object hitting said spaceship and changing it's direction, plus also having a stars gravity pull it as well etc.

So what I presume this should look like is something like this for basic movement;


public void MoveInDirection(float deltaTime)
{
    float power = ?;
    Vector2 force = direction * power;
    float mass = 1000.0f; // 1000kg in this case
    Vector2 acceleration = force / mass;
    Vector2 velocity = acceleration * deltaTime;
    position += velocity;
}

Now the part I'm a little confused with, is where I should be adding my delta/elapsed time. Should I only be adding it to the velocity, not at all or somewhere else to simulate the movement? And as for power, what sort of input would I need to give it to move a 1,000kg object etc.

As the power input, I presume it needs something else behind it, as in, to generate it from 0 up to a max power. So we don't just instantly go max speed. So some form of coefficient with a limiter etc?

And lastly if I have say a projectile hit the ship. I presume I should be doing something like adding that projectiles force by doing this to the previous function in the post:


Vector2 force = direction * power;
force += projectileForce * projectilePower;

To the previous function in the post. So that way we incorporate another objects force to our own. But then slowly dampen it till it's effect wears off etc.

Any lastly how would I add in gravity to this function, so I can have this object being slowly pulled to the gravity's source.

I should know all this stuff, but after not programming or doing math for a couple of years, I can't believe how rusty I feel in some areas sad.png

Cheers,

Scott smile.png

acceleration = force_acting_at_this_time_frame / object_mass;

vel = vel + acceleration*time_between_frames;
pos = pos + vel*time_between_frames;
should work
Advertisement

acceleration = force_acting_at_this_time_frame / object_mass;

vel = vel + acceleration*time_between_frames;
pos = pos + vel*time_between_frames;
should work

So if I try something like that, with a constant force of say 10,000 on an object with a mass of 100. I end up moving like a couple of pixels per second~


float power = 10000.0f;
Vector2 force = direction * power;
mass = 100.0f;
Vector2 acceleration = force / mass;
Vector2 velocity = acceleration * DT;
position += velocity * DT;

How come we're applying the time between frames twice per update? Sorry, I'm a little confused, either that or I'm implementing what you posted wrong smile.png

Whoops, I was resetting velocity every frame. It works now though. Should power be a constant though, I'm just curious what sort of values I should be using.


float power = 10000.0f;
Vector2 force = direction * power;
mass = 100.0f;
Vector2 acceleration = force / mass;
Vector2 velocity = acceleration * DT;
position += velocity * DT;

How come we're applying the time between frames twice per update? Sorry, I'm a little confused, either that or I'm implementing what you posted wrong smile.png

You're not applying it twice per frame, you're simply applying it to two different quantities. The best way to think of it is that all quantities evolve as a object moves around while accelerating. If an object has velocity, then it's position changes over a time delta_t, hence

position(t + delta_t) = position(t) + velocity(t)*delta_t

BUT at the same time, there is an acceleration acting which is causing the velocity to increase. So, the velocity will change over the timestep to be :

velocity(t + delta_t) = velocity(t) + acceleration(t)*delta_t

So, both of these quantities change at the same time, but we use the values at the beginning of the step to calculate the motion. This is a super-simple way of numerically simulating dynamics (called Euler integration). There are more sophisticated ways of doing it (e.g. Verlet integration), but for getting started it works just fine.

About power being a constant, this all depends on what forces are acting. I have to admit I would not use the term 'power' here since that means something else in dynamics so I will stick with forces.

- If there is no force acting, then (obviously) force = 0 and your object (spaceship for example) will drift with its current velocity

- If you press a button to fire up the boosters, then force = rocket_boost (some number??) and the acceleration will act.

- If you want to simulate gravity, then |force| = const/r^2 where const = some constant that you tune and r is the distance to the planet.

You can add whatever physics you wish to here, and if you have multiple forces acting at the same time (e.g. trying to escape the gravitational force of a planet by firing the rocket boosters), then you can simply VECTOR-add the forces together and it will still work! Note that this means you should calculate all forces/accelerations at the beginning of each step because they will in general always change!

Hope that helps!

How come we're applying the time between frames twice per update? Sorry, I'm a little confused, either that or I'm implementing what you posted wrong smile.png

you don't need to use time between frames but if you don't then your physics will slow down and speed up as the game speed slows down/speeds up. If you suddenly find the fps lags then your game's physics will also lag (go in slow motion). If your frame rate increases then your physics will suddenly speed up; things will move a lot faster than they should be able to. This is the reason you use time between frames. If only 1 second has passed (that would be really low fps but just an example) then your physics should update by 1 second. If only half a second has passed then your physic's simulation should only update by half a second.

You can do everything 'per frame' which works ok if you have consistent frame rate (steady 60 for example). If you have variable framerate e.g. with some heavy areas where fps drops, then you should account for the time that has elapsed since the last update, that's a 'variable timestep'. The variable time step is simple enough but when it is more of extreme values it can end up making your physics go crazy. For that reason people tend to switch to a 'fixed timestep' which you can find information on with a quick search.

I saw the following recently that I thought explained it well (for me at least):

http://gafferongames.com/game-physics/fix-your-timestep/

You sorta get best of both worlds with this approach. It gives steady physics but also accounts for fluctuations in frame rate.

Interested in Fractals? Check out my App, Fractal Scout, free on the Google Play store.

sergamer1, what you've said makes much more sense related to deltatime. I understand deltatime fine, I was just unsure the reasoning in applying it twice to the movement calculation, but I can see why now.

- If you press a button to fire up the boosters, then force = rocket_boost (some number??) and the acceleration will act.

In this case, would rocket_boost be a constant number though? Or should It be something I increment from 0 to a max value. For argument sake of my understanding, lets say it's a constant value of 2,000.


public void Accelerate(float DT)
{
    float rocket_boost = 2000.0f;
    Vector2 force = direction * rocket_boost;
    mass = 10.0f;
    Vector2 acceleration = force / mass;
    velocity += acceleration * DT;
    position += velocity * DT;
}

That works fine and starts to accelerate nicely. However it does take a few seconds to really kick in. But once it does, I feel locked to the direction I'm traveling for far to long. So how can I change my direction faster in this instance? Not exactly a sharp turn, but being able to fully change direction in a second or two, so it has a smooth turn?

Thanks so much for the help though guys, it's greatly appreciated :)

Advertisement

Also make sure to stabilize large translations. Otherwise your sympletic integrator (position update scheme) will explode. This can be accomplished with the following pseudo-code (h = DT) just after you have updated the velocity.


			
		Vec2 translation = h * velocity;
		float sqrTranslation = SquaredLength(translation);
		
		if (sqrTranslation > MAX_TRANSLATION * MAX_TRANSLATION) {
			float ratio = MAX_TRANSLATION / SquaredRoot(sqrTranslation);
			velocity *= ratio;
		}
                position += h * velocity;

That works fine and starts to accelerate nicely. However it does take a few seconds to really kick in. But once it does, I feel locked to the direction I'm traveling for far to long. So how can I change my direction faster in this instance? Not exactly a sharp turn, but being able to fully change direction in a second or two, so it has a smooth turn?

Thanks so much for the help though guys, it's greatly appreciated smile.png

Are you finding your ship is 'drifitng' more than you would like when turning? If that's the case you need to sort of retro thrust to slow you down. With planes you have air resistance doing the job and with cars it is the friction between the road and the wheels but in space you have nothing so you need to provide that counter force to stop you drifting.

In your code you have a 'direction' variable which I am assuming is the direction of the ship and presumably else where when you wish to rotate/turn you rotate that direction. What you need to do is have another variable 'desired velocity', which is just direction*maxspeed (or you could just make it the same magnitude as your current velocity). You then need to work out a vector from your current velocity to your desired velocity:


desired = direction*magnitudeOf(velocity)
retro = normalize(desired-velocity) * retroForce

That may take some tweeking as my vector math is a bit rusty but it should provide a counter force that reduces velocity in the direction you are drifting. There are probably some issues with it though. You could limit the direction retro thrust can be applied too.

Interested in Fractals? Check out my App, Fractal Scout, free on the Google Play store.

That works fine and starts to accelerate nicely. However it does take a few seconds to really kick in. But once it does, I feel locked to the direction I'm traveling for far to long. So how can I change my direction faster in this instance? Not exactly a sharp turn, but being able to fully change direction in a second or two, so it has a smooth turn?

Thanks so much for the help though guys, it's greatly appreciated smile.png

Are you finding your ship is 'drifitng' more than you would like when turning? If that's the case you need to sort of retro thrust to slow you down. With planes you have air resistance doing the job and with cars it is the friction between the road and the wheels but in space you have nothing so you need to provide that counter force to stop you drifting.

In your code you have a 'direction' variable which I am assuming is the direction of the ship and presumably else where when you wish to rotate/turn you rotate that direction. What you need to do is have another variable 'desired velocity', which is just direction*maxspeed (or you could just make it the same magnitude as your current velocity). You then need to work out a vector from your current velocity to your desired velocity:


desired = direction*magnitudeOf(velocity)
retro = normalize(desired-velocity) * retroForce

That may take some tweeking as my vector math is a bit rusty but it should provide a counter force that reduces velocity in the direction you are drifting. There are probably some issues with it though. You could limit the direction retro thrust can be applied too.

I'm a little confused with what you wrote. My ship is drifting to much in X direction, so I do need something to slow it down. I've tried a couple of forms of the math you posted but keep ending up with NaNs (not a number) at random points. Mostly when I try to normalize the resto vector. I'm confusing myself now biggrin.png

I been trying something like this with no luck:


float rocket_boost = 0.0f;
if (Global.Instance.IsDown(Keys.W))
    rocket_boost = 2000.0f;
Vector2 force = direction * rocket_boost;
mass = 10.0f;
Vector2 acceleration = force / mass;
velocity += acceleration * DT;

Vector2 desired = direction * velocity.Length();
float retroForce = 10.0f;
Vector2 retro = desired - velocity;
retro.Normalize();
retro *= retroForce;
velocity += retro;

position += velocity * DT;

EDIT:

It seems to function decent with something like this, but is this what you had intended?


float rocket_boost = 0.0f;
if (Global.Instance.IsDown(Keys.W))
    rocket_boost = 2000.0f;
Vector2 force = direction * rocket_boost;
mass = 10.0f;
Vector2 acceleration = force / mass;
velocity += acceleration * DT;

Vector2 desired = direction * velocity.Length();
Vector2 retro = desired - velocity;
velocity += retro;

position += velocity * DT;

I'm a little confused with what you wrote. My ship is drifting to much in X direction, so I do need something to slow it down. I've tried a couple of forms of the math you posted but keep ending up with NaNs (not a number) at random points. Mostly when I try to normalize the resto vector. I'm confusing myself now biggrin.png


retro.Normalize();
retro *= retroForce;
velocity += retro;

The issue with this is that when you are travelling in the direction you need to be (i.e. not drifitng) then desired==velocity. At that point retro is 0. When you try to normalize you then end up dividing by 0, hence the issue you are experiencing. The second code snippet you posted does avoid this issue but the amount of retro thrust then can vary significantly. It will be strong when your 2 vectors are very different and non existent when they are the same; this may work out rather well so if you like it keep it. The method I first suggested allowed you to have the strength of that retro thrust as a variable/property of the rocket which lets you have more responsive rockets or more drifty rockets and so on which might be useful.

To fix the first version you posted you just need to do:


retroLength = retro.SquaredLength();
if(?retroLength > 0)
{
   retro /= ?sqrt(retroLength); // this normalizes and saves you having to do the costly Length/Normalize function twice
   retro *= retroForce;
   velocity += retro;
}

Interested in Fractals? Check out my App, Fractal Scout, free on the Google Play store.

This topic is closed to new replies.

Advertisement