This time around I've added a user-controllable player who can shoot, I fixed a few bugs, and created proper classes for the "RocketMan" and rocket. It's starting to look like a real game. There's no collision detection or enemy AI yet, so you can't really do much. Even so, we're getting close to having something playable up and running.
Here's what I ended up with:
Here's my attempt at a top down view of the player:
With a few tweaks to the TexturedObject class from the last installment, it's fairly easy to get the player, the bullets he fires, the enemy character, and the rocket into simple classes.
The Input class was expanded quite a bit to allow some input. I'm an old-school Quake player, so I wanted to be able to strafe and shoot easily. I figure that the Wiz the shoulder buttons can be used to rotate the player, while on the Pandora the second analog stick can be used to rotate. For now I'm using the mouse to rotate, and the WASD keys to run and sidestep.
For now the player runs backward more slowly than he runs forward, and running sideways is somewhere in between. This seems more realistic, and may end up enabling some interesting game mechanics.
I struggled quite a bit to get reliable mouse and keyboard input working under Windows. I didn't want to spend a huge amount of time on it, but ended up sinking a good night or two trying to work the kinks out. It turns out I had a message loop problem in my last several installments which was causing some of the problems.
I finally found a
good article on the web talking about capturing mouse input. The article is over 11 years old, but the code still worked great. There are probably more modern ways of reading the input, but it solves the immediate problem.
I had to brush up on my fixed point math skills. In BlastRiot I'd implemented all the math as fixed point, but there wasn't much math involved overall. Plus, that was over a year ago. Since this game needs sine and cosine functions I had to relearn a bit as I went along.
Here's a good article talking about trigonometry function implementation using fixed point. I struggled along, my current solution is probably not optimal, but it works.
I added my cosine/sine functions to a new class named FixedTrig. It's a singleton, and the first time it's referenced it builds a look up table used by the sine and cosine functions. It stores 1024 values over 90 degrees in a 0.10 fixed format. Note that it takes degrees instead of radians for it's argument, OpenGL uses degrees, so this saves a conversion.
CODE
class FixedTrig
{
public:
// Get singleton instance
static FixedTrig *GetInstance(void);
~FixedTrig(void);
// Returns the cosine of the passed in angle. Angle is in degrees.
S32BIT Cosx(S32BIT angle);
// Returns the sine of the passed in angle. Angle is in degrees.
S32BIT Sinx(S32BIT angle);
};
The trigonometry functions are used to move an object along a path. This is needed when the player shoots his gun, or the rocketman fires a rocket. In both cases the projectile inherits the angle from the shooter, then moves along that angle at a certain speed. The CalcOffset method in the TexturedObject does the work. It's a bit nasty due to the fixed point math.
CODE
// Uses object's angle and passed in x and y values to calculate offset
void TexturedObject::CalcOffset(GLfixed
ist, GLfixed yDist, GLfixed &lx, GLfixed &ly) {
lx += (((
ist >> 8) * FixedTrig::GetInstance()->Cosx(angle)) >> 8) -
(((yDist >> 8) * FixedTrig::GetInstance()->Sinx(angle)) >> 8);
ly += (((
ist >> 8) * FixedTrig::GetInstance()->Sinx(angle)) >> 8) +
(((yDist >> 8) * FixedTrig::GetInstance()->Cosx(angle)) >> 8);
}
For the RocketMan class, I made a straight-forward update method that just moves him in a circle firing rockets as fast as he can. All the classes have simple bounds checking to make sure they don't move out of the arena, but there's no real collision detection yet.
After getting things up and running I quickly found that the control scheme was a bit hard to use. In a first person shooter the world rotates around you as you turn. Here, the world was standing still and just the player was rotating.
After much trial and error, I got the playfield to rotate around the player. This makes things a lot easier (at least for me), and makes me hanker for some deathmatch. Even though it took me a while to figure out the required translation and rotation order, it only took 3 or 4 lines of code to get this to work.
The first half of the video below is recorded using the original control scheme, about halfway through it switches to the other mode.
http://www.youtube.com/watch?v=XVVtrfm3b1Q
Next time around I'm hoping to address collision detection and some simple AI for the bad guy.
The sourecode for this version can be found
here.