Sega Genesis Programming Part 26: A less than optimal gradient background fill

 

Step #1 to starting is starting

I'd like to write a 4th, and maybe penultimate, Sega Genesis demo. It's small and something I want to build to test out a slightly different style than what I've done so far. The biggest barrier to starting this project has simply been starting it. My first two demos were built iteratively. Try something and see if it works, fix it or scrap it. This approach doesn't work so well if you're trying to add a garage to your house, but it's fine for software.

I've probably had a job title that included "engineer" in it at some point. That is such a misnomer. If an engineer designs a boat dock that catches on fire the first time someone tries to dock a boat, they'd be out of work. On the other hand, an engineer couldn't deal with a request like "Yeah, I know the boat dock is supposed to go-live in three days but what the customer really wanted all along was a golf course. If you could go ahead and change it that would be great, it still needs to be live in three days by the way."

These are not the same job. I could sit here and deliberately plot out what demo I want to build, or I could start writing stuff and see what I end up with.

First things first, after all this time I finally tried running Gens KMod running in Crossover:

Gens KMod running in Crossover

Why did I not think of this before? It's so obvious now and would have saved me all kinds of time.

In my next demo, I'd like to create a gradient background fill. That doesn't sound very difficult, and it isn't in theory. In practice, being limited to a 9-bit RGB palette introduces some challenges.

I'll start with what an 8-color gradient layout looks like, or what I think it looks like:

8 color gradient layout

Here's a mock-up:

8-color gradient example

I think that looks bad.

Here's what a 16-color gradient layout might look like:

16-color gradient layout

And another mock-up:

16-color gradient example

I think that also looks bad.

Let's see how these actually look when incorporated into my project template:

8-color gradient demo

Yes, that looks bad.

16-color gradient demo

That looks even worse.

Before trying other layouts, can I make this look OK with only 4 colors?

4-color gradient demo

That's an improvement. Keep in mind, with the build tools I created, it only takes a couple seconds to change this background image and re-compile. Some day I should do a better job explaining how that works because I have built a pretty functional system for creating "no code" Sega Genesis demos.

Anyway, 4 colors looks better. A slight tweak to the layout might help more:

4 color gradient demo - another style

That's a decent improvement... how about some darker colors?

4 color gradient demo - more subtle

OK. So this is shifting from #0000C0 -> #2000C0 -> #2020C0 -> #0020C0. That creates a noticeable jump in color vs. a smooth transition. I'm using HTML RGB values here for readability.

Something more subtle is going from #002000 -> #002020 -> #002040 -> #002060.

4 color gradient demo - darker

Now I do the thing I should have started with - reviewing how better developers & artists handle this:

Skyline examples

The second from the bottom is the subtle look I'm trying to achieve. Bonus points if you can name all five of these games.

Rather than rip one of these off, I used a sky pattern from the Pixel Art Infinite Runner Pack. I bought it in a bundle a while ago. Using that pattern, the results are what I wanted:

4 color gradient demo - looking much better

Here's an enlarged side-by-side comparison that makes the improvement more obvious. Instead of straight edges, it looks like a smooth transition:

Comparison of two styles

Here are three other examples, sticking with close colors produces the best results:

Three examples

So this looks OK, but is horribly inefficient with regards to storage. My esoteric tooling is very good at taking an image, converting it to a set of unique 8x8 tiles, and creating a pattern to draw the image. It starts with a base pattern like this:

The base pattern

This process can also extract the palette used for the base pattern:

The base palette

It will then produce a set of unique tiles like these (enlarged of course):

Unique tiles in the base pattern

When the scene is actually drawn in the game, the palette is overridden. For example, here's the palette for the last image in the previous set of three:

The overlay palette

This all goes into a scene definition (JSON) that the tooling converts to smaller binary data:


{
	"name":"SceneTitleDebug",
	"id":"SCENE_ID_TITLE_DEBUG",
	"destinationFilePath":"src/scenes/TitleDebug.X68",
	"tilesetNames":[
		"TransparentTile",
		"Gradient04Tiles"
	],
	"paletteNames":[
		"PaletteTitleBG",
		"PaletteText00",
		"PaletteText01",
		"PaletteText02"
	],
	"scenery":[
		{
			"patternName":"PatternGradient04BGHorizontal",
			"comment":"background",
			"tilesetIndex":1,
			"tilesetOffset":0,
			"highPriority":false,
			"paletteNumber":0,
			"repeat":0,
			"layer":"VDP_VRAM_WRITE_B",
			"row":"00000000",
			"column":"00000000"
		}
	],
[...]

The good part of this whole process is the number of tiles stored in the ROM in minimized to only the unique ones. This is inefficient though because PatternGradient04BGHorizontal has the entire 320x224 pattern stored.


PatternGradient04BGHorizontal:
	dc.w	$1B	; 28 rows
	dc.w	$27	; 40 columns
	dc.w	$0
	dc.w	$0
[... and so for a total of 28x40 words ...]

That's bad. I'll make it a bit smaller though. A small savings is removing the first tile from the unique tileset since it is simply the background color.

Updated unique tiles

Now the layout can be reconsidered:

The new layout

Then I need a new generic pattern to draw the same tile across a row. Somehow I've never needed this before, and it highlights a design problem (I created) that I will explain soon:


PatternSingleTileFullRow:
	dc.w	$0000	; 1 row
	dc.w	$0027	; 40 columns
	dc.w	$00,$00,$00,$00,$00,$00,$00,$00,$00,$00
	dc.w	$00,$00,$00,$00,$00,$00,$00,$00,$00,$00
	dc.w	$00,$00,$00,$00,$00,$00,$00,$00,$00,$00
	dc.w	$00,$00,$00,$00,$00,$00,$00,$00,$00,$00

This is still taking up more space than I want. This is because when I implemented repeat for pattern drawing, I did it vertically. It fit how I was doing things at the time. Ideally, I would have something that is just the tile ID and number of times to repeat, oh and which direction to repeat (horizontal vs vertical). I will save this for another time.

This means the JSON that defines the scene is a lot larger, but that doesn't matter. It has a series of entries like:


"scenery":[
	{
		"patternName":"PatternSingleTileFullRow",
		"comment":"background - row 0 - tile 0 x40",
		"tilesetIndex":1,
		"tilesetOffset":0,
		"highPriority":false,
		"paletteNumber":0,
		"repeat":0,
		"layer":"VDP_VRAM_WRITE_B",
		"row":"00800000",
		"column":"00000000"
	},
	{
		"patternName":"PatternSingleTileFullRow",
		"comment":"background - row 1 - tile 1 x40",
		"tilesetIndex":1,
		"tilesetOffset":1,
		"highPriority":false,
		"paletteNumber":0,
		"repeat":0,
		"layer":"VDP_VRAM_WRITE_B",
		"row":"01000000",
		"column":"00000000"
	},
[...]
	{
		"patternName":"PatternSingleTileFullRow",
		"comment":"background - row E - tile C x40",
		"tilesetIndex":1,
		"tilesetOffset":12,
		"highPriority":false,
		"paletteNumber":0,
		"repeat":0,
		"layer":"VDP_VRAM_WRITE_B",
		"row":"07800000",
		"column":"00000000"
	}	

This all gets compiled down to a small amount of byte code. It's still not super-efficient, but it works.

I know this wasn't really much of a tutorial. This was really just a long way to motivate myself to start working on a new demo.


 

Tags: 68K programming Retro programming Game development Sega Genesis


Related