Trying to make a new character controller

ludovic.silvestre
Posts: 4
Joined: Tue May 11, 2010 9:24 pm

Trying to make a new character controller

Post by ludovic.silvestre »

I'm trying to make a new character controller for a project, similar to the one physx has available. Unfortunately I don't understand some decisions made for the implementation of KinematicCharacterController.

But first I want to understand more about the several collision tests available, because i need to use one to verify if I can move a collision object to a particular position without colliding with another object. As far as I know (I just recently started using Bullet) Bullet has 3 kind of test that do what I want:
  • ContactTest
    ConvexSweepTest
    RayTest
I've heard that the last 2 are pretty expensive in term of performance, so I suppose the ContactTest is the best. Am I right or there's something I'm missing? I also need to know the normal of the contact point, so I can verify if I've collided from below/above/sides.

Now about the implementation, why the KinematicCharacterController tries to recover from a penetration at the beginning of the step? There's only 2 ways we can collide:
  • We move the character and we collide against something (I take care of this while a translate the character)
    Another object (static, rigid or kinetic, depending of the collision flags given to the ghostObject) collide against us (I suppose this is taken care of by Bullet)
For now that's all, but I'll probably have more questions as the development continues :)

Thanks for your time.
ludovic.silvestre
Posts: 4
Joined: Tue May 11, 2010 9:24 pm

Re: Trying to make a new character controller

Post by ludovic.silvestre »

I think I almost got my character controller working, but unfortunately i've got a bug in the collisions and I don't knwo what I'm doing wrong. By the way, I'm using contactTest to test for collision.

In my ContactResultCallback, I'm doing the following when a result is added:

Code: Select all

addSingleResult( btManifoldPoint &contactPoint, const btCollisionObject *colObjectA, int partIdA, int indexA, const btCollisionObject *colObjectB, int partIdB, int indexB )
	btAssert( colObjectA != &mGhostObject );

	if( contactPoint.getDistance() < mMaxPenetration ) {
		mNumCollisions++;
		mMaxPenetration = contactPoint.getDistance();
		mTouchingNormal = contactPoint.m_normalWorldOnB;
	} 
	
	return 0;
}
The attribute mMaxPenetration and mNumCollisions starts at 0, and mTouchingNormal starts as a zero vector3.
Does the distance's signal indicates if the colObjectB point is inside (minus signal) or outside (plus signal) of colObjectA?

EDIT: No need to answer that question, found the answer... Minus signal -> inside and Plus signal -> outside.
ludovic.silvestre
Posts: 4
Joined: Tue May 11, 2010 9:24 pm

Re: Trying to make a new character controller

Post by ludovic.silvestre »

My character controller is working :D Unfortunately I've some problems with the collisions, nothing serious, but the movement seems less fluid than the original character controller. If someone could give me a tip of what could cause such problem, it would be a start.

Here goes the code:
OgreCharacterController.zip
And here goes some examples how to apply some movements:

Code: Select all

// Do this at each update fase ( before or after doing the stepSimulation )
btVector3 movement = btVector3( 0.0, 0.0, 0.0 );
movement += applyMovement( dt );
movement += applyGravity( dt );

// m_character is my character controller object 
m_character->setMoveTranslation( movement );
The function to move forward and/or strafe

Code: Select all

btVector3 applyMovement( float deltaTime ) {
	// Rotate character
	if( gLeft ) {
		btMatrix3x3 orn = m_ghostObject->getWorldTransform().getBasis();
		orn *= btMatrix3x3( btQuaternion( btVector3( 0, 1, 0 ), 0.01 ) );
		m_ghostObject->getWorldTransform ().setBasis( orn );
	}
	
	if( gRight ) {
		btMatrix3x3 orn = m_ghostObject->getWorldTransform().getBasis();
		orn *= btMatrix3x3( btQuaternion( btVector3( 0, 1, 0 ), -0.01 ) );
		m_ghostObject->getWorldTransform().setBasis( orn );
	}
	
	// Move character
	btTransform transform = m_ghostObject->getWorldTransform();
	
	btVector3 forwardDir = transform.getBasis()[2];
	forwardDir.setY( 0.0 );
	forwardDir.normalize();
	
	btVector3 strafeDir = transform.getBasis()[0];
	strafeDir.setY( 0.0 );
	strafeDir.normalize();
	
	btVector3 walkDirection = btVector3( 0.0, 0.0, 0.0 );
	
	if( gForward ) {
		walkDirection += forwardDir;
	}
	
	if( gBackward ) {
		walkDirection -= forwardDir;
	}
	
	btScalar walkVelocity = btScalar( 1.1 ) * 40.0; // 4 km/h -> 1.1 m/s
	btVector3 walkMovement = walkDirection * ( walkVelocity * deltaTime );
	
	return walkMovement;
}
The function to simulate gravity

Code: Select all

btVector3 applyGravity( float deltaTime ) {
	btVector3 fallMovement = btVector3( 0.0, 0.0, 0.0 );
	
	if( isGrounded() == false ) {
		btScalar fallVelocity = -20 * deltaTime;
		fallMovement.setY( fallVelocity );
	}
	
	return fallMovement;
}
The function to verify if we are on solid ground :)

Code: Select all

bool isGrounded() {
	return ( m_character->getCollisionFlags() & Below ) != 0;
}
All those functions where used with the CharacterDemo project. I didn't implemented the jump, warp or reset function into the character controller class, because it depends on how the customer want the character to behave. For example, the jump function could be implemented in several ways:
  • using a fall acceleration and the jump height;
    using a jump speed and a fall speed;
    using a jump acceleration and a fall acceleration;
    etc...
Any idea/opinion is welcome.
You do not have the required permissions to view the files attached to this post.