Layer cake

layers

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.

 

Advertisements

Back from a long break

I haven’t posted anything for a while because I was refactoring my code and working on other projects. But I’m back and I will soon post some new videos and stuff! Gotta fill this Coco3’s 512kb with loads of compiled sprites and tiles and animations, until it goes…

kaboom.png

Kim drank some really strong coffee

I have benchmarked the sprite compiler I’ve recently programmed. For 10 000 renderings of a 16×16 sprite with some transparency (I’d say it’s a “reference” case), I’ve got the following results:
– Rendering using a transparency mask table: 44 seconds
– Rendering using a compiled sprite: 10 seconds
That’s pretty impressive. I’ll be able to free up lots of CPU cycles now. And I’m working on even faster compiled sprites!

Dither me up, Scotty!

To make a great game you need great tools, and I have been working on a dithering algorithm for quite a while. I have just programmed an image converter that attempts to select the best 16-colors palette for an image. It uses both solid and checkered colors, with constraints on which colors can be paired.

How does it work? I expand the 64-colors EGA palette to (64×65)/2=2080 virtual colors. Each virtual color is represented either by a solid color or by 2 checkered colors. The resolution doesn’t change (it is not divided by two), it’s like painting a picture using a magic checkered paintbrush. ThenI count the virtual colors in the image, build histograms, and keep the 16 most useful “pure EGA” colors (a checkered color has 0.5 of each color, a solid color has 1.0). It works surprisingly well. The palette selection, checkerboard dithering and RLE encoding worked without a hitch.

These tools will be useful to build background images and splash/menu screens.

So the image below is a pure 320×225 16-colors RGB image displayed on my own Coco3. No video tricks.

17796829_10155295154317922_1811612464419605942_n

Die Kim Die!

Game update! With some help from Simon, I have started making the code faster and better. Lots of functions were “naive implementations” that I had programmed to optimize later. I am a strong believer in “quick and dirty” code that, if programmed smartly, can then be improved. Now we have many enemies on the screen and, even though the animation is still a bit clunky, there isn’t any massive slowdown anymore. This test screen has 4 moving enemies, 3 animated items, and 3 blocks which can be triggered. And there’s still room for more. Collision detection with enemies and lethal blocks is now real; watch the video to see Kim get roasted over a flame. No dying animation yet, just the screen border turning to red. Oh, and there’s the new enemy, the Slimy Snail. Enjoy!

Beware the Baffle Bees!

So after adding the first enemies, I have reached the point where I need to start optimizing my code, both globally and locally. Hopefully Simon Jonassen is helping me with that. Here’s a short video showing the slowdown with 3 medium-sized enemies and 3 animated items. These enemies are called the Baffle Bees and will eventually be a bit more animated and aggressive. They currently use a “coordinates box” and bounce inside its limits.