Avoiding Cracking Under Pressure
February 18, 2011 Leave a comment
Sometimes, when working on a project, you get stuck.
The Problem
Recently, I was working on (what should have been) an elementary programming exercise: get the player to rotate around an object in 3D when a key is held down. I’d immediately started tackling the problem from the angle of “OK, how do I rotate around an arbitrary axis?”. Big mistake.
It’s pretty easy to make an error (or twenty) when you’re writing this out in code: (Image source)
For days, I just could not get the code to work. The player quickly developed a knack for teleporting all over the place, but despite his new-found skills with wizardry, he still wouldn’t rotate around the ball.
The Solution
Eventually, I gave up and stepped away from the problem for a while. A few days later, after a full night’s rest, I sat down in front of my laptop, scrapped all the code I had, and started from scratch. I got it working in less than an hour.
Lesson learned: Sometimes, you need to step back and take a break. In retrospect, I feel kind of silly for getting stuck for a few days on this one.
Here’s the (somewhat unoptimized) code to calculate the offset to move by each frame:
// Helper method to re-calculate the translation vector each frame while the
// player is rotating around the ball
// returns the offset the camera should move by
Ogre::Vector3 FrameUpdateListener::calculateRotation() {
// get player position
const Ogre::Vector3 playerPosition = GameState::getInstance()->getPlayerCamera()->getPosition();
// get ball position
const Vector3D* const pBallPosition =
GameState::getInstance()->getBall()->getBoundingVolume()->getCenter();
const Ogre::Vector3 ballPosition(pBallPosition->x, pBallPosition->y, pBallPosition->z);
// get vector for player relative to ball
const Ogre::Vector3 localPlayerPosition = playerPosition - ballPosition;
// get theta (angle to rotate by)
const int direction = isRotatingClockwise ? 1 : -1;
const Ogre::Radian theta(static_cast<Ogre::Real>(direction * 0.05f)); // angle to adjust by
// store cos/sin in temp variables to save calculations
const Ogre::Real cosTheta = Math::Cos(theta);
const Ogre::Real sinTheta = Math::Sin(theta);
// calculate new player position
// (rotate player position around Y axis)
Ogre::Vector3 newPlayerPosition;
newPlayerPosition.x = cosTheta * localPlayerPosition.x - sinTheta * localPlayerPosition.z;
newPlayerPosition.z = sinTheta * localPlayerPosition.x + cosTheta * localPlayerPosition.z;
// newPlayerPosition.y = localPlayerPosition.y; // commented to save the calculation, uncomment if you need this later for some bizarre reason
// calculate dx and dz
Ogre::Real dx = newPlayerPosition.x - localPlayerPosition.x;
Ogre::Real dz = newPlayerPosition.z - localPlayerPosition.z;
// return the offset vector
return Ogre::Vector3(dx, 0, dz);
}

