See also: part 1, part 2, part 3, part 4, part 5, part 6, part 7, part 8, part 9 and part 10.
NOTE: I tend to sit down and write up a bunch of things at one time, then publish them over the next week. This article was originally written on 2/17, and often by the time they are published I have had time to think about things and reconsider. Much of what you will read in the next two articles is being replaced by a better approach, but I still want to share the thought process. Maybe we will end up in the same place! Here we go… (Also, I created a Facebook page for Sub-Etha as well. It will update every time I post a new article.)
I believe I have solved the dot dilemma, and it looks like it might be easier than I expected. First, a recap on what this dilemma is.
The Pac-Man game considers a collision to be when the center of a sprite crosses over the center of another object. This is how collisions with the ghosts, dots, energizer pellets and fruits are handled. Since the center is used for this, it is possible for two sprites to touch and even overlap slightly without anything happening. This is why it’s possible to brush up against ghosts in the arcade game without losing a life. It is also why if Pac-Man approaches a dot halfway, then turns around without getting to the center point, the dot is not eaten.
As previously discussed, a simple way to keep track of the dots would be to use variables to track the on/off status of each dot. 240 dots could be represented by the bits of some bytes (8 bits per byte) and would take up 30 bytes of RAM. Since I only have around 200 bytes of RAM available, using 15% of available RAM for this may not be practical. Plus, it would also require some sort of table that links each bit to which grid tile on the screen the dot is in. While this may be doable, I have decided to try to come up with a different approach.
Instead of tracking each dot, I will be detecting them as each sprite moves around. If a ghost is moving right and is about to cover up a dot, it will remember that and, as the ghost passes over where the dot is, it will redraw the dot behind it. Some considerable has to be taken for when a sprite changes directions while covering a dot, and I think I have this all worked out.
For Pac-Man, the story is the same, except instead of redrawing the dot, it will score points when the Pac-Man sprite is over the center of where the dot was.
Since objects are constantly being drawn, erased, and redrawn in new locations as they move around the screen, some care must be taken with the order that these things happen to ensure a dot is properly detected. I have a few ideas on how this might be handled, from one that will “most certainly” work but take more CPU time and code, to one that will “quite possibly” work and leave more CPU time free for other game tasks.
Brute force programming is not something I generally do beyond initial tests to see if something will work.
Once again, let’s revisit our screen. The screen is made up of a grid of tiles. Each tile is 3×3 in size. The character sprites are 5×5. A dot is in the center of a tile, so every three pixels there can be a dot. This means a 5×5 sprite can actually be covering two dots.
If a 5×5 sprite begins in the center of a 3×3 tile, it will extend one pixel past each edge of the 3×3 tile. There are four cross paths in the maze where there could be dot touching each of the four sides of a sprite, but we really only need to track the ones in front of the character and remember to draw it back once the character is past it, or count it for the score once the sprite is dead center over it.
Because of how the math works out, any time the sprite is at a tile offset of 0 (in the center), we should be able to check the tile in the direction we are going to see if there is a dot there. If there is, we can increment a counter variable that will indicate how many dots are under us. For this game, this counter will never be higher than 2, but if we were animating a long snake through a maze, we could use the same system to count being on top of many more dots.
Also at the 0 offsets, we check to see if we need to redraw a dot behind us that we just passed over. We do this by checking the counter to see if it is greater than 0. If it is, we draw a dot behind us and decrement the counter.
If we are moving over a dot and have just incremented the counter, the next frame will be to draw the sprite on the current tile with an offset of 1 in that direction (moving one pixel to the right, for example, and now covering the dot). As the character continues moving right, it moves to the next tile, with an offset of -1. When it moves right one more pixel, the offset is 0 and once again we check to see if there is another dot in front, and if a dot needs to be redrawn behind.
This seems like it will work quite easily, but we also have to take in to consideration what might happen if the sprite changes direction. If it backs up, the counter is still set (>0 = we are covering a dot) and I expect the same code may work, since we reverse back and expose where a dot used to be, and just draw one. Some test code is needed to see if it can really be this simple…
Since all turns are done at offsets of 0 in the middle of a tile, there is never a situation where the two dots being covered would be around a corner, but even if there was, it seems like this system would still work.
Some test code will need to be written.