Posted on December 11, 2007
Pumpkin Man Mid-Mortem
Games take a long time to make. Seriously, games are a bitch to make. I started my latest game as a supposed month long project to update Pacman into the semi-modern (circa 1990) age. I figured that Pacman would be a pretty easy game to make, and I also wanted to create a new engine in AS3 for rendering the screen. I have made many mistakes along the way, some I have fixed and some I am currently living with. Since I didn’t know much about AS3 when I started, I figured that I would use all of my mistakes as a learning experience. The game still isn’t done, but I am nearing completion, and I thought it would be cathartic to spew out some of my thoughts on game design, AS3, etc before I tackle the last few pieces of the game.
Pacman is much more complicated than I thought. I have a collection of game programming, flash, game design, and video game history books that rival any good bookstore or library. None of them have any examples of how to create a decent Pacman clone. One does have a maze/chase game, but it is one of the early Flash grid-based games where the player jumped from tile to tile. The tiles were just painted on the background (on a permanent layer) and the grid was nothing but an array of values (0,1,2) representing walls, dots, and open squares. The enemy would chase the player to a fault, and the run away when a power dot was eaten. Not too shabby for an early 2000’s Flash 5 game, but not what I was looking for either. The real Pacman is much more complicated than that. The player smoothly roams between tiles, but cannot change direction unless the tile in the direction he wants to go is open. Also, the player remains in the middle of each tile and cannot float to either site. This type of movement requires the use of relatively simple math to tell when the player is in the center of a tile, and only then let him move in the next desired direction. My first problem – what is the integer center of a 32×32 tile? Is it 15 (on a 0 based scale) or is it 16, or both? Plus, since I am using bitmaps for everything, the center of my sprites is not the same as the origin point (0,0). That forced me to make extra calculations to find centerX and centerY when needed and then check that center point against the center of a tile so a direction change could be made. I also chose to store all of this data as integer values to save calculation speed, so what I received is a bunch of rounded off numbers that work, but are not exactly as precise as I would like.
I did come up with a hack solution that required the used of a range of acceptable values for the center of a tile (15 or 16 will work), but I am currently (after 2 months of dev time) not satisfied with the final result. It is on my list of bugs to fix if I have time. It is probably ok to use for now, and only I notice any problems, but this is my new baby and I want to feel like I gave it my all when I am done. Needless to say, I had originally thought that movement would be the easiest part but it took quite a while and I am still unsatisfied with it.
Basic chase and follow is easy. Doing it around a maze is a pain in the ass. I spent an inordinate amount of time trying it out on my own before I hit up an older Jobe Makar book to find his hit quick and dirty maze AI. Basically, the enemy chase you in the direction you are closest. If a wall is present in that direction it chooses the other. If it’s blocked in that direction too, or if both directions are the same distance away, it randomly chooses as new direction. Each of my enemy have an intelligence of 0-90. This the % time that it will not choose an random direction, but follow the AI rules. This all works pretty well until I got to the part where the enemy need to run from the player. If an enemy is just spawning, and currently has a speed of 0 when a power pellet is eaten, he freezes in place. This is because I just send the enemy running from the player at his current speed * -1. If the current speed is 0, and 0*-1 is still 0 and he hovers around the re-spawn point like a idiot. This is yet another bug I intend to fix very soon.
Boy do I hate fonts! I mean, I love them, but we have a love-hate relationship. Fonts cause me all kinds of trouble, so I decided to create my own font (based on Arcade) for this game. I traced over an existing font (changing characters to my liking) with vector squares in Flash. This gave me a set of characters (40×40 roughly) composed of white squares with a black outline. I used the colorTransformation object to add color to the white and subtract from the black to dynamically create different colors for the text (this can also be done with the Advanced color change option in the properties of an instance on the timeline). To display the text on the screen, I first dynamically built a Sprite, create a colored blur filter on it, and then draw it into a bitmapData object. Doing this slowed the game to a halt while GAS (gotoAndStop) was called on the alphabet MCs I created to dynamically hold each letter. My frame rate dropped from 40 to 10 every time a message was built the first time, then went back to 40 as the cached bitmapData version was placed on each subsequent frame. That was not good, so I then proceeded to pre-build all of the text messages as mcs and do the same blur filter and cache as runtime. The performance by not using GAS was outstanding and my frame rate stayed at 40 the whole time.
Here is the current result of the dynamic text render (ATTACK!). It starts to alpha out, so the screen capture is about 10 frames into that fade. In any case, I need to make the text darker as it is difficult to see. As a side note, the ‘bonus + 25’ is a font that is dynamically built and the textbox cached as a bitmap. I did this because the bonus amount can be any number. They don’t have a tequila bottle large enough for me to try and build every number can think of as a pre-rendered mc.(if I did drink a bottle that size, text in a Pacman game wouldn’t be my current worry…)
I started out having virtually no experience with using events in action games. I had used them for puzzle games and in a few projects with data grids, etc, but never tried to use them for rendering an action game. I figured that it might slow down the game or at least make it difficult for me to know when an individual event completed. Oh how wrong I was. My first use of events was to send out an update event to each game character and animated tile on every frame. Once those are complete a render event is sent out. My first thought was that I would never know when the update was complete, so the render would be using the old values…NOPE! The update event runs, and only after each object is updated does the render event get fired off. I didn’t do anything special, this is just how Flash calls multiple listeners – in order, and waits until they have all been called before it moves on in processing. I was under the impression that is was async, but they all run like a series circuit, no parallel processing here. In this case it’s a great thing.
For the first month of programming, those two events, along with a keyListener, and an OnEnterFrame event were the only events in my game. Then, one day I needed to write a soundManager class. It turned out that the best way to fire off all of my sounds and music was to respond to in-game events. So, since all of the most important events needed sounds, I built the sound manager to respond to them, and then retro-fitted the scorePanel, and LevelIn, and LevelOut screens, as well as Player and Enemy objects to respond to the same set of events instead of just calling methods all over the place. This makes for some clean-ass code. I have a long way to go to make a completely Object Oriented / design pattern clean / event based game engine, but so far, so good.
There are two problems with this approach. One, make sure you create all of your listeners with a weak reference. This will make garage collection a dream. Second, if you go about this in an un-organized manner like I did, you will come up with more chicken before egg instances than you can handle. One example is that I needed the Player to listen for ScorePanel events and the ScorePanel to listen to Player events. I was originally creating all of the listeners in the constructor of each object…hmm, how do I do that if each object relies on the other to exist before it can register a listener. My solution was to move the listener add/subtract to a separate method and only call it when all needed objects have been instantiated. That will teach me to plan a little before I just go jump into a new design pattern (in the middle of game development).
Anyway, those are just a few of the many things I have used / learned while creating my first real AS3 game. I will have more updates as the game nears completion.