• Advertisement

Archived

This topic is now archived and is closed to further replies.

Gravity

This topic is 5069 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Advertisement
Hey Mr_Ridd,

I dunno where you can find any tutorials, but these might be somehow useful (if you can figure out your own implementation)... I decided to write my own brief tut. Everyone be clear that I'm not an expert physicist or programmer, so pointing out my errors is probably a good thing... onwards!

Another note:
This is nowhere near optimised, there are bugs that I can pick out and other such things. The reason that it is not optimised is so that it is easier to see what's going on. And hey, this all deals with a point as opposed to a player capsule.

First off, horizontal movement has no effect on gravity (assuming that it is downward - like on earth), so you just need to worry about your y values. Then you can use the following:

v=u+at
v^2=u^2+2as //this one's pretty irrelevent for games methinks
s=ut+1/2 * a t^2
or more simply:
s=vt

Where,
v = final velocity
u = initial velocity
a = acceleration (due to gravity, put in -9.8m/s/s for earth)
t = time (in seconds - this'll probably be your frametime or whatnot)
s = displacement (from a relative point 0 at the start of each physics step)

This is what I'd use to deal with the falling of the characters / objects etc. once the player has jumped or whatnot. In a game implementation, these would look like [to me at least]

(called once every frame, probably in with all of your other physics routines)


void CPawn::HandlePhysics()
{
this->velocity[Y_AXIS]=this->velocity[Y_AXIS]-system->gravity*system->frametime;
this->location[Y_AXIS]+=this->velocity[Y_AXIS]*system->frametime;
return;
}


I think that the objects are pretty obvious what they are (the wrapper is an actor class).


To jump, I would either apply an impulse to the pawn [a change in momentum - which boils down to a change in velocity]. Momentum is setup as p=mv, where momentum = mass * velocity. Basically what I'd do for jumping is say "The jump action will provide x amount of momentum". In code, the corresponding change in velocity would be:

(this would be called on jump - but make sure that the player pawn is on the ground!)


void CPawn::Jump()
{
this->velocity[Y_AXIS]=system->jumpmomentum/this->mass;
return;
}



The next loop, the physics routines would begin to handle the falling automatically. The pawn would start out moving upwards fast [like they just jumped], and then slow down, eventually beginning to move downwards.


We have a working gravity system. You can iterate through all of your world objects in the same way, and all of them will behave pretty well. Except, there's one big problem! Everything will start falling, and never stop. They will begin to fall through the ground as soon as the physics system starts up. To combat this, we'll introduce the test (touching_ground) as a member function of the pawn class. It returns true when the player is on the ground, or false when they are not - this is supposed to be quite fast, so you could inline it, and optimise the function itself.

Anyways, how you determine where the ground is is up to you - my method is to use a line-of-sight function (which could also be used for weapon trajectories...) and project it along the negative y axis from the player [psuedocode: system->push_ray(CPawn->location,vec3_identity_minusY)] assume for simplicity that this function returns the distance to the nearest surface through the property ->distance.

The one problem with this is that it determines if there is ground beneath the CENTRE of the object. My code would look like this [somewhat] - the pawn is the type of player:


double CPawn::touching_ground()
{
//project downwards from the pawn's location

if (system->push_ray(this->location,vec3_identity_minusY)->distance<=(this->size[Y_AXIS]/2))
{
//we collide with the ground

//return the amount that we collide by.

return system->push_ray(this->location,vec3_identity_minusY)->distance-(this->size[Y_AXIS]/2);
}else
{
//we don't

return -1;
}
}


Now, we place this into our handle physics function. Let's just assume that gravity only has an effect when a pawn is in the air. Sure, there is an effect when the pawn is on the ground, but that serves to keep the pawn from launching up from it.


void CPawn::HandlePhysics()
{
double groundoffset;
groundoffset=this->touching_ground();
if (if groundoffset>0)
{
//we're touching the ground, reposition our unit so that we're not falling through the ground.

this->location[Y_AXIS]+=groundoffset;
//assume no acceleration on us when we're on the ground [though not physically correct]

this->velocity[Y_AXIS]=0;
}else
{
this->velocity[Y_AXIS]=this->velocity[Y_AXIS]-system->gravity*system->frametime;
this->location[Y_AXIS]+=this->velocity[Y_AXIS]*system->frametime;
}
return;
}


let's also rewrite our jump function with this new touching_ground function...


void CPawn::Jump()
{
//are we touching the ground?

if (this->touching_ground()>0)
{
//touching ground

//jump!

this->velocity[Y_AXIS]=system->jumpmomentum/this->mass;
}else
{
//not touching ground

}
return;
}


The next thing to include with the gravity code is sliding. Now, when you walk on a surface which is really angular, then you get the problem of sliding off it. Which is why we can't all climb cliff faces etc.

(in 2D) If the angle between the horizontal and the surface is x, then the acceleration along the surface will be :
mg sin x

where m is the pawn's mass
g is gravity [-9.8m/s/s on earth]

We don't slide along flat ground, because x=0, so sin x = 0, thus no acceleration. It's getting late, so I can't figure out exactly how to explain it, but in 3D [+ still 2D], the angle between the normal of the ground surface and the negative direction of gravity (eg straight upwards) is equal to the value of x. In 2D, it is simply 2 sets of perpendicular angles forming similar angles... The extention into 3D is similar.

Let's assume that our handy little push_ray function returns the member ->normal which returns the vector3 of a unit long normal to the surface that it returns, and the member ->slidedown which is the direction vector which travels the furthest downwards (I'm sure that you can generate that mathematically though I don't know how right now). We're also going to have all of the maths functions I want available.

From what I read on google, the angle between vectors function basically uses the identity:

dotproduct(vec3 a, vec3 b)=cos x

when a and b are unit vectors.

To get ourselves sin x, we say...

sin x = sin(cos_inv(dotproduct(a,b)))

so acceleration down the slope = mg sin(cos_inv(dotproduct(a,b))), or in our code, assuming that we have all of the operators for vectors...


void CPawn::HandlePhysics()
{
double groundoffset;
groundoffset=this->touching_ground();
if (if groundoffset>0)
{
//we're touching the ground, reposition our unit so that we're not falling through the ground.

this->location[Y_AXIS]+=groundoffset;
//assume no acceleration on us when we're on the ground [though not physically correct]

this->velocity[Y_AXIS]=0;

//is the surface flat?

if (cos_inv(dotproduct(vec3_identity_plusY,system->push_ray(this->location,vec3_identity_minusY)->normal))<system->slidesurfaceangle)
{
//the surface is flat [enough].

}else
{
//the surface is all angular. Handle sliding

this->location+=this->mass*system->gravity*sin(cos_inv(dotproduct(vec3_identity_plusY,system->push_ray(this->location,vec3_identity_minusY)->normal)))*system->push_ray(this->location,vec3_identity_minusY)->slidedown);
}
}else
{
this->velocity[Y_AXIS]=this->velocity[Y_AXIS]-system->gravity*system->frametime;
this->location[Y_AXIS]+=this->velocity[Y_AXIS]*system->frametime;
}
return;
}


OK. I think that that's almost completeish. One problem which I've noticed is that if the player is falling very fast, they can move right through the ground surface. We can't have buggy code [well, we DO have buggy code - but anyways] so we'll try to trap these errors. The logical solution to me, test to see if we moved through anything during the last physics step.

We'll be wanting to store our old position info to do this.


void CPawn::HandlePhysics()
{
//backup our old position info

this->oldlocation=this->location;

double groundoffset;
groundoffset=this->touching_ground();
if (if groundoffset>0)
{
//we're touching the ground, reposition our unit so that we're not falling through the ground.

this->location[Y_AXIS]+=groundoffset;
//assume no acceleration on us when we're on the ground [though not physically correct]

this->velocity[Y_AXIS]=0;

//is the surface flat?

if (cos_inv(dotproduct(vec3_identity_plusY,system->push_ray(this->location,vec3_identity_minusY)->normal))<system->slidesurfaceangle)
{
//the surface is flat [enough].

}else
{
//the surface is all angular. Handle sliding

this->location+=this->mass*system->gravity*sin(cos_inv(dotproduct(vec3_identity_plusY,system->push_ray(this->location,vec3_identity_minusY)->normal)))*system->push_ray(this->location,vec3_identity_minusY)->slidedown);
}
}else
{
this->velocity[Y_AXIS]=this->velocity[Y_AXIS]-system->gravity*system->frametime;
this->location[Y_AXIS]+=this->velocity[Y_AXIS]*system->frametime;
}

//cast a ray between the old position and this position to check to see if we went through anything. If we did, we simply rollback to the point that we collided at.

if (system->push_ray(this->oldlocation,this->location-this->oldlocation)>vec3_length(this->location-this->oldlocation)
{
//we went through nothing :D

}else
{
//we went through something :(

//rollback

this->location=this->oldlocation+vec3_unitvector(this->location-this->oldlocation)*(vec3_length(this->location-this->oldlocation)/this->location-this->oldlocation);
}
return;
}







OK. I'm not sure if any of that made sense, or even answered your question, but I figure that that's a nice place to finish for now. Friction, Sliding against objects etc. are nice things to add, but I don't have any more time now. Hopefully this will help someone at least in some way.

Anyways

//end rant [oh and what a rant!]

CJM


[edited by - CJM on June 9, 2004 9:15:43 AM]

Share this post


Link to post
Share on other sites

  • Advertisement