Retro game engine project

commodorejohn

Shameless recidivist
Joined
Apr 2, 2010
Posts
1,607
Country
United States
Region
Minnesota
Hey, we've finally got a programming forum! I've been waiting for this, so now's a good time to do a write-up on my current programming project: a game engine for 16-bit computers. I've been scheming this up for some time now, but it's only recently that I've made real progress in nailing down the design. I plan to use this thread as a sort of running development journal as I move from design to implementation to the development of a test game. (I will be doing a full-fledged game for this eventually, but that'll have its own thread.)

So, the project. As I said, it's a generalized 2D game engine designed to run on fairly low-end 16-bit computers. I have two specific targets in mind: 8086 PCs and OCS Amigas. (I may also do other ports, but these are lower-priority.) I will also put together a portable, SDL-based version of the engine that can use the audio and video engines from any machine-specific version, for the lazy schmucks who just want to run it from XP or whatever.

General goals

Amiga baseline - One of my key goals here is for the other versions of the engine to be either cut-down or enhanced versions of the Amiga engine, rather than the Amiga version being a cut-down or marginally-enhanced port of something else, as was too often the case back in the day.

Low requirements - The engine should be designed to keep the requirements for playable operation as low as possible without compromising the game experience. In other words, it should impose as little overhead in memory and CPU usage as possible.

Uniformity of game experience - While it's not really possible for the game to be identical across all target systems, and while it should be possible for the engine to make the game to look and sound a little nicer on more capable systems, the game should, overall, play the same on all targets.

Distinctive tailoring of graphics and sound per target - Though it seems like a contradiction of the above point, the game should take specific advantage of whatever hardware it's running on. The game should play the same across all systems, but it should look and sound a little different on a VGA PC with an Adlib than it does on an Amiga, for example.

Flexibility and ease of use - The engine should be flexible enough to be used for more than just one game; it should provide general capabilities that game-specific scripts can make use of in whatever way works best for them.

OS friendliness - As someone who spent a number of my formative computing years trying to get games to run on a Windows 95 machine, and consquently, formatting a lot of DOS boot disks, I appreciate it immensely when a game works alongside the host OS rather than hijacking it. While it may not be feasible on some targets and may not matter on others, as a general rule the engine should do its work through the facilities of the operating system whenever possible.

Potential targets

These are the systems that I'm thinking of running the engine on. As stated above, only the Amiga and the PC are guaranteed targets, but any of these may potentially see a port:
  • OCS Amiga
    The standard equipment the engine is designed around. 7.16MHz, 512KB of RAM, 320x200 resolution, 64 colors out of 4096, and four channels of sampled sound (not to mention some very handy hardware and firmware for making use of all this.)
  • 8086 PC
    Something of a wildcard. 320x200 resolution is still guaranteed, but the rest of the hardware can vary quite a bit. It can have 16 fixed colors, 16 colors out of a palette of 64, or 256 colors out of a palette of 256,000. It can have only one channel of simple square-wave output, nine channels of two-operator FM synthesis, nine channels of four-operator FM synthesis, and/or one channel of sampled sound. The CPU speed can vary from 4.77MHz to multiple gigaherz. And the amount of memory can be anywhere from 64KB to 640KB (though the latter is definitely more common, and it's unlikely that any non-test games will be running in less than 128KB.) Needless to say, game experience may vary significantly.
  • Tandy PC
    Basically like any other PC, but the video hardware is always at 320x200 with 16 fixed colors, and there is a sound chip that produces three channels of square-wave output plus one channel of white noise. The CPU speed is almost always within 4-8MHz, but I'm not sure whether that will be enough, since the video hardware provides none of the assistance that the EGA/VGA cards provide.
  • 68k color Macintosh
    A lot less hardware-focused than any of the other targets; just about everything is done in software, so a Mac version will probably require more CPU oomph to run acceptably. Not too different from the Amiga otherwise; a 68000 running at 8+ MHz, 512KB+ of RAM, either 16 or 256 colors out of a palette of 4096 or more, and 320x200 possible either by letterboxing (on a 512x384 screen) or pixel-doubling (on a 640x480 screen.) Sound can be one channel of sampled sound (Sound Manager 1-2) or an indefinite number of channels (Sound Manager 3+.)
  • Sega Genesis
    An interesting piece of hardware. The 7.67MHz 68000 means a lot of Amiga code can be re-used, and the tile-and-sprite-based video hardware fits nicely with the general design of the graphics engine, but while the video chip does provide 64 colors (out of a palette of 512,) they're divided into four separate 16-color palettes, and the system has only 64KB of RAM. This isn't such a huge problem, since most game resources will work fine off a ROM cartridge, but it does limit the number of script objects that can be active at once. Features a Yamaha OPM chip with six channels of 4-operator FM synthesis and a Tandy-style PSG, plus a Z80 to help drive them.
  • Sega CD
    The Genesis, unfettered. Adds 512KB of RAM, a second 68000 running at 12MHz, some kind of graphics-accelerator chip (not real clear on its capabilities, I need to go over the documentation again,) and eight channels of sampled sound, plus that whole CD thing (read: 650MB of storage, also usable for Redbook audio.) The documentation is cryptic, but it's definitely powerful.

Graphics

The engine is geared towards moving variable-sized graphic objects around in tile-based maps, for the most part (there will be some support for both bitmapped and vector-based backgrounds, but these are more intended for menus and cutscenes than for gameplay proper.) Support for a fixed-position, fixed-size status window is also provided, though it can be turned off and status information displayed via graphic objects, at the discretion of the game designer.

Since the goal is for the Amiga version to be the standard, the engine will generally be arranged around OCS Amiga capabilities. Specifically, the default, ideal graphics mode will be 320x200 with 32 colors and a 1-bit "shadow mask" to provide simple lighting effects - in other words, Extra Half-Bright mode.

Systems with 256 colors (VGA PCs, high-color Macs) can be enhanced with a more detailed shadow mask (a 3-bit shadow mask, specifically.) Systems with only 16 colors, on the other hand, will use a customized set of 16-color equivalents to the normal game graphics. These should generally be arranged around the common RGBI palette, a.k.a. the EGA/Tandy color set, but support will be provided for tweaking these colors on systems that support it (EGA PCs with EGA monitors, 16-color Macs, etc.) 16-color mode will probably ignore the shadow mask.

Sound

Sound is a little different, since the Amiga hardware is not quite ideal; sampled instruments are nice, but having only four channels can be a pain. Therefore, the Amiga version will use specific 4-channel versions of the game music, which can be tweaked to fit as best as possible (i.e. combining chords onto a single channel,) while systems with higher polyphony will use a multi-channel version of the soundtrack (to allow for the possibility of a Sega port, I'm thinking six channels, but we'll see.)

The Amiga 4-channel version of a song will just use samples, but the multi-channel version will use notional instruments that are translated to the appropriate sounds for the target hardware. In other words, the "guitar" instrument for a song is loaded from the game's instrument bank in the appropriate format depending on whether the music is played on an OPL2/OPL3 sound card, the Sega's OPM chip, the Macintosh Sound Manager, or whatever.

Sound effects can come in both sampled format and PC-speaker type format (that is, a series of pitch values for a square-wave output.) Most versions, when possible, will use the sampled version, but some targets may not have sampled channels available, and will use the PC-speaker version instead. Sound-effect polyphony and priority are entirely dependent on the target system.

Of particular note in the audio department is the Tandy. It has four channels in addition to the PC speaker, but unfortunately, they produce only three square waves and a white noise channel, not samples. I'm undecided as to whether to provide for another custom-fit version of the soundtrack, or just feature four-voice polyphony for PC-speaker format sound effects. The Sega Genesis version will do that for sound effects, since it has a basically-identical sound chip included, but also has a six-channel Yamaha FM chip for the music.

Game logic

The actual game part of the game is geared towards multiple independent script objects all running simultaneously. The basic idea is that each object in the game (the player, the enemies, projectiles, etc.) is represented by a script object, which is comprised of the script code (which can be shared across identical objects) plus an area of RAM for script data (which is unique to that individual game object.) There is also a shared area of RAM for global game data. Much of the non-game logic (menus, cutscenes, etc.) is handled by script objects as well; in fact, the engine as a whole is basically a giant implementation and management layer for the script API. The scripting language is essentially a sort of ersatz Forth, which I'm using because it's memory-efficient and the easiest thing in the world to write a compiler for (as well as being ridiculously easy to implement on the 68000.)

The script compiler will support a simple form of inheritance. Each script can have a parent script (however, the parent script cannot have a parent script,) and can use all of the code defined by that parent script. In addition, every script can use code defined at a global level, which will include the API for controlling the game engine as well as some basic support-library functions (accelerated math/trig functions, etc.)

Script objects can pass messages to each other by pushing arguments onto the recipient's data stack and calling the appropriate subroutine in the script. This is done through the engine, which first checks to make sure that the target object is allowing messages from the sender. The engine can also pass messages to a script, which is used for things like collision detection.

Implementation

The way I see it, the implementation of the game engine is divided into six major components, some of which have additional sub-components:

Memory manager
Depending on the platform, this may be a simple wrapper for OS APIs, or an actual (if simplistic) memory manager. Its job is simply to handle allocation, deallocation, and relocation/garbage-collection of objects, resources, and work memory as simply and quickly as possible.

Resource manager
Keeps track of what game resources are loaded into memory; relies on the memory manager to keep track of where. Handles loading of resources from disk and saving of game state. (This works slightly differently on ROM-based targets; only game/object data is actually loaded or stored, and all other resources are kept in ROM.)

Object manager
Handles spawning and killing of game objects, message-passing between objects and other object-object interactions (e.g. collision detection,) and decides object execution order.

Sub-components:
  • Script manager
    Executes object scripts as directed by the object manager. (Generally not an interpreter, as the scripts will be compiled to native code wherever possible, but that probably won't work on the Windows XP version, and may not even work on a Mac.)
  • Object API
    Provides an interface between the other engine components and game objects, as well as handling message-passing between objects, and providing a library of accelerated operations for scripts to use (mathematical functions, collision detection, etc.)
Input manager
Handles input from the keyboard, mouse, and/or joystick/gamepad, translating key presses, movement, and button presses into generalized events that can be handled by scripts.

Graphics engine
Handles drawing operations. It has has five essential functions: drawing a scrolling window comprised of 16x16 tiles, drawing graphic objects into that window, providing simple vertical prioritizing for graphic objects to allow for pseudo-3D game layout, overlaying a static window onto the screen, and drawing text and graphic objects into the static window. On hardware with higher color depths, it also provides shadow-mask capability for lighting effects.

Sub-components:
  • Vector rasterizer
    Used for cutscenes, this component draws shapes from solid-color polygons in place of a tile-based background. Graphic objects and the static window may be overlaid onto this background just as with the tile-based backgrounds.
  • Graphics loader
    Decides which representation of game graphics to load from disk based on what video hardware is present. Converts between graphics formats when necessary (though as much of the conversion as possible should be done at production time.)
Sound engine
Handles conversion of game sounds from note/instrument information provided by its sub-components into hardware-appropriate sound data.

Sub-components:
  • Music player
    Handles playback of game music, outputting note/instrument information for each channel to the sound engine.
  • Sound-effect player
    Arbitrates between sounds effects of higher and lower priority when sound-effect channels are limited, and outputs note/instrument information to the sound engine.
  • Instrument loader
    Loads the appropriate instrument resources for the sound hardware.

Conclusion

And that's basically it for now. I'll be posting updates as I make progress on more detailed design goals, individual implementations, and development tools.
 
This sounds like a daunting task! Very impressive though. I'm really interested in knowing how the engine presents itself to a developer. Is it going to be a really high-level language (like Shoot-em-up Construction Kit) or a collection of C/C++ library functions?

Crossing the boundry between 8086 and 68k is enough of a headache alone, never mind catering for widely differing multimedia capabilities. :ninja:
 
I thought it sounded like a big job, too, until I started breaking it down into individual components. Still looks like it'll be work, but it's a lot more manageable when you think of the individual pieces.

As far as development goes, it's like I said above: the engine is basically a giant implementation/support layer for the API of a Forth-like scripting language. C/C++ may be involved in some of the ports to other platforms (at the moment, I'm doing it in assembler, just because,) but not in game development.
 
I see. How are logistical things like the limitations of each platform going to be handled, though? The Amiga is fairly adept at scrolling platformer games, but parallax support gets tricky beyond 8-colour dual-playfield, whereas a Genesis can handle a good 3 or 4 layers without breaking a sweat.

How are you going to stop devs targeting only the lowest common denominator within the supported set of platforms? Or is that not part of the goal?
 
Eh, if you give people a set of tools, it's basically inevitable that a lot of them will use them sloppily. I'm not the kind of person that tries to force other people to do things the Right Way; the only real goal here is for me to be able to adapt the game I'm planning to multiple platforms with less effort ;P

As stated above, the Amiga is kind of the "baseline" system that defines what the engine is designed to do, so things like multi-layer parallax aren't really going to be implemented, at least not by me. All of the potential target systems are capable, at some not-uncommon level of CPU horsepower, of doing a scrolling playfield with a static window on the bottom of the screen and moving arbitrarily-sized graphic objects around in it, so it shouldn't be a huge problem (the only system I think might be an issue is the Tandy, but we'll see - after all, OverKill runs at a playable framerate on it.) Any extra hardware features (the Genesis's multiple layers, for instance) are basically going to be used to take a load off the CPU by taking over parts of the engine's existing capabilities.
 
I would ditch the 8086 and tandy modes and use something that would be usable to 386/486 machines out there... 8086/tandy are awesome and nostalgic, but there are very few pc's of that config that are actually being used... not to mention both need their special cga/ega monitors ...
 
Feh, everybody does 386 development. I might as well make the Amiga version requrie AGA and a 68020 while I'm at it :dry: Besides, it's not like 8086 software won't run on a 386 or anything. My goal here is to make something for the systems that are consistently neglected by the homebrew community, not to join in the neglect. Besides, EGA modes will work just fine on a VGA, and will probably be faster, which is a nice option for PCs that don't have enough oomph to do VGA-mode graphics at full speed, and anybody with a Tandy is almost certainly going to have a compatible monitor.
 
alright I see you made up your mind and feh while I am at it I should mention that there is no need to start being ironic when someone posts his opinion trying to help with suggestions.
good luck with your project.
 
A lot of what you're proposing to create is already taken care of by low level game API's like Allegro. You might want to take a look at that before you start writing graphics, sound and other custom libraries.
 
A lot of what you're proposing to create is already taken care of by low level game API's like Allegro. You might want to take a look at that before you start writing graphics, sound and other custom libraries.
Again, there's nothing wrong with Allegro, but it's specific to more modern platforms (Windows, 32-bit DOS, and various Unices.) None of these platforms are hurting for homebrew games; Windows and Unix are where it's at now, and protected-mode DOS was the platform of choice before then. Old-school Amiga and real-mode DOS, on the other hand, see very little development; even when someone does write a non-PPC Amiga program, it typically requires a 68020+ and SDL, instead of taking advantage of or being tailored for what the computer actually provides. This is something I've been miffed about before, and I figure that it'd be better to address the problem than just to complain about it.
 
I think that you should target an A500 with 512KB expansion because it's most common A500 configuration and 512KB are often too little to keep lots of data.
 
That's my goal. I would like it to run on an unexpanded machine if possible, but 1MB is a definite cap on the minimum requirements.
 
A very interesting project. Although I have nothing really beneficial to add, I do have four of the target platforms (OCS - A500/ECS - A600, 486 PC, 68K Mac - Performa 637CD, and Sega Genesis) and would be willing to test the engines in a real world environment on hardware other than your test beds. I always think things like this are very neat and want to help, even though I have a very limited ability to help. Even if I can offer nothing that you need, I hope this project goes far. Keep up the good work.:)
 
Thanks :) I haven't done much beyond the initial planning at the moment, as I'm trying to reserve my time for this year's MSXdev, but I do hope to make significant progress afterward.
 
Back
Top Bottom