Ok, first off I have to say that this turned out to take a lot longer than I thought. I figured it would be pretty quick to knock out a test version - I could use some of the code I'd written during prac classes in weeks earlier, and get a test version out for you netizens to test and critique for me... Instead I had lots and lots of problems with maze scrolling, sprite rendering, assertion failures, positional determination etc etc. (They were all logic problems - I didn't have any.)
Well, it works now; there's still a few glitches and lots of stuff yet to be worked out, but I don't think it's too bad for something knocked together inside of a week.
Anyway, this post is about you and me having a chat about this program (well you read my prattlings about it anyway), that you might better understand and be able to critique it for me...
Where to start?...From the beginning I guess... The program is written and compiled using MS Visual C++ 6.0 Standard with the DirectX 7 SDK, here on my humble Win95 machine (gee whiz, program compilation takes a LONG time on this!), and has been tested with DirectX 8.0a runtime. We've been using the 8.0/8.1 SDK at uni and been getting horrid memory leaks with every program, but I didn't encounter any of that here, so it should be sound. You'll need DirectX 7 or later and that's about it... as longas your PC goes, it should run (it did on this dinosaur). Presently, it runs in 800x600x16 mode, so you'll need at least 1MB of video memory for it (which I don't think is asking too much).
At present I'm still using the bitmaps created for us by Jason, one of our lecturers. If you've already run the program you've probably seen that they're ugly as heck, but they'll do for working out the mechanics of the game. The player sprite (hereafter 'Zippy') is also still a poorly-drawn platypus that does not quite match the size of the tile elements of the maze it wanders around in, but it's free-roaming so this is not a problem.
The maze is made up of 9 tiles to form a world of 3x3 tiles, each with the same dimensions as the screen (which as I said above, is 800 x 600). Each tile is made up of 16 x 12 elements - walls, pickups etc. I'll have more to say on all this later. Do the mathematics and you'll see that this means that there are 192 elements per tile, and 1728 elements for the whole maze, which is quite a lot, so rather than define a maze I've written an algorithm to randomly generate the maze every time the program is run. Initially I was worried that this might produce some duff terrain, and sometimes it does, but from my testing it's turned out to be quite satisfactory so I'll probably just leave it as it is and just tweak the weighting for the random element generation depending on the feedback I get (IIRC it;s something like 50% is empty, 25% is walls and the rest is divided between holes, food and poison).
When playing, the maze continually scrolls around at constant speed - the direction and time (up to 10 sec of scrolling) is determined randomly. The player (as Zippy) has to navigate around, avoiding getting 'splatted', either by getting squished between a wall and the edge of the screen, or by running out of health. Zippy has a minimum move rate of 50 pixels per second, plus an extra 50 multiplied by his health as a decimal (so at 100% health, you can move 100 pixels per second). The maze scrolls at 60 pixels per second, so obviously you want to keep moving as fast as possible to negotiate the maze and not get splatted.
In the maze, blank while is empty ground.
The red-and-yellow crosshatched things are meant to be walls, and Zippy cannot travel through them (well, except for when the game glitches).
Food is represented by the red-and-yellow cans of Beans - if you run Zippy into them, he gains a random amount (up to +10) health which makes him go faster. As yet there is no upper limit on the amount of health you can have, so you start on 100% and can get well over 300% (currently record is 650+ set by brother, although that was with an earlier development version of the program in which you could still walk through walls).
Poison is represented by the skull-and-crossbones - if Zippy eats them, he loses up to 10 health, which makes him go slower, and if it reaches 0, he dies.
The round green things are actually holes - I was at bit of a loss for what to do with them, so for now they just halve Zippy's movement.
Zippy's current health is displayed in a read-out at the top left corner of the screen. When you first run the program, it will be paused for your convenience. You can press 'P' to pause/unpause the game. If you want out, hit the Escape key with enthusiasm to terminate the program.
Hidden goodiesThe effects of my debugging and diagnostic work still riddle the program and can be called upon if you know the correct keyboard codes (just like with MTM2 in fact!):
Framerate counter: I have placed a framerate readout (to two decimal places) in the bottom right corner of the screen so I can monitor performance. Presently it defaults to on, but you can make it go away (and then come back again) by hitting the 'F' key. The program maxes at 60 fps, so the counter shouldn't go any higher than that. On my comp it was averaging about 10 fps in the screen mode it'd chosen, which probably isn't too bad if you consider what my algorithms look like! Some convoluted stuff, let me tell you. (Not that that has any more than a minor effect on the slowdown - the main culprit is DirectDraw, as bit-blitting at high colour depth causes the most lag.)
Maze Reader: each tile of the maze is really just a big array of integers. For stuff like collision detection with walls, and detecting whether we've just run over a pickup, the player sprite has to 'read' the maze. This is down to some tricky algorithms to quantify screen position of the sprite in terms of what element of the array we're looking at. While I was trying to get this to work, I made the program paint a number on Zippy's back so I could see what it though it was walking over. You can toggle this on/off by pressing capital 'M' (ie. Shift+M).
Diagnostic Readout: Debugging is practically impossible with a program like this, and when the above approach didn't give me enough information to work out what was happening, it was time to resort to the sledgehammer method of printing every variable involved - screen coordinates, world coordinates, tile numbers, array indices, the lot - onto the screen so I could see the numbers in real-time. The master switch is 'D' (that's capital D, mind) which will print out said values in a hefty portion of the lower left of the screen, as well as override the Maze Reader mode above.
Known glitchesSometimes (on my computer at least) the game will hang momentarily to load a new tile from memory as the maze scrolls. Because the player movement is based on time elapsed to keep a constant rate of motion, it sometimes happens that Zippy will travel right through walls because the collision detection method isn't called until after it moves: ie. game hangs = long elapsed time = long distance travelled before the game checks where the sprite has got to).
The collision detection works by drawing an invisble bounding box around the sprite and reading the maze at 8 points along the sides of that box (in each corner, and the middle of each side). I fudged the collision detection method because I was worried about CPU time - instead of doing it the failsafe way of calculating the position of each point in the maze world from scratch, I just calculated the position of the middle of the sprite and then adjusted this for each point. This means that when the sprite is moving from one tile to another, some of those coordinate values become invalid, eg. when half of the sprite is on one tile, and half on another. To avoid errors I simply temporarily switched-off the detection of walls with the affected points while the sprite is in the transitory state, so it is posible to have weird things happen like running halfway up a wall where the tiles join. I've also had it appear to bounce or slide off walls in an unusual fashion one or twice (may or may not be related to the above) but overall I surprised myself in that it seemed to work quite well.
Things to do, and stuff I'd like help and ideas forFirst, I want to make some nicer bitmaps. Still not decided on what critter I'll go for, but at this stage it's looking like I'll either stick with the platypus, or maybe some sort of lizard (chameleon or iguana) would appeal...
Do some sort of frontend for the game - just a dialog box window would do. Figure out how to Enumerate Display Modes, so the player can pick the display mode they want to play at before the game starts from said dialog window. Maybe even customise controls.
Figure out transparency so we haven't got an ugly green rectangle around the sprite...
As I said, the tiles are each set to the screen dimensions. I did this because in a tutorial we were meant to develop a scrolling circular world that used tiles that exactly fill one screen (640x480) though I note that's not a prerequisite of the assignment. Anyway, if I were to implement a method for picking what screen res to run at, I wanted to make the tiles scale so that someone running at 640x480 could still see the same area (in lower detail) as someone running in 1024x768. To make it exactly tile to fit screen dimensions of 640x480, 800x600, 1024x768 etc, you have to use a factor of four. The only ones I could work out that fit were 16x12 elements per tile (40x40 tile elements for 640x480, 50x50 for 800x600, and 64x64 for 1024x768), which I felt was too many elements per tile and too small for the elements I wanted to draw (especially for defining a maze!), and 8x6 (80x80 tile elements for 640x480, etc), which was too few and too big. After seeing the results of my random maze generation, however, I've come to think that maybe 16x12 elements per tile isn't too bad. So what I'm considering (if I do get EnumDisplayModes figured) is to draw my tile elements at 64x64, and shrink them for smaller resolutions. Just wondering if anyone else has any thoughts on this.
At present, there is no victory condition for this game - if you're good enough you'll just keep wandering around for eternity, I haven't even got it to keep score yet. Jason suggests:
You need to add at least one other challenge to gain the reward of finishing the maze (eg: picking up an axe, and breaking down a door to leave the maze). An invisible door exists, which becomes visible only when you have (say) achieved a certain amount of points, or picked up an axe surrounded by poison. I leave it to you to animate a platypus carrying an axe.So that's one thing I need (hopefully I can do better than that example though!). One idea I had was the collection of baby platypi from the maze to open the exit? The problem is that it scrolls randomly, so it wouldn't be good to employ something that requires you head from A to B. Also some powerups (double speed, invulnerable) and useful items (freeze maze, etc) would be fun.
Finally (for this post anyway) I need help tuning the game, as I think it's probably a bit too easy to survive at present and needs more ideas... any suggestions on this - things like changing the proportions and types of maze elements, speed of scrolling and/or player sprite, and any other ideas for things to add to it (levels with increasing difficulty, victory conditions, scoring mechanisms...) would be most welcome.
You can read the assignment criteria at the following link, might give a better idea of what's needed:
http://clio.mit.csu.edu.au/subjects/itc320/02_assignments/assignment1.html Anyway, I'll be off for a day or two, so leave your thoughts and I'll come back to it when I get back. Enjoy playing with it for me
D2S
[This message has been edited by Drive2Survive (edited 12-04-2002).]