btKinematicCharacterController questions, take two...

N_K
Posts: 14
Joined: Mon Jun 04, 2012 11:40 pm

btKinematicCharacterController questions, take two...

Post by N_K »

...and hopefully the final one.

Since my dynamic controller does not work as expected, I still didn't give up on the btKinematicCharacterController. I reimplemented the code from the CharacterDemo in my app, and I noticed a few strange things, but these are all present in the original demo as well. I see people using the built-in character controller succesfully for simple projects, but no one seems to have asked these questions, no matter where did I search.

The first thing is, why the character controller can't push dynamic objects correctly? Regardless of the object's mass or material configuration, it can't be pushed by the character. For example, I have a rigid body cube, and a kinematic character controller capsule. When there's a head-on collision with the box, the box will jiggle a very little bit (indicating that the character has came in contact with it), the character controller starts shaking (looks like it's trying to penetrate the box repeatedly, even when CCD is turned on), but the box will "win", the character controller simply slides off from the side of the box it was facing.

But, for some reason, when the capsule hits the box diagonally (what my character normally doesn't), it is able to push it to a varying distance correctly, then it locks down as in the previous case.

This very same thing happens in the CharacterDemo as well, if you shoot a small cube somewhere, then you try to push it with the character, there's a chance that it will push it correctly (but again, only to a limited distance), but most of the time, it exhibits the same behavior as my app.

The other curious problem is that even if the simulation runs at a fixed timestep, the movement depends on the framerate. And to make it more strange, the lower the framerate, the faster the character moves. Again, both in my app and the CharacterDemo.

Any clues about these?

And here's a problem that only happens in my app: even if the initialization/destruction code is the same as in the demo, I have a small (40 bytes) memory leak when using the kinematic character controller.

Here's the destruction code:

Code: Select all

void GameState::exit()
{
    /*The 3D engine's clean-up stuff goes here.*/

	if(kinematicPlayer)
	{
		delete playerController;
	}

	for (int i=phyWorld->getNumCollisionObjects()-1; i>=0 ;i--)
	{
		btCollisionObject* obj = phyWorld->getCollisionObjectArray()[i];
		btRigidBody* body = btRigidBody::upcast(obj);
		if (body && body->getMotionState())
		{
			delete body->getMotionState();
		}
		phyWorld->removeCollisionObject(obj);
		delete obj;
	}

	delete m_pGroundShape->getMeshInterface();

	for (int j=0;j<m_collisionShapes.size();j++)
	{
		btCollisionShape* shape = m_collisionShapes[j];
		delete shape;
	}
	m_collisionShapes.clear();

	delete dbgdraw;
	delete phyWorld;
        delete mSolver;
	delete mBroadphase;
        delete mDispatcher;
        delete mCollisionConfig;
}
What did I miss here?
frank28_nfls
Posts: 7
Joined: Wed Jul 18, 2012 4:54 am

Re: btKinematicCharacterController questions, take two...

Post by frank28_nfls »

I'm also new to btKinematicCharacterController, and I'm not sure if my answers are all correct, just give some thoughts and experiences from my use here.

1, I really didn't notice the "character controller can't push dynamic objects correctly" thing until you mentioned. I test it in the demo, it seems to have some problems too. I do not really understand the dynamic calculation here yet, so I'll just wait someone else to explain it.

2, I never see the problem in CharacterDemo, and only met the same problem in my own app when I used "variable timestep" before. I could think that the only reason you still get problem when use fixed timestep simulation is that, you set that "fixed timestep" too high, like set max numbers of steps to 10, and in fact, this is equal to a "variable timestep" simulation in most cases.

I suppose you just use the code from CharacterDemo, that use "setWalkDirection" to update the pos of character every frame, without noticing that the the number of steps for simulation is fixed to 2 at maximum. See the code from CharacterDemo and also the comments I added

Code: Select all

int maxSimSubSteps = m_idle ? 1 : 2;  // NOTE: m_idle is always false here, maxSimSubSteps is fixed to 2
if (m_idle)
	dt = 1.0/420.f;
...
m_character->setWalkDirection(walkDirection*walkSpeed);
// notice maxSimSubSteps here, we must limit the max nb of steps
int numSimSteps = m_dynamicsWorld->stepSimulation(dt,maxSimSubSteps); 
All I have to say is that, "setWalkDirection" is really misled here, you should read the comment of this method in btKinematicCharacterController.h, it should be called "setPositionIncrementPerSimulatorStep". Then what you see "the lower the framerate, the faster the character moves", should be just logical: when in a "variable timestep" simulation, lower the framerate, more the number of steps, so more the character moves.

In my app, I just get rid of this method. Instead, I use setVelocityForTimeInterval, as I also need the "variable timestep" simulation. Here is my code:

Code: Select all

void MyCharacterController::UpdateMove( const btVector3& deltaPos, float deltaTime )
{
	if (m_btCharacter)
	{
		btVector3 nextPos = deltaPos / deltaTime; // a little bit inefficient here, but necessary...
		m_btCharacter->setVelocityForTimeInterval(nextPos, deltaTime);
	}
}
and in this way, I could update the dynamic world in a normal way:

Code: Select all

int numSimSteps = m_dynamicsWorld->stepSimulation(deltaTime, 10);
3, The CharacterDemo has a LOT OF memory leaks too, don't follow the example for memory management. The leak is not in btKinematicCharacterController though. See the code carefully for creation the character controller:

Code: Select all

	m_ghostObject = new btPairCachingGhostObject();
	m_ghostObject->setWorldTransform(startTransform);
	sweepBP->getOverlappingPairCache()->setInternalGhostPairCallback(new btGhostPairCallback());
	btScalar characterHeight=1.75;
	btScalar characterWidth =0.75;
	btConvexShape* capsule = new btCapsuleShape(characterWidth,characterHeight);
	m_ghostObject->setCollisionShape (capsule);
	m_ghostObject->setCollisionFlags (btCollisionObject::CF_CHARACTER_OBJECT);

	btScalar stepHeight = btScalar(0.35);
	m_character = new btKinematicCharacterController (m_ghostObject,capsule,stepHeight);
so all is obvious, you have newed:

a btPairCachingGhostObject(m_ghostObject),
a btGhostPairCallback(unnamed, so need to add a variable here to track it),
a btCapsuleShape(m_ghostObject->getCollisionShape),
and finally btKinematicCharacterController(m_character).

And no, you could not just delete "m_character" here, because Bullet didn't help you to manage any newed memory, so you have to delete all of them by yourself in the reverse order of creation/initialization.


btKinematicCharacterController works quite well here in my app, well, not perfect, as I also have some questions about it to ask. Just hope the above may give you some help.