New Bullet Character Controller


This is a Bullet port of my ODE Character Controller, with some additional improvements made possible by Bullet’s awesomeness. It is aimed to replace the Bullet’s default KCC in Panda, because that code has lots of missing features and some bugs. Hopefully this one will be better :wink:.

You have to use a new development build of Panda and even there collision detection might not work. This is a problem with Bullet and enn0x is looking into it.

Features included:

  • Walking with penetration prevention
  • Jumping with active and passive jump limiter. Active means limiting the max jump height based on the distance to the “ceiling”. Passive means falling automatically when a “ceiling” is hit.
  • Crouching with stand up limiter which prevents the character from standing up if inside a tunnel or other limited space
  • Slope limiter of arbitrary maximum slope values which may or may not affect the movement speed on slopes smaller than maximum
  • Stepping, supports walking steps up and down (prevents “floating” effect)
  • Flying support for no-clip, ladders, swimming or simply flying
  • Simplified state system. Makes double/multiple jumps impossible by default (and you can easily change that if you want to)
  • Callbacks for landing and standing up from crouch

The controller is composed of a levitating capsule (allowing stepping), a kinematic body and numerous raycasts accounting for levitation and spacial awareness.

The code is documented, so take a look into Note it is not integrated yet and doesn’t inherit from Node.

If you want to parent anything to the controller, you will have to use the movementParent node. It is now exposed. is obviously the demo and is the custom mouselook code used for TPP camera in the demo. I’m not sure if the demo code will work correctly on Windows because of the differences in mouse support, so let me know if it doesn’t. If you end up with the camera under the map, don’t worry this is not a bug, it’s just an orbit code, not real TPP – just move the mouse until correct orientation is restored.

The jump height is deliberately insane in order to better visualize the active jump limiter.

Demo controls:

  • WASD – as usual
  • space – jump
  • c – crouch
  • shift – run
  • control – toggle flight
  • r, f – fly up, down

And finally, you can get it here:

Enjoy and please give feedback

Really great work you have done here. It is really useful.

But one question I have about the demo and the “flying” support. As it looks in the demo, the r and f key should apply some flying. But if I press them, the character doesn’t do much. It just moves a few pixels up and down. Did I understand this wrong in the Demo, or how would one use the Flying support for for example ladders?

I just forgot to include a proper presentation of flying into the demo. The r and f keys do make the controller fly(yyyy like an eagle… sorry, couldn’t help myself), but there’s no binding for switching movementState to “flying”. And that’s required for r and f to work. I will include that tomorrow, and for now you can just add:

self.character.movementState = 'flying'

at the end of the setup() method, which will permanently enable flying, just so you can try it out.

That also reminds me a setMovementState method would be nice.

To make a ladder (or a force field, water etc.), you would simply use an area trigger (made of a bullet ghost object) which would set movementState to ‘flying’ on every character entering it, and ‘falling’ on every character leaving it.

A very useful snippet. Thanks.

I just wrapped my head around your code. Its really impressive but I cant figure out how to get it included in my scene. I would like to reparent a 3d Model to the charactercontroller, so that its moved together with the controller. Unfortunately the charactercontroller itself is not a nodepath. In your code you sync the position of the physics capsule with the “movement parent”. An intuitive choice would be to reparent my 3d object to the movement parent. But since its marked as private i figured there would be a better way. What did you intend as the “canonical” way to use the character controller. Im afraid the demo has solved my problem.

Thank you for your great code.


The code is work in progress so there is no “canonical” way to do anything yet ;D. The goal is to make it a node/nodepath so that you can use it just like anything else. I would like to have it integrated as deeply into Panda and Bullet as possible. This is not straight forward.

I will expose the movementParent, so that you can have something to parent to for now. Making it private right now was not the best decision.


Exposed the movement parent, added a fly switch and moved the files to github.

Thank you very much for your great code and the fast response. I really appreciate your help

sorry for the last response. This is really a great piece of code. I would not have thought that it is possible to write so much character functionality with so little lines of code. Congratulations.

About the “duplicate manifold points” problem, well, I don’t have much more insight than three weeks ago. It seems that this problem is gone when using Bullet 2.80. but I can not pin down the change in Bullet’s source code which is responsible for the effect. I think we should upgrade the buildbot machines to Bullet 2.80.

After a while playing around with the code, I found some things which don’t work as expected. The things I’ve found are the following:

  1. The setPos function doesn’t set the characters position which as far as I know is caused by the __currentPos variable. As workaround I put an extra function in my copy of the code, which will set the __currentPos variable and the movementParent position to the position which is given to the function. Maybe there is a better way to do this but that one worked for me.

  2. If the character collides with ghost nodes he is pushed back and don’t walk through the ghost node. I’ve try to change collision masks and some other things but haven’t found anything that worked. Maybe someone knows a way how i need to change the code so my character can walk through the ghost nodes.

  3. Maybe a design error by my side, but when I want to check if a collision occurs and need to know if the one of the nodes is my character then as a workaround i had to set up another function in the character controller class to check if the given node is self.__walkCapsuleNP.node().

Thanks for the report, Wolf. I’ll look into it as soon as I can.

Private variables might be a problem, I expected that. As I said, the plan was to make it a Node, but I’m not sure if that’s what the plan still is. For now, you can even remove __ from all collider variables and use them directly. It won’t hurt and I might expose them like that myself, just make everyone’s lives a bit easier.

Regarding the bug #2, it’s very much possible. I haven’t yet checked the controller’s behavior with triggers (although I probably should have) so it might not take this case into consideration. There is a special case needed for that, when the KCC will acknowledge the contact, but won’t react to it. This should be easy to fix.

Once again, thanks for the info.

Sorry I took so long, I had no time to get to this earlier.

I have committed the requested changes. Now setPos should work correctly, along with setX, setY and setZ.

The self.capsule and self.capsueNP variables are no longer private so you can use them to check if the colliding object is the KCC. Note that you should use these variables and not walkCapsule or crouchCapsule, which are still private. This is because capsule and capsuleNP are state independent.

I’ve also fixed the Ghost collision behavior.

Thank you, I’ve just updated my code and now everything works fine. Position is set correctly and ghost collision behavior is OK too. And also thanks for the hint with the collision detection over the capsuleNP variable.

Thanks for the feedback, I’m very happy the code works fine now.

The Foot ray is still colliding with the top of ghost body’s,i tried to fix it myself but for some reason ray testing is returning blanks about 80% of the time.
Just letting ya know.

Ah, right, forgot to take change that. Frankly, I’m not actually using the KCC at this time (busy with other things) so you must excuse this kind of otherwise obvious bugs :slight_smile:. Stay tuned, I’ll fix it ASAP.

It should now work correctly. Thanks for reporting :slight_smile:.

There is now another problem with the foot ray and ghost objects. If the foot ray hit a ghost object, while the character stand on the ground, the foot ray won’t collide with the floor collision mesh. So the character falls through the ground until the body capsule hits the ground collision.

Sorry for the lag, I forgot to enable watching this topic. The newest commit should fix the problem.

I just updated to the newest version and it looks good now. Thank you for fixing that problem.

nice posts…

Found a fix for a tiny unimportant bug :slight_smile:

in KCC function startCrouch on line 114 add line

if self.isCrouching == False:

Or you will get this if trying to crouch while chrouching:

:bullet(warning): rigid body not attached

:bullet(warning): rigid body already attached