There's loads of interesting things to think about when creating a game as I discovered, as this version went beyond my simple childhood experiments. Capturing key events is one thing, but my initial version involved you being perfectly lined up with a direction at a junction or the move wouldn't be made. This was fixed by adding a buffer to the key events - the next move is stored until it can be made - if it can't be immediately the current direction persists and movement switches to the buffered direction as and when it becomes possible. Looking at the resulting action and how smooth it is, this is how it works in other games.
Programming the logic for the ghosts again was fun. I didn't want to just recreate the original game (read as "I had no idea how to create the original game!") and wanted to see what I could come up with. The result was that they made decisions randomly at junctions, however if you are actually in line of sight they will change direction and move towards you at any point there is no wall between you and them. This was fairly simple traversal of the obects down the x and y axes at each co-ordinate where a ghost was. If you had eaten a 'powerpill' they would do the opposite. Actually, this isn't technically correct, in the 1999 version they will try and move in any way other than towards you - whether or not it is possible. The result of this is it slows them down - if they can't make that move then they don't. This gives you a chance to catch up with them. This was a decision made because of the speed issue - running the game in a single loop meant everything moved at the same speed and you couldn't ever catch them. I did experiment with two game loops - one for you and one for the ghosts, and switching the loop speeds as and when a powerpill was eaten. I had lots of interesting effects out of this and some were really a lot of fun to play, but I found that I was coming up against the computer processing speed. I discovered this when I sent the game to someone with a far higher powered computer than mine and the whole thing ran so fast you never had a chance to do anything before getting eaten! Well, the solution that I actually went with was pretty hacky to say the least - essentially it was turning a bug into a feature - but I was extremely pleased with it at the time - it got round the processor speed issue , and it seemed to give the most playable experience and somehow added to the game.
In 2009, the game forgotten about, somebody tracked me down (I have no idea how!) asking if I was the chap that wrote this particular game he'd found and could he have a copy for his pacman archive. Digging it out I discovered it didn't work in Chrome or Firefox, but fixed that up, made a few minor improvements including getting rid of alert boxes between lives and added in shiny new divs(!), and new slightly better ghost images (which I blatantly stole of some web site somewhere.. ho hum!)
I can honestly say that I don't think I've ever had so much fun creating anything in programming, and it's led me to think about it again some 17 years down the line, when I finally had a bit of time on my hands What would I do differently now?
Almost all of it, it turned out. First up, given that I have all the data for the maze stored in objects, it seems logical to actually generate the maze image itself out of this data. I had a look on the internet for how other people had achieved this and discovered some great things, including the 'Recursive Backtracker' algorithm for randomly generating mazes. This could make the game very cool indeed, however I had one issue - pacman traditionally has double walls - it's not a simple case of occupying every cell and adding borders as required to create the maze. After a bit of thought about this, my initial experiments in this in March2016 can be seen in the original code by going to the settings page and changing 'image' for 'css' in the mode field.
Ignoring the recusrive backtracker for now, I started by making borders for the cells as the data was read from left to right on each row. This gave a rough outline but was nowhere near good enough and the double walls were'nt taken into account. After a few experiments I started adding extra bits and pieces using css :before and :after selectors, and eventually was able to create the lower wall from each line. The result was close, but not bad at all, and there were breaks in the walls are because the data is not put into a perfect grid in the first place. Each move of a 30x30px pacman is a 10px move, pills and junctions are roughly in a grid of 50x50px cells, however sometimes they're 60x50px. And at that point, I realised that the only way to do this properly was to do it properly - aka a rewrite.
Beginning in March 2016, I copied the code to a dev folder. First I needed a robust grid, and something easier and less time consuming to program than the huge arrays of possible moves for each maze, which took a good couple of hours each time I wanted a new maze. This was done using 0s and 1s to mark spaces pacman could be in (1s) and walls (0s). I then wrote a translation program to translate this data into the same data structure I originally used, but this time in a 2d array rather than an array of objects. This meant the new mazes were playable immediately which was good, and being nicely lined up meant none of the css troubles working with unevenly blocks of possible move positions. With everything happening in 50px increments, I was able to make the maze look pretty decent using my original idea with css :before and :after selectors, but soon realised that I couldn't get the rounded corners building up the walls from the movement cells - I had to draw the walls from the spaces where pacman *couldnt* move in the grid rather than the path spaces. A further rewrite and I perfected the CSS maze drawring from the data fairly quickly. Finally confident in my approach, I added some other characters to the maze data - 3s to denote the ghosts home space, 4 to add tunnels to the other side of the maze, and a 5 as the entrance to the ghosts home (which has a red barrier over it) allowing me to render a pretty good maze.
With the maze graphics gone from the equation, pacman was next. The current version is a CSS animation of two semicircles which rotate according to a CSS keyframes animation. The ghosts are still the old graphics.
Moving to the logic I did the tiniest bit of tidying up. When a ghost was eaten in the original it would follow a path to home which was pre-programmed into the maze data array. With this missing from the now binary maze data, it needed to be calculated at each junction instead. This was far easier than anticipated. Simply comparing the co-ordinates of the home barrier (denoted by a 5) to the ghost positions I suggested a direction to move in. How to decide whether to go left/right or up/down though? I simply picked one arbitrarily so it would always take a horizontal choice if it had one. This meant they all got stuck underneath the ghost house if they were eaten underneath it. Then it became obvious - the last movement they make is down, therefore up/down should be prioritised over left right. One little change and it worked perfectly. The seemingly complex paths they sometimes follow are actually just generated out of this logic, which also explains why at the end of a level the ghosts do a little dance around their home base - they're simply seeking the path to the home position in the array. Later did I read that similar logic applied in the original Namco algorithms.
A little bit more playing with the code and I created a 'headFor' function, which allowed me to send a sprite to any co-ordinates I chose. Not only could I 'home' them, but could send them to the four corners of the maze just like in the original. Then I programmed some mode switching, and implemented 'scatter', 'chase' and 'random'. Using the headFor function I could send them to scatter to the corners and keep circling - as they endlessly try to get to a position in a wall they can't actually ever reach (again, I later discovered this is exactly what Namco did!), or to the position of pacman for chase mode, and to home when they were eaten. Suddenly, the game started behaving eerily like the original! It was fascinating to see a few small algorithms create the seemingly far more complex behaviour that Pacman is known for.
Finally with some dynamic path calculation, I put in an extra bit of logic so that they could chase pacman a little more aggressively. This made them very aggressive, so I programmed a dice roll as to whether they would head towards you or use the existing logic. With a bit of tweaking the probabilities, this element of randomness crossed with giving them more of a purpose has improved the game no end.
The last thing I had to do was get the ghosts to look up the on screen tunnels dynamically. This was the last bit of logic tweaking that I am going to do. The code is too complicated to work with effectively and it needs a rewrite. Even if I end up with essentially the same logic, the code is very messy. Everything is in global variables and functions such as getGhostDirection should do exactly that at a junction - not be called several times during a single loop depending on if they've generated an acceptable direction or not... and all those eval statements which I needed once upon a time for a certain browser environment can all go too - as can framesets, separate pages for each level, tables for layouts and mazes that only work at the top left of the window..
One last update.. (maybe...)
A year has passed and it is March 2017. I've made many of the changes mentioned above at odd times here and there, and implemented some other things such as css transitions when resetting the sprite positions. Attempting to play the new version is still annoying me as the movement is too 'jumpy'. I'm still moving in 10px increments, as are the ghosts. So I've just changed this now to 2px increments, implemented a moveInc variable to set the increment so I can play with it easily, and the resulting action is nice and smooth! The game timings have had to be adjusted to deal with the fact that there are now more game loops going on per second. Finally, it feels like I've created something quite good!
So where next? Possibly something where different algorithms are used at different times, random maze generation and create your own mazes.
The original has been archived as pacman-1999 on github. Version 2.0 is now there as pacman-2016. Version 3.0 is being contemplated, starting with a huge amount of code tidying up in order to make new ideas easy to drop in and try.
Matt Platts, March 31st 2016 (final edits March 26th 2017).