Advertisement

How to implement WoW-like physics

Started by November 03, 2021 11:30 PM
6 comments, last by Gnollrunner 3 years, 1 month ago

I am making an open world game, and I would like to implement movement similar to the one in e.g. WoW. For example like here: https://www.youtube.com/watch?v=-ZkCLQtFTZc

​So I want my character to collide with the terrain and with the objectsI want my character to jump and fall. I want my character to slide down steep slopes but be able to walk up gentle slopes. I don't want realistic physics though. I don't want my character to push things or be pushed by other things. I don't want surfaces to feel too slippery or my character bouncing around.

How would one implement such system? Should I use a physics engine or use something simpler? I feel like physics engines work well for realistic physics, not necessarily for such game-like physics.

I believe most of these older games simply “sweep” a sphere, or a kind of virtual stack of spheres forming a pill, against the mesh. Sometimes they do a trick to stretch a single sphere into an ellipsoid, but I would stick with the pill. Also they may use a septate physics mesh, in fact it's likely.

If you're implementing it yourself, you want to check the sphere(s) vs each triangle. You fist check the face, then the 3 edges and finally the 3 vertexes. If it misses all 3 then you didn't collide with that triangle. As an optimization edges and vertexes that are shared between triangles you only need to check once. You also need “collision response”. For instance if you hit a wall at an angle you want to slide along the wall rather than just stopping. In fact walking on the ground is really just sliding your bottom sphere along the ground.

There are also double collisions where you collide with more than one face at a time. I take the cross-product of both collisions and generate a new vector for the response. As an example, say your character is walking up a little V shaped valley.

There are a lot of little things to worry about. For instance its really possible to collide with many tris at once given certain geometry. I wrote a routine that picks the best pair to do the above double collision calculation on. It does dot products with all possible response vectors (generated by cross-products or single collision responses) , and a vector in the direction you are tying to go, and then picks the closest solution. This is a rare situation however.

Gravity is just an additional downward acceleration you apply. To keep things reasonable you can have a max speed for that. Gravity is used whenever you aren't touching the ground, or when you are touching it at an oblique angle such as on a steep hill. In that case you may want to slide down the hill. This kind of naturally gives you the behavior that you can't walk up a very steep hill. Also when you jump, you just apply a bit of upwards acceleration and then let your gravity system take over. Keep in mind that the animation itself has nothing to do with the physics, at least until you get into Inverse Kinematics. I don't think WoW does that, or at least it didn't used to.

Finally you need to organize your physics mesh into some sort of data structure that lets you find tris with possible collisions quickly. I use an octree.

If all this seems complex, it is a bit. It took me quite a while to get my stuff working. But I will say the math isn't too hard. It's mostly dot products and cross products.

You also have the option of just using an off the shelf physics engine like bullet. I'll let someone else advise you on that since I haven't used one. That's probably a much easier rout. One minor advantage with sweeping spheres is that you can't go through your geometry no matter how fast your character is moving. It will always find the perfect collision (assuming no bugs). I've been told that with a lot of physics engines, that can be an issue, but for character running speeds it may not matter.

Advertisement

If your engine of choice includes a simpler non-physics collision-system then that will likely prove much more congenial to such mechanics than would likely a full physics system, I would expect.

(Specifically, I'm thinking of a system that implements detection of collisions and likely the separation of colliding objects, but that doesn't involve forces, torque, or the like, or that makes them optional.)

MWAHAHAHAHAHAHA!!!

My Twitter Account: @EbornIan

The two general approaches are “lollipop” or “capsule.”

A “lollipop” uses a ray cast for the legs, and a sphere that encompasses the head/torso/arms. You move it kinematically (just update the positions) and push the sphere out of any colliders, and then determine leg length by the ray. Typically, you also attempt to keep the legs at a certain target length, but allow “ground contact” up to some longer (extended) length, just start applying a bit of gravity to shorten them again. If the ray makes no contact, you switch into “falling.”

A “capsule” is similar to a swept sphere – a cylinder, where the bottom/top are each a half-sphere. This is popular because it's not very much more expensive to calculate than a sphere plus a ray. The capsule will detect ground contact, and when you don't have ground contact, it should fall down a little bit, and if you haven't had ground contact for some amount of time, you switch into “falling.” Crouching is a bit harder – you may need to “shorten” the capsule and play an animation to match, you don't get such nice leg extension automatically as with the lollipop, although if you still do ray casts for foot IK, you can make it match up visually.

Finally, the actual integrator is typically one that just applies a velocity based on control input, and a down force based on whether you're falling or not. You need nothing fancy. If you want easing in/out of movement, you can drive it with a force and a velocity, and apply an air resistance based on square of velocity that you tune to counteract the movement force at whatever your desired top speed is. (And, perhaps, actually clamp the speed to whatever top you want.)

enum Bool { True, False, FileNotFound };

hplus0603 said:

The two general approaches are “lollipop” or “capsule.”

A “lollipop” uses a ray cast for the legs, and a sphere that encompasses the head/torso/arms. You move it kinematically (just update the positions) and push the sphere out of any colliders, and then determine leg length by the ray. Typically, you also attempt to keep the legs at a certain target length, but allow “ground contact” up to some longer (extended) length, just start applying a bit of gravity to shorten them again. If the ray makes no contact, you switch into “falling.”

A “capsule” is similar to a swept sphere – a cylinder, where the bottom/top are each a half-sphere. This is popular because it's not very much more expensive to calculate than a sphere plus a ray. The capsule will detect ground contact, and when you don't have ground contact, it should fall down a little bit, and if you haven't had ground contact for some amount of time, you switch into “falling.” Crouching is a bit harder – you may need to “shorten” the capsule and play an animation to match, you don't get such nice leg extension automatically as with the lollipop, although if you still do ray casts for foot IK, you can make it match up visually.

Finally, the actual integrator is typically one that just applies a velocity based on control input, and a down force based on whether you're falling or not. You need nothing fancy. If you want easing in/out of movement, you can drive it with a force and a velocity, and apply an air resistance based on square of velocity that you tune to counteract the movement force at whatever your desired top speed is. (And, perhaps, actually clamp the speed to whatever top you want.)

The lollipop approach sounds good. I put NewtonDynamics physics engine into my engine. Created a heightfield collider for my terrain, cylinder colliders for my tree objects and sphere collider for my character with raycast “legs”. I detect grounded and not grounded states and apply velocities as needed.

For the slopes, I read the terrain normal from the raycast and use the Y value as steepness. If character is “grounded” and steepness is above a specified threshold, I apply gravity velocity to pull the character down, resulting in a slide down the slope effect.

It works quite well actually. But it comes with a caveat. The slope detection happens after character already moved along the steep slope, so trying to push through a steep slope results in a jittery movement as the object constantly moves and gets turned back. Perhaps I can anticipate the movement before it happens, do a raycast then and check for the slope, and if it's invalid, cancel the movement. This would be problematic with sliding down the slope though.

Somewhat jerky motion when trying to move up hills that grow too steep is not at all uncommon in games. It's often a perfectly acceptable artifact.

To get smoother behavior, you could apply gravity based on steepness – basically, project the surface normal to the horizontal plane, multiply by some value, and if the length of that vector is greater than some threshold, apply that as a force.

It's been years since I did a character controller manually, but something like:

test = spheretest(character.sphere, world);
if (test) {
	// avoid the world
    character.position += test.normal * test.depth;
}
// first order integrator

force = gravity * character.mass;
ground = raycast(character.position, world.down);
leglength = ground.length;
if (leglength < MAX_LEG_LENGTH) {
    // have ground contact, push up, and move if controlled
    force += world.up * (DESIRED_LEG_LENGTH - leglength) * KNEE_FORCE_SCALE;
    if (input.run_forward_axis != 0 || input.strafe_right_axis != 0) {
    	force += character.forward * input.run_forward_axis * FORWARD_FORCE_SCALE;
    	force += character.right * input.strafe_right_axis * STRAFE_FORCE_SCALE;
    } else if (length(velocity) > SMALL_NUMBER) {
    	//	slow down with linear velocity ramp
    	force = normalize(character.velocity) * -BRAKING_FORCE;
    } else {
    	//	stop in place in this tick
    	force = character.velocity * character.mass * deltatime * -1;
    }
    //	when turning, don't keep strafing forever, but "right yourself" towards forward
    force += character.right.dot(character.velocity) * character.right * -SIDEWAYS_STABILITY_FORCE;
    //	slope push-off -- I'm assuming Y up
    sloping = 1.0 - ground.normal.y;
    horizontalslope = ground.normal;
    horizontalslope.y = 0;
    if (sloping > 0.1) {
    	//	make it harder to go uphill, with enough push force, make it impossible
    	force += horizontalslope * SLOPE_PUSH_FORCE;
    }
}

speed = length(character.velocity);
force += normalize(character.velocity) * speed * speed * -1; // air drag
velocity += normalize(character.velocity) * force / character.mass * deltatime;
character.velocity = character.velocity * powf(0.5, deltatime); // linear damping
if (test) {
	//	don't move into world
    character.velocity -= max(-(character.velocity.dot(test.normal)), (0,0,0));
}
position += character.velocity * deltatime;

Treating the world as only a single collision is often good enough, but in a real physics simulator, you'll want to use the built-in integrator instead, and provide the forces above to that system. (Newton can do all that, you can run it by applying forces.)

The important bit is to tune the various control parameters until running works well and feels good. Also, the specific behaviors:

  • control movement only when leg contact
  • linear and air drag damping
  • attempt to control leg height and “forward movement”
  • slopes push you back harder the steeper they are
enum Bool { True, False, FileNotFound };
Advertisement

helikopterodaktyl said:

It works quite well actually. But it comes with a caveat. The slope detection happens after character already moved along the steep slope, so trying to push through a steep slope results in a jittery movement as the object constantly moves and gets turned back. Perhaps I can anticipate the movement before it happens, do a raycast then and check for the slope, and if it's invalid, cancel the movement. This would be problematic with sliding down the slope though.

This may be what I was talking about at the end of my first post. One advantage of sweeping spheres is you don't get this problem. You can never move in or through the geometry so there is never a need to be "turned back". Checking slopes is handled in collision response. You simply check the angle of your collision response vector and if it's too great you skip over the response. done!. The angle you're checking is not anything specifically to do with the terrain, it's based on the contact point of your sphere(s), so it works for any kind of crazy terrain you might throw at it. WoW movement is quite smooth and I would bet this is how they do it. It works for anything, overhanging branches of trees, Jagged pits in the ground, whatever, and you can even do flight.

This topic is closed to new replies.

Advertisement