Complicated ODE Shenanigans not working

@Magcius

Have you tried making a kinematic character controller? I know the ODE wiki and some(most?) of the mails on the ODE mailing lists suggest to go with dynamic, but as far as I know, none of the more feature complete physics engines goes this way, and with my own experiments with KCs I’ve found the dynamic to be only apparently easier to make, but it’s also quite unpredictable sometimes and that’s exactly what you want to avoid with KCs. PhyX, Bullet, and I guess Havok too, use Kinematic Character Controllers, because they’re so much easier to control and get right. With dynamic you have to constrain the body so much, it doesn’t really have a point.

I actually have wrote a KKC, but it’s very deep in my code, so before posting it here I need to extract it from there and make it more universal (right now the code is a typical internal one). But I will post it eventually, I promise.

Anyway, the route I’ve taken is that I’ve made a body-less capsule geom, stuck a down-facing ray to the bottom of it (remember that geoms in ODE have position at the center, not at the bottom), hovered it a little above the ground. “A little” means the height of the steps the character is meant to be able to walk up.

In every step I update the position of the character to be exactly X units above the ground. That way it can easilly walk up and down stairs, and I avoid a situation in which the character - walking very fast - “takes off” of the ground and glides a little above steps or a slide. At the same time, the capsule allows it to collide normally with everything higher than the steps limit. And obviously when the character happens to be higher above the ground than it should (that is when the foot ray doesn’t collide with anything) I put the character into falling. Otherwise it would be falling instantaneously of cliffs, ending up on ground like 10 meters lower in a fraction of a second.

For jumping (explanation - highest entry is the higest hit point on the foot ray - I sort them myself, because I don’t trust it always being the first, or the last, I prefer to make sure I get it right myself, and jumpTime is zeroed when the jump is finished):

def processJump(self, newPos, stepSize, highestEntry):
	self.jumpTime += stepSize
	
	np = self.jumpStartPos + self.jumpSpeed*self.jumpTime + (-9.81)*(self.jumpTime)**2
		
	if highestEntry and np <= highestEntry.getContactPoint(0)[2] + self.levitation:
		self.state = "ground"
		return highestEntry.getContactPoint(0)[2] + self.levitation
	
	return np

Falling:

def fall(self, newPos, stepSize, highestEntry):
	if self.state != "falling":
		self.fallStartPos = self.capsuleGeom.getPosition()[2]
		self.fallSpeed = 0.0
		self.fallTime = 0.0
		self.state = "falling"
	else:
		self.fallTime += stepSize
	newPos[2] = self.fallStartPos + self.fallSpeed*self.fallTime + (-9.81)*(self.fallTime)**2
	return newPos

I post those jumping and falling functions because I found making that look good the toughest part of making a KCC.

For movement I have a simple self.speed = [0, 0] list. The way I handle that in update is very simple and very standard:

speedVec = Vec3(self.speed[0]*stepSize, self.speed[1]*stepSize, 0)
quat = self.capsuleGeom.getQuaternion()
speedVec = quat.xform(speedVec)
newPos += speedVec

Also, I use a state variable (not Panda FSM in that case, too sophisticated for the task) with possible values of “ground”, “falling”, “jumping”. When the character jumps and get’s hit in the head (i.e. the normal of the collision geom is (0, 0, -1)) I set it to “falling” and the character doesn’t penetrate through other geoms.

For collisions with static geometry I move the character away in the direction of the normal by the length of penetration depth (I’ve found that making it smaller than the penetration depth, like depth * 0.8 gives better results and the character doesn’t shiver). And for collisions with dynamic objects, I just get the collision normal and set a force on the body. That way I can puch objects around.

I hope this helps in any way. I will try to get my ode character controller code out there, because I myself found that subject in ODE the most problematic, and there aren’t any real sollutions on the net as far as I know. But as I said, it’s just a typpical internal code - almost no comments and very integrated with the rest of the stuff. Additionally, I also use my own collision callback (i.e. I don’t use autoCollide) because with autoCollide handling the foot ray was a little problematic. Still, if you want ODE collision detection for a real game, you will find autoCollide unsuitable because there’s no way to make area triggers with it.

Oh, and for time steps, I personally don’t use the proposed time accumulator code, but just doMethodLater with return task.again instead of task.cont. That gave me a little better results, and while the simulation still slows down sometimes, I’ve never got it to freeze (even though my PC is not very strong).

Again, I hope you’ll find my suggestions helpful in any way.