How do you create rich animations over a background, on a computer that doesn’t offer much in terms of hardware video features? First, let’s have a look at what the Coco3 offers you that not all 8-bit computers do:
- 19-bits video address relocation: This means that the image that is displayed by the video chip (the viewport) can be located anywhere within the 512kb memory map, including ranges that are not loaded in CPU RAM (the first 64kb) by the MMU. In fact, the video address is completely independent from any MMU mapping. However, in order to actually modify parts of the viewport, you have to load a 8kb page into CPU RAM. Other 8-bit computers such as the C64 don’t give you that kind of flexibility. The Coco3 lets you really display whatever bytes are at any location in RAM.
- Page flipping: As a result of the total flexibility in video address relocation, you can flip between 2 viewports, thus modifying one instance while the other is displayed. This allows for flicker-less animation.
- VSYNC interrupt: You can flip your viewport at the exact time the computer has finished refreshing the screen. With proper timing in the programming, this means that you avoid tearing, a type of flicker that occurs when you flip the viewport while the screen is being refreshed.
- Hardware horizontal scrolling: The Coco3 allows users to define a 256 bytes wide virtual screen (512 pixels when using a 16-colors mode), and seamlessly scroll the viewport across this virtual area. The word “seamlessly” is important here. The virtual screen truly wraps around, which is amazing when you want to create horizontal scrollers like Super Mario Bros. Which is not the case here. I’m not programming that kind of game.
My game is not using ALL of these hardware features. For instance, I don’t need any horizontal or vertical scrolling… except maybe when I will implement cool transitions between levels! Most of my optimizations are implemented as software. With a 6809 running at 1.7MHz, you really have to be careful what you’re asking the CPU to do. The Coco3 lacks the following features:
- No hardware sprites
- No specialized graphics acceleration or math co-processor
- No DMA or block copy (some CPUs allow you to copy or move a large number of bytes in RAM in just a few instructions through a “pipeline”, but a stock 6809 doesn’t offer this feature)
- No built-in collision detection
- No transparency management or hardware blitter (a blitter is a rendering filter)
- No tileset handling hardware
- No sounds or music (except for the 6-bit DAC, which requires extensive CPU activity)
One of the fundamental features of any video game is how graphical items stack up on the screen. Some simpler games use a black or single-color background, so moving a sprite or character around only requires to erase its edge to avoid leaving a trail.
Try to animate characters over a non-trivial but static background and you end up having two layers:
- The background layer, which never changes (we say it is static).
- The characters layer, where your game character, friends, foes and animated stuff exist. They must kindly restore the background when they move or are animated.
Let’s make it a bit more complex now. What if we want to draw some scenery over the background. Scenery requires some transparency, so that drawing a lamppost or a table over the background doesn’t require you to pre-merge the item with the background tiles. What if we want to use a bitmap for the background? What if we want to move the background (parallax scrolling)? Some decisions need to be made, here.
- Option 1: The scenery is completely static. You can draw the scenery over the background and it becomes part of it (their pixels are merged), for the duration of the level.
- Option 2: The scenery has some animation in it. You can merge the static parts (bricks, stairs and such), but the animated items will become part of your characters rendering routine.
- Option 3: Parallax scrolling. Your scenery will move independently from your background, so you can’t merge them. You need to redraw scenery elements as if they were characters in your game. It requires lots of CPU power, a graphics co-processor and/or high speed processing (which a single 6809 doesn’t have), so you’d better hire an optimization guru.
For Kaboomerang Kim, the solution I have chosen to use mostly Option 2. Some of the scenery is merged with the background, while the animated items are rendering prior to rendering characters. Animated items don’t require super fast animation (since they don’t move around) so I update them in a round-robin fashion: one item per redraw cycle. This saves lots of CPU. And I might implement Option 3 for super cool background animation effects, but this will require quad-buffering (holy cow!), an arcane concept which I will cover in another post!
Finally, some games use a foreground layer, which is populated with scenery that goes in front of the character, enemies and projectiles. This foreground layer can be implemented using many techniques:
- Redraw at every cycle: If the number of foreground objects is not too high (and especially if the objects are rendered as opaque blocks), then you can redraw them all at the end of every cycle, after redrawing the characters layer. What’s nice with foreground objects is that you just dump them on the screen. The whole point is to cover whatever is behind them.
- Viewport-wide mask: Another approach is to pre-calculate a transparency mask that will indicate where to punch holes into the characters you are redrawing. This method uses a lot of RAM (it doubles the amount of RAM used to store the viewport) and adds steps when drawing a character. But if you have complex foreground scenery (for example, if your game’s setting is in a medieval castle or in a jungle), then it might be worth the effort.
- Selective refreshing: When drawing your characters, you can mark positions in the scenery grid that are “dirty”, which means they have to be redrawn. You can also put the coordinates that require foreground redrawing in a queue. So you don’t need to redraw the whole level, just the grid locations where your characters currently are.
I am currently working on the Foreground layer so I have not settled for one of these 3 options yet. They’re all interesting, though the first option (redraw at every cycle) imposes limitations that might prove to be unacceptable if I want to design varied and interesting levels.
So this is how layers stack up in Kaboomerang Kim. Programming a video game is not as simple as it might initially seem, especially on a 8-bit computer with limited graphical capabilities. In the end, what matters is that it looks good, is animated nicely, and is fun to play.