Bullet Physics
Loading...
Searching...
No Matches
Character Controller

A character controller is a physics object, which is designed to be able to be moved using human or computer inputs.

Current Status

When you first start researching character controllers with bullet you'll probably initially be bombarded by many people asking how to build one, additionally if you start reading into these articles you'll start piecing together an image of the state of the character controller in bullet, needless to say it's not in the best state.

Kinematics vs Dynamic

When it comes to character controllers there are two main types:

Dynamic

A dynamic character controller is thought of as a rigid body but unable to rotate around it's origin. This allows the body to interact with the phsics engine regularly, though these interactions may not always be predictable.

This type of controller doesn't have direct control over the bodies position, as it's position changes through impulses or forces. An easy way to remember this type of controller is that it's just a regular physics entity with some custom behavior.

There are a few things to keep in mind with this type of controller:

  • It may be difficult move your character controller to a specific location as you can only interact with it through forces
  • To make it act like a "character", there will be situations where we have to hack in custom behavior to the object
    • For example if we've modelled a stair set using a wedge, and the player attempts to climb up this wedge, since our character is just a regular physics object, they would start sliding down the wedge

The general summary of this is that to to create a dynamic character controller, you have to take a regular physics object and tweak it's behavior and disable features until it behaves as desired.

Kinematic

On the other hand a kinematic controller is one that cannot directly interact with physics objects unless it is written explicitly in the code. In order to make it actually interact with the physics world we need to define custom behavior so that if it starts intersecting a wall it is moved out of that state, or if it hits a ramp we move up the ramp instead of getting stuck.

This type of character controller can be good for a player that might make impossible movements such as turning corners incredibly fast and so on. To some extend character controllers are game dependent, but I believe having a solid capsule based kinematic character in bullet should be available.

btKinematicCharacterController

Introduction

As we mentioned previously the kinematic controller is not in a good state, see some of the current issues:

relevant issues:

Usage

Even though it's not in the best state, we can still use it, here's how:

// create a kinematic character controller
btPairCachingGhostObject* ghost_body = new btPairCachingGhostObject();
// without this line, the ghost colision shape won't collide with anything
overlapping_pair_cache->getOverlappingPairCache()->setInternalGhostPairCallback(new btGhostPairCallback());
// has to be an X shape due to the fact that we have to change up direction
btCapsuleShape* character_body_collision_shape = new btCapsuleShapeX(1.0, 3.0);
//btCapsuleShape* character_body_collision_shape = new btCapsuleShape(1.0, 3.0);
ghost_body->setCollisionShape(character_body_collision_shape);
ghost_body->setCollisionFlags(btCollisionObject::CF_CHARACTER_OBJECT);
btKinematicCharacterController* kinematic_character_controller = new btKinematicCharacterController(ghost_body, character_body_collision_shape, btScalar(0.1));
// The default gravity is pulling down in the x axis, therefore we fix this by pulling down in the y
kinematic_character_controller->setGravity(btVector3(0, -.5, 0));
// only collide with static for now (no interaction with dynamic objects)
dynamics_world->addCollisionObject(ghost_body, btBroadphaseProxy::CharacterFilter, btBroadphaseProxy::StaticFilter | btBroadphaseProxy::DefaultFilter);
dynamics_world->addAction(kinematic_character_controller);

To control the kinematicCharacterBody, we can use setWalkDirection.

Architecture

To understand the different components of a character controller, it's best to build it up from nothing.

Starting with nothing, we know that it should behave like a character, but what is a character anyway? A character is a humanoid like object that is affected by gravity, can move around, jump and bump into objects.

From this it motivates the attribute m_walkDirection representing a heading direction which represents which way the character is facing.

We also want our character to be affected by gravity, naively we could just change the players position by taking a gravity acceleration value and then using that to update our position using delta time. Now our character will have the ability to fall, but since our character controller doesn't have any way of detecting collisions yet, it will just fall forever.

In reality we want our character to fall if it's not on solid ground, so we need to be able to figure out if we're on the ground at any given moment in time. If we're not on the ground then gravity should be applied, if we are then it should not.

This motivates the following attributes

Todo
find which attributes are created because of this.

We also want our character to be able to move along a solid surface, this motivated the method stepForwardAndStrafe, while this allows us to move, if we run into a wall which would be a static rigid body in our simulation then we don't have any code to deal with this collision. Actually you might have already thought about this when we mentioned that our character should not move through the ground too.

The method motivated by this idea is called recoverFromPenetration, the naming convention reflects the idea that first our chracter might run into a wall and get partially stuck in the wall so that the collision shape of the character is penetrating the collision shape of wall, and now we have to recover from this situation, the standard recovery method is to just move the player back to the previous position it was at before it pentrated the wall.

While this penetration method can work, it can cause bouncing when you hold a key into the wall, this is caused by the character body being teleported back out of the wall and there is some space between the character and the wall. Because there is this space, the character has room to move again, this process repeating makes it seem like you are bouncing off the wall.

Even with no bouncing this would result in our player stopping immediately when they touch a wall, this movement would be very jarring because if you're moving quickly and you hit a wall at a shallow angle, then we would expect to keep moving around the same speed instead of just getting locked at the point we touched it.

In general there are two methods of dealing with situations that you don't want to occur, the first one is to allow the situation to occur and then correct before anyone notices, or to look a bit ahead into the future and make sure the situation never occurs.

This is generally how the move and slide function works, as it will first check based on the inputs given to the controller, if the resulting movement would make the character penetrate a static rigid body, then take the extra motion and project it onto the side of the surface so that it moves along it.

resources: