ad astra per scientiam. Random thoughts and stuff.

Making Skyline: Art that doesn’t suck

Skyline is targeting, primarily, the Atari 2600, but also other “old” systems. And that presents a sort of challenge to us: art that doesn’t suck.

Other folks have great write-ups at various levels of technical sophistication of the hardware of each system, but let’s start with this: the goal is to look, at least no worse than average, and at best, as good as possible, on each platform.

That means for n platforms, we’ll want to remaster the artwork for each, to take advantage of its strengths (or hide its weaknesses). Drawing a sprite that looks “all right” on the Atari’s 8-pixel-wide Player sprites, with one unique color at a time, versus the Commodore 64’s 12×21 sprites with 1 unique and 2 shared colors, versus the NES’s 8×8 sprites with 3 colors from one of a few shared palettes, these each have unique challenges.

But my goal is to have one game design, that plays on each platform. I don’t want the designer to have to worry about all those different art options. What to do?

Well, in the simplest case, we design for a “meta” system, and then compile down to all the specific architectures. The game’s design itself — maps, dialogue, scripts — is the same; how they appear (and, how the controls work, music sounds, &c) are determined by the platform it’s running on.

That model, in this case, is the “tiles and sprites” model. The background maps are immutable and broken down into a set of (very few) large world maps.

For Skyline, there will be four worlds, each with its own library of tiles. For each world, there are four possible levels — as in, roughly, altitudes: Below, Ground, Above, and Special. These levels can draw from the relatively rich library of tiles for that world, freely, and are imagined as very large, free-scrolling spaces. The designer has major latitude, at this point.

Now, that lets us use the very nice Tiled app to draw the maps. Cool. We can sketch out some high-quality proxy art, draw the maps, lay out the game in pretty nice detail, and pan around to see what it would look like in some ideal world.

In fact, I might even want to hook up Hath to it, to let you actually playtest (or play?) the game under Linux …

What then?

Well, we take that big, beautiful tile sheet, and we copy it for each video architecture; say, the Atari 2600’s TIA. We scale it down, set it up fro the right palette, and generally do our best to present the best possible interpretation of each tile.

That part is basically manual. So, for each platform, you can “tweak” the artwork. You’ll still want to keep in mind various platform-related quirks, but you can work in the nice, modern environment of Gimp to design it.

After those are “done” (to whatever degree), the build process takes over. We’re taking advantage of the fact that Gimp happens to host a beautiful Lisp environment called “Script-Fu,” which is actually the Guile language — the GNU version of Scheme. Using that Script-Fu, the Make process for each version of the game can “call” Gimp to export its native XCF files into useful, flat PNG files that are easier for the compiler tools to read.

The compiler tool has to know about the various platforms’ constraints, and it can issue warnings about possible-problems (eg: an exact color wasn’t available in the palette, so the nearest-matching color was used instead) or signal actual errors.

Those actual errors, of course (this is Common Lisp, after all) don’t stop things dead. Restarts are established around the compilation process, and you can choose how to cure the problem. For example, you might open the affected artwork in Gimp and edit it, then ask the tool to reconsider it and see if you’ve fixed the problem.

Those restarts aren’t (today) very sophisticated, but they do work. In future, better error conditions being signalled from within the compiler tool will enable more fine-grained restarts to be presented, and eliminate some of the work by the user. Currently, the user sees a boatload of possible restarts and has to manually evaluate which one(s) are actually useful in the current context.