btCharacterController movement with variable framerate

Mind Calamity
Posts: 13
Joined: Tue Feb 28, 2012 7:51 am

btCharacterController movement with variable framerate

Post by Mind Calamity »

Hi, I'm trying to use the btKinematicCharacterController and this is how I'm currently using it:

Code: Select all

	btScalar walkVelocity = btScalar(1.1) * 4.0 * mSprint; // 4 km/h -> 1.1 m/s (5x if sprinting)
	btScalar walkSpeed = walkVelocity * dt; 

	int maxSubSteps = 5;

	Ogre::Vector3 strafeDir = (mCharNode->getOrientation() * Ogre::Vector3::UNIT_X) * mDirection.x;
	Ogre::Vector3 forwardDir = (charDir * mDirection.z);
	Ogre::Vector3 actualDirection = strafeDir + forwardDir;

	mCharacter->setWalkDirection(BtOgre::Convert::toBullet(actualDirection) * walkSpeed);
This gives me some skipping while moving. And if I set dt to 1 / 60, then it's stable, but I've experienced similar problems with lower framerates. Any way to make character movement framerate-independent ?
jazztickets
Posts: 21
Joined: Tue Dec 29, 2009 12:48 am

Re: btCharacterController movement with variable framerate

Post by jazztickets »

You need to use a fixed time step for your physics update.

Check out Glenn Fidler's article on the subject:
http://gafferongames.com/game-physics/f ... -timestep/

It basically comes down to your game loop:

Code: Select all

float TimeStep = 0.01666666f // 60 hz

TimeStepAccumulator += LastFrameTime;
while(TimeStepAccumulator >= TimeStep) {
	UpdateGameLogicAndPhysics(TimeStep);
	TimeStepAccumulator -= TimeStep;
}
This loop eliminates the "lower framerate problem", but introduces temporal aliasing.

LastFrameTime is the time between this frame and last.
Inside UpdateGameLogicAndPhysics is where you would called bullet's stepSimulation function (with a constant value: TimeStep)

The next step is eliminating the temporal aliasing problem. Doing that requires a bunch of work (you have to derive btDiscreteDynamicsWorld if you want to use motion states to inject the timestep remainder).
Mind Calamity
Posts: 13
Joined: Tue Feb 28, 2012 7:51 am

Re: btCharacterController movement with variable framerate

Post by Mind Calamity »

Thanks a lot that gave me a stable simulation at 60 FPS, but if the framerate goes over 60 the simulation gets too fast. A workaround to this is using VSync to imit the framerate, anything less hackish ?
jazztickets
Posts: 21
Joined: Tue Dec 29, 2009 12:48 am

Re: btCharacterController movement with variable framerate

Post by jazztickets »

You may have to post the code surrounding your call to bullet's stepSimulation. If done right, the framerate won't affect the physics. The call to stepSimulation should have zeros in the last two parameters. This makes it skip bullet's internal fixed time step calculations. I'm not sure why it's even in there in the first place. Fixed time step needs to be applied to all game logic, not just the physics.

Note that the value of TimeStep should never change during the simulation. LastFrameTime is the value that feeds the timestep accumulator.

Code: Select all

float TimeStep = 0.01f // 100 hz

TimeStepAccumulator += LastFrameTime;
while(TimeStepAccumulator >= TimeStep) {
   UpdateCharacterMovement(TimeStep);
   World->stepSimulation(TimeStep, 0, 0);
   TimeStepAccumulator -= TimeStep;
}

Can check out my code here and see if you can figure it out: https://github.com/jazztickets/ogrelamb ... c/play.cpp
Mind Calamity
Posts: 13
Joined: Tue Feb 28, 2012 7:51 am

Re: btCharacterController movement with variable framerate

Post by Mind Calamity »

Here's my code:

Code: Select all

	double timeSinceLastFrame = 0;
	float TimeStep = 1 / 60.0f;
	float TimeStepAccumulator = 0.0f;

	while (!mWindow->isClosed() && !mShutDown)
	{	
		boost::timer frameTimer;
		Ogre::WindowEventUtilities::messagePump();
		TimeStepAccumulator += TimeStep;
		while(TimeStepAccumulator >= TimeStep) 
		{
			update(TimeStep); // Here I call stepSimulation(TimeStep, 0,0);
			TimeStepAccumulator -= TimeStep;
		}
		
		//update(TimeStepAccumulator / TimeStep);

		mRoot->renderOneFrame(timeSinceLastFrame);

		timeSinceLastFrame = frameTimer.elapsed();
		frameTimer.restart();
	}
And the result is still the same, the simulation is still too fast.

Thanks for the link, I'll have a look ASAP.
jazztickets
Posts: 21
Joined: Tue Dec 29, 2009 12:48 am

Re: btCharacterController movement with variable framerate

Post by jazztickets »

Close, but you have one fatal flaw on this line:

Code: Select all

TimeStepAccumulator += TimeStep;
The timestep accumulator should be incrementing by the last frame time, which can be calculated in ogre as:

Code: Select all

// Handle last frame time
float FrameTime = (Timer.getMilliseconds() - TimeStamp) * 0.001f;
TimeStamp = Timer.getMilliseconds();
if(FrameTime > 1.0f)
	FrameTime = 1.0f;

TimeStepAccumulator += FrameTime;
...
Timer is an Ogre::Timer object and TimeStamp is just an Ogre::uint

Or just use timeSinceLastFrame, assuming the units are seconds.
Mind Calamity
Posts: 13
Joined: Tue Feb 28, 2012 7:51 am

Re: btCharacterController movement with variable framerate

Post by Mind Calamity »

Yep, that did it, thanks :)

Code: Select all

	double timeSinceLastFrame = 0;
	float TimeStep = 1 / 60.0f;
	float TimeStepAccumulator = 0.0f;

	while (!mWindow->isClosed() && !mShutDown)
	{	
		boost::timer frameTimer;
		Ogre::WindowEventUtilities::messagePump();

		TimeStepAccumulator += timeSinceLastFrame;
		while(TimeStepAccumulator >= TimeStep) 
		{
			update(TimeStep);
			TimeStepAccumulator -= TimeStep;
		}
		
		//update(TimeStepAccumulator / TimeStep);

		mRoot->renderOneFrame(timeSinceLastFrame);

		timeSinceLastFrame = frameTimer.elapsed();

		if(timeSinceLastFrame > 1.0f)
			timeSinceLastFrame = 1.0f;

		frameTimer.restart();
	}
That's the final code.

I think the temporal aliasing is noticeable a bit now, so I'll have to do something about that soon. But the current code will do just fine for now.
jazztickets
Posts: 21
Joined: Tue Dec 29, 2009 12:48 am

Re: btCharacterController movement with variable framerate

Post by jazztickets »

Cool! Glad to hear it worked. When the time comes, fixing temporal aliasing requires saving the previous transform of every object and blending between that and the current position of a factor of (TimeStepAccumulator / TimeStep). This means all the objects will appear to be delayed by at most 1 timestep (which isn't noticeable at all). Luckily bullet does this for you if you use motion states, but in order to pass the blend factor to bullet you have to access a private member inside btDiscreteDynamicsWorld. The variable is called m_localTime. That's why i had to derive my own discrete world from that.

Again, you can just look through ogrelamb's code to find everything.