player characters and ground slope detection

Arcanor

13-04-2007 02:52:43

I'm trying to figure out the cleanest way to deal with having a player character move at constant speed regardless of the slope of the ground they are on. Right now I am using the following code in my custom force callback:
if (obj->isWalking())
body->setVelocity(Ogre::Vector3(orientation * Ogre::Vector3::NEGATIVE_UNIT_Z * obj->getWalkSpeed()) + Ogre::Vector3(0, velocity.y, 0));

This works perfectly well as long as the character is on level ground, but since it keeps a constant Z velocity, if the character climbs a slope it moves much faster (since it's also climbing the Y axis simultaneously). Taken to the extreme, if the character walks on an 85 degree inclined slope, the character goes shooting off high into the sky because of all this velocity!

What I think I need to do is to change the velocity vector from being a global Z vector. Instead, I should use the same velocity amount, but with a direction parallel to the ground. My problem is I haven't figured out how to do this yet.

Specifically, I am looking for:

1. An efficient way of detecting if I'm in contact with a static object in the negative Y axis (i.e. the ground, or something else I might be standing on and could push off of). I've got a function isOnGround() which uses a BasicRaycast to calculate distance to ground and if it's within tolerance limits it returns true. This does work, but it seems inefficient to call this function every frame for every moving object. I'm hoping for a cheaper alternative.

2. Once I have established ground contact, I need to determine the slope of that ground in the YZ plane, so I can set my velocity at that same angle. I haven't figured this out yet at all.

Can anyone offer me any pointers? Thanks in advance.

Tubez

13-04-2007 09:55:57

Do not fear about doing a single raycast per frame. Those things are relatively cheap.

Your basic procedure will be something like this:

1) Figure out the ground normal. You can get this from the raycast collision i believe.
2) Figure out your desired direction of travel, without accounting for terrain
3) Find the unit vector that lies in the plane of the two previous vectors and is perpendicular to the ground normal. This might take a bit of algebra.
4) Multiply this unit vector by your desired velocity

Basically, every time you see a vector handled as components it should raise a warning flag in your head. Always think of vectors in terms of length and direction, not of components.

Regarding the "bit of algebra", if you can't figure this out I suggest you go to your local library and check out a good book. It only gets harder from here. Here's a start though:

There's a thing called a cross-product that given two 3d vectors returns a vector perpendicular to both of them. Obviously this is only defined in 3 dimensions. If you feed it the ground normal and your character's shoulder-to-shoulder axis you should get what you want. Beware of sign issues.

Arcanor

13-04-2007 11:23:21

Thanks very much Tubez. This should help quite a bit.

One question though is when you mention about vector components. I assume you're talking about where I add Ogre::Vector3(0, velocity.y, 0). The reason for this is to add back in any vertical speed due to gravity. Should I not be doing this?

Tubez

13-04-2007 12:28:31

First of all, you should let gravity add a force, not a velocity :P

Anyway, the direction of gravity is -Y, the magnitude is g. Due to the fact that the direction is aligned with one of the axes, the multiplication becomes trivial and you are right to optimize it in that fashion. But what you are really doing is "f = grav_dir * g".

However, should you ever need to tilt gravity (e.g. in a Super Monkey Ball clone) you would see what I mean :P

Arcanor

13-04-2007 12:48:27

I am applying the gravity as a force later in my force callback, using:
netForce += Ogre::Vector3(0, -9.8f, 0) * mass;
body->addForce(netForce);

The problem is accumulated speed due to gravity. When my objects fall they should accelerate until they hit ground. But since I'm setting the velocity explicitly each frame, this is not happening unless I add back in the component of the velocity that is due to gravity. I'd like to add this back in, but I don't know how to separate the "Y-axis velocity due to gravity" from the "Y-axis velocity due to character motion while on a slope".

Is there a way to find this?

Tubez

13-04-2007 15:40:00

Each time you set the velocity directly you mess up the dynamics of the object. By far the best way is to steer the object using joints and forces. There are numerous examples of that in this forum, but it's a tricky thing.

Most schemes involve using a joint to keep the character right-side up.

Arcanor

13-04-2007 21:40:04

Thanks again for your reply Tubez. :)

I think the problem is that I don't really want the character body to act "physical" most of the time. I only want it to have gravity applied, and to check for collisions to restrict its path, never to have any impact on another entity. Outside of that, it should do exactly what the user asks it to do, without any delays to wait for acceleration, or corrections to the destination vector. I'm not putting the player in a hovercraft. It's a person with their feet solidly on the ground. They shouldn't be sliding around. ;) My character is never going to "push" anything out of the way. It's either going to be stopped by a collision (because it ran into a static landscape item), or it's going to walk through it as if it were a ghost. It's more about the game system than about the physical realism.

(please understand the following rant isn't aimed at you, it just shows my frustration about failing to find a way of doing something that should be simple...)
<rant on>
To be honest, I have already spent over a week looking at many dozens (hundreds?) of other forum posts about this (and the API, and the tutorials, and the OgreNewt source, and the Newton SDK samples, and the Stunt Playground source, and the wiki, and the Newton forum, and........), and I have yet to find a serious working example of what I'm trying to accomplish, which is pretty much "standard" character movement for an RPG.
</rant off>

I do want to mention that I've REALLY tried to use forces in my code (and avoid using setVelocity()). But I keep pulling it back out because the bottom line is that that's not how these kinds of games look to the end user. It doesn't look like a "normal" RPG game.

Now, it's certainly possible that I just haven't figured out how to do it "right" yet. Maybe there's a way to set velocity and omega for a player body nearly instantaneously (which is a requirement) without using setVelocity() and setOmega(). I'd love to use addForce() and addTorque() if I could figure out how to get the same effect, but I haven't figured out how yet, and I have a feeling it may not even be possible.

Here are some of the biggest problems with using force and torque that I've found so far (for a typical RPG type game):

1. players accelerate/decelerate slowly when it should be instantaneous, or nearly so.

2. there's no easy way to decelerate accurately, i.e. without removing other forces inappropriately.

3. forces apply acceleration, not velocity. how do you know when to stop?

4. same things as above for torque.

So I guess the bottom line is that my character object is doomed to be a hybrid of forces (gravity) and explicitly set velocity/omega, unless I can find a way to solve these problems.

I'm certainly open to suggestions. :)

Game_Ender

14-04-2007 15:35:59

Welcome to controls :). As tubez said, you really can't be mucking about with velocity settings manually and expect the simulation to still behave right. If all you want is collision detection OgreNewt is over kill. Something like OgreOpcode would do all you want.

If you still choose to use a physics engine you need to make a simple controller, some like the partial pseudo code bellow:

Ogre::Vector3 error = desired_velocity - body.getVelocity();
netForce += error * gain;
body.addForce(netForce);

This applies more force to the object the farther it is away from the desired velocity. The "gain" allows you to tweak exactly how much force to apply. It might be wise to build frame rate into that value so players with different frame rates won't move differently. This kind of proportional control might not always be stable, but its worth a try.

Arcanor

14-04-2007 16:07:17

Thanks for the tip Game_Ender. I've seen that sort of approach mentioned before; it seems to make sense.

The biggest question remains: how do I know what my target velocity really is? One component of that velocity is my desired walking speed, but it may also be affected by gravity during a jump into the air (or off a cliff). During such a state (and probably other states as well), my "desired velocity" isn't really horizontal any more; it has to include consideration of those other forces. So now I'm back to adding forces and velocities together, and that's where I'm getting hung up. I can set forces OR velocities, but I haven't figured out how to do both in the same frame update.

I guess what I really need is a way to translate my setVelocity() and setOmega() calls into whatever the appropriate addForce() and addTorque() calls should be at that moment. Or maybe I should be using addImpulse() somehow?

I don't know what the answer is yet, but I'm still searching.

Game_Ender

14-04-2007 16:50:18

You could remove any up/down component of force that above controller generates and gravity decide on the up or down force. I have also seen people making there players actually little cars (ie use wheels).

Arcanor

14-04-2007 16:54:32

You could remove any up/down component of force that above controller generates and gravity decide on the up or down force.

Yes, this is the approach I'm currently pursuing. I'm trying to figure out how to calculate the Y-axis force generated by my setVelocity() calls. If I can then subtract it out later, it may work properly. I'll see how it goes and report back.