Sound, Graphics, Installation, and Documentation
Moving Your Game to Windows, Part III
By Peter Donnelly
Abstract
This third, and last, in my series of articles on converting MS-DOS® games
to the Microsoft Win32® environment introduces digitized sound effects, MIDI
music, TrueType text, and animation with DirectDraw®. I’ll also touch on the
subjects of installation and online documentation.
It is assumed that you have a general understanding of game design, along
with MS-DOS programming skills with C or C++, and are familiar with the basics
of Windows-based programming.
Moby Dick—The Chase Continues
In Parts I and II of this series I introduced a simple MS-DOS game called
Moby Dick and took it through the first stages of development for the Win32
environment. For this article, I’ve added more features to the version
for the Windows platform: stereo sound effects, MIDI music, TrueType fonts, and
DirectDraw graphics with transparent sprites. To keep the source files from
getting too big, I’ve stripped out some of the code that was meant to
illustrate specific issues covered in the first two articles.
As you know if you have been reading this series, Moby Dick is not intended
as a serious game or even as an example of sophisticated game programming. It
is merely a vehicle for some examples of implementing specific features. The
latest version is more sophisticated than its predecessors, but it lacks some
obvious refinements like in-between animation (that is, smooth movement of
sprites).
The interface hasn’t changed from version 2. If you have a joystick attached
to your computer, it is automatically enabled except when mouse movement is
turned on. You can control the ship’s speed with the joystick throttle. To
implement mouse-controlled movement, you have to click Mouse Movement on
the Settings menu.
Moby Dick is written for Windows® 95 only, not Windows NT®. Because the game
now uses DirectDraw for the graphics routines, you must have DirectX™ installed
on your system in order to run it. Since the runtime files required for a
proper installation of DirectX amount to more than 15 megabytes, I thought it
best not to include them with the sample code.
The supplied executable file uses DirectSound®. To build the program without
DirectSound support, comment out the “#define USE_DIRECTSOUND” line in MOBY.CPP.
I’ve provided this option so you can compare DirectSound with the old way of
playing sounds in Windows.
I’ve made one small but important change to the code for the main program
loop. I talked about the advantages of using PeekMessage in real-time
games in Part I. However, I did not take into account the fact that the PeekMessage
loop gobbles up CPU time even when the application is in the background and
theoretically not doing anything. So I’ve added a call to WaitMessage
when Moby Dick is paused. This function suspends the thread until there is a
message in the queue.
Sound
.VOC-to-.WAV Conversion
If you're converting an MS-DOS game to Windows, chances are your sound files
are in .VOC format. You will want to convert these to .WAV format, the standard
for Windows.
Many freeware and shareware utilities are available for converting 8-bit VOC
files to .WAV files; check the Windows AV forum (WINAV) on CompuServe or the
vast programming-related archive at ftp://x2ftp.oulu.fi/pub/msdos/programming.
You may already have a utility that came with your sound card; for instance,
the Sound Blaster comes with VOC2WAV.EXE.
Playing Waves with PlaySound
The benefits of Windows are nowhere more obvious than in playing digitized
sound, as my own experience illustrates. For this article, I meant to develop
some routines for the MS-DOS version of Moby Dick that would at least work with
Sound Blaster and Sound Blaster–compatible cards. After spending a day tracking
down and unsuccessfully tinkering with code samples (most of them generously
sprinkled with inline assembler), I finally gave up and turned to the
Windows-based version of my project. Within minutes, I had a .WAV file compiled
into the executable and playing loud and clear—and I knew it would work with any
sound card installed in Windows.
My first step was to play the sound directly from a .WAV file. This is all I
needed to do to get Moby to “blow” audibly:
PlaySound("blow.wav", NULL, SND_ASYNC | SND_FILENAME);
The arguments are: (1) the name of the file; (2) NULL for the handle of the
executable containing the resource because we’re not using a compiled resource;
and (3) flags to ensure that the function returns as soon as possible, rather
than waiting for the sound to finish, and that the first argument is interpreted
as a file name.
Later I decided to make the sound data a resource that would be compiled
into the executable. That meant adding this line to the resource (.RC) file:
IDR_WAVE_BLOW WAVE "blow.wav"
IDR_WAVE_BLOW is a numerical ID that I have defined in the RESOURCE.H
include file. WAVE is my own application-defined (and arbitrarily named)
resource type. Waves are not standard resources like icons and bitmaps, so a
type name must be assigned.
When it comes time to play the sound from the resource, this line of code
does the trick:
PlaySound(MAKEINTRESOURCE(IDR_WAVE_BLOW), hTheInstance,
SND_ASYNC | SND_RESOURCE);
Here, the first argument is the resource name (supplied by the macro MAKEINTRESOURCE
from the numerical identifier). The second argument is the handle of the module
that contains the resource (in the case of Moby, the main program instance).
The last flag has been changed to indicate that the first argument is a
resource name rather than a file name.
Playing Waves with DirectSound
PlaySound is adequate for playing simple waves at discrete points in
a game, but it doesn't allow on-the-fly mixing or any other effects. There is a
group of functions in the multimedia extensions to the Windows application
programming interface (API), most of whose names begin with wave, that
allow more advanced tinkering, but these have been superseded by DirectSound, a
component of the DirectX Software Development Kit (SDK). DirectSound 3 has
several advanced features, including automatic mixing, 3D sound (the illusion
of "surround sound" on a two-speaker system), and stereo panning. I
won't go into the finer points here, but I'll give you a glimpse of the
benefits—and the extra overhead—that come with DirectSound.
First the bad news. DirectSound doesn't provide the behind-the-scenes
loading and parsing capabilities of PlaySound. It's your responsibility
to parse the .WAV file and copy the data into a DirectSound secondary buffer. (Secondary
buffers hold the wave data in the background. When the sounds are played,
they're streamed into a primary buffer. You don't have to concern
yourself with the primary buffer, which is maintained by DirectSound.) My
sample project does the loading and parsing in WAVE.CPP, where the sound data
is loaded from a resource. The code is quite lengthy, so I won't quote it here.
Similar code for reading directly from .WAV files is found in Dave Edson's
article "Get World-Class Noise and Total Joy from Your Games with
DirectSound and DirectInput," (MSDN Library, Microsoft Systems Journal,
1996, volume 11) from which most of WAVE.CPP has been borrowed.
Now the good news: once the DirectSound secondary buffer has been set up,
things get simple again. To play the sound, just call the Play method of
the DIRECTSOUNDBUFFER object:
lpDSB_Blow->Play(0, 0, 0);
The first two arguments are always zero in current versions of DirectSound.
The third argument can be set to DSBPLAY_LOOPING if you want to keep repeating
the sound.
Moby Dick for Windows illustrates how to loop a sound and shows the
different results of PlaySound and DirectSound's Play method when
two .WAV files are vying for attention. Turn the sound on through the Settings
menu and click the left button to start a “boing” sound. (This is the sound of
Queequeg’s harpoon, which is on a bungee cord.) Stop the sound with the right
mouse button. If you compiled the program with USE_DIRECTSOUND defined (or are
using the supplied executable), the "boing" and Moby's heavy
breathing are mixed by DirectSound and play simultaneously. If you didn't
define USE_DIRECTSOUND, Moby will be silenced by the looping sound, because PlaySound
can only do one thing at a time.
I think you'll agree that the automatic mixing feature is reason enough to
use DirectSound in your game, without even considering its many other
capabilities. However, before leaving the topic of DirectSound I'll show you
how to implement a simple stereo effect, just to demonstrate how easy it is to
enhance the sound effects in your game when you convert to Windows.
Presuming you have a stereo sound card, when playing the DirectSound version
of Moby you will notice that the whale's audible blow moves in space depending
on whether the spout is toward the right or the left of the game screen. Each
time Moby takes a breath, the program simply calculates his relative east-west
position on a scale of -10,000 to 10,000. Then it passes this integer value to
the SetPan method for the buffer, thus:
lpDSB_Blow->SetPan(pan);
This is done just before Play is called. DirectSound automatically
attenuates the playback on one channel or the other in proportion to the
deviation of pan from zero. While the first channel is attenuated, the
other remains at full volume. In other words, both channels are at full volume
when Moby Dick is at dead center in his world; one channel is silent when he is
fully to the west or east.
Playing MIDI Files
Through its multimedia API, Windows provides both high-level and low-level
support for playback of MIDI files. The high-level support is in the form of
media control interface (MCI) functions. These will work with MIDI formats 0
and 1, but not with 2.
The MCI functions can actually be called in two different ways. The mciSendString
function lets you control the MIDI sequencer (or any media player) with string
commands that hardly look like computer code. Here’s an example that opens the
sequencer and readies a file for playback:
mciSendString(
"open song.mid type sequencer alias aria",
lpszReturnString, lstrlen(lpszReturnString), NULL);
The call defines the file name, the device type, and an alias (“aria” in the
example) which can be used in subsequent calls that will play, pause, or
otherwise control the playback of the data.
More familiar looking to C++ programmers are the equivalent orders issued
through mciSendCommand:
MCI_OPEN_PARMS mciOpenParms;
MCI_PLAY_PARMS mciPlayParms;
DWORD dwReturn;
mciOpenParms.lpstrDeviceType = "sequencer";
mciOpenParms.lpstrElementName = "song.mid";
dwReturn = mciSendCommand(NULL, MCI_OPEN,
MCI_OPEN_TYPE | MCI_OPEN_ELEMENT,
(DWORD)(LPVOID) &mciOpenParms));
You can see a simple implementation of mciSendCommand in MOBY.CPP.
The program simply plays a tune from beginning to end, at which time the
Windows operating system sends a message to the application. The program’s
response to the message is to play the tune again.
A lower-level approach to playing MIDI files is illustrated in CGMIDI.CPP,
which can be found among the source files for the “Immortal Klowns” sample in
the DirectX SDK. This example does not use MCI but loads the music file into
memory and streams it to the MIDI sequencer using the Windows MIDI stream
services—not quite as efficient as custom-built sequencing code but more
efficient than MCI. Preloading the data is the preferred method if you don't
want your program accessing the hard drive—or, worse, the CD—during game play.
You may be able to salvage much of your existing code for streaming to the
sequencer under MS-DOS (allowing for the difference in implementing timers
under Windows). You are, however, welcome to use the routines in CGMIDI.CPP
with or without modification—Microsoft has granted a royalty-free license for
this code.
As for sound effects, by now it's probably safe to assume that game players
running Windows 95 have sound cards capable of playing digitized sound.
However, if you want to use General MIDI to create some simple sound effects
such as trumpet calls, you have many happy hours in front of you studying the
API reference for the low-level MIDI functions. A good place to start is midiOutShortMessage.
Graphics
Here we are, halfway through the third article in the series, and at last we
reach the subject of graphics. I’m not going to attempt a short course in
animation techniques in Windows 95; for that I refer you to the many books on
the topic. (New titles are appearing in quick succession as I write this. Check
out the Microsoft DirectX Web site at http://www.microsoft.com/directx/
as well as the shelves of your bookstore.) What I will do is touch on some key
areas where programming for Windows differs from programming in the MS-DOS
environment.
Palettes
In this brief discussion I will presume you’re designing for 256-color
modes. Two key palette issues have to be considered when porting the graphics
from your MS-DOS game to Windows.
First, if your game is not going to run full screen, it will need to live
with other programs that have their own palettes. Say your program displays a
bitmap that uses 150 colors. In the background there is another application or
perhaps desktop wallpaper that uses 200 colors, and Windows itself is reserving
20 colors for the caption bars, menus, and other items. That makes for a total
of 370 colors that need to be displayed simultaneously in a 256-color mode.
How does Windows handle this demand? Essentially, it mediates among
different applications by making minor adjustments to colors—in other words, by
reducing those 370 colors to 256. Nigel Thompson has explained the process more
lucidly than I can, so I’ll simply quote from Chapter 3 of his Animation
Techniques in Win32:
The application currently in the foreground gets first
choice in choosing the set of colors in the actual physical hardware palette.
By giving the foreground application first choice of the palette colors, the
system keeps the user happy most of the time because the application that is
getting most of the user’s attention is the one that gets to select the color set.
Applications in the background have to make do with the colors they’re given,
but Windows helps the background applications out a bit by trying to
accommodate their color needs too. If the foreground application hasn’t used
all the free hardware palette color entries, the background applications get to
use the remaining entries on a first-come-first-served basis. When all the free
slots are used up, Windows tries to map the colors requested by the background
applications to those currently set in the hardware. This accommodation results
in a recognizable, if not wonderful, rendition of the background application’s
images.
Now, what is the practical effect of all this on the game developer? If your
application uses DirectDraw in full-screen mode, none at all. But for a game in
a window, your choice of colors may have side effects.
Some years ago, there was a wonderful adventure game called Loom that was
written for the Extended Graphics Adapter (EGA). (For you youngsters, the EGA
could display 16 colors at a time out of a total palette of 64.) The designers
of this game overcame the limitations of 16 colors by creating story locales in
thematic hues—for instance, an emerald city that used all the greens available
in the 64-color palette while discarding other colors that were not needed for
the persistent elements of the interface.
If you did something like this in a Windows-based game—say, created a
wonderful, realistic forest scene using 150 shades of green—then Windows might
have a very hard time trying to map background colors to the available palette,
and the photo of a red sunset being used for desktop wallpaper could suddenly
look like Junior’s first attempt at paint-by-numbers. By the same token, your
forest scene might be reduced to mud when it was in the background. Not a huge
problem, perhaps, but a side effect that you may want to consider.
Consideration number two—and this is a big one—is the 20 system or
“static” colors that Windows reserves unto itself. It guards these jealously,
and if your game uses 256 different colors, it’s too bad for you—20 of those
colors will be mapped to the reserved colors, with results that may or may not
be pleasing to the eye. So you have to either restrict your artists to 236
colors or ensure that the static colors are present in the palette for every
scene. For maximum performance you also want to ensure that the static colors
are in the palette slots where Windows expects to find them. See Chapter 6 of Animation
Techniques in Win32 for details on how to create an “identity palette” that
has the static colors in the correct positions.
There’s a lot more to palette management on the Windows platform, and I
recommend that you read up on the subject if you are porting a
graphics-intensive game. Thompson’s book is an excellent place to start.
Fonts
Graphical fonts vs. TrueType fonts
For display text in your MS-DOS game, you no doubt created one or more
alphabets as large bitmaps and used an algorithm for finding characters in the
font and displaying them with the correct proportional spacing. This technique
will still work in Windows, of course, where you could put each font in its own
DirectDraw surface buffer for quick blitting to the offscreen display buffer.
The alternative is TrueType, the scalable font technology built right into
Windows. You will probably be using TrueType fonts in standard dialog boxes,
but you can use them as part of your graphical screens as well. The benefits
are ready-made fonts and scalability; the disadvantage is that you’re
restricted to the standard typefaces that come with Windows 95 plus any others
that you’re willing to license and distribute with your game.
Moby Dick contains a very simple example of placing TrueType text directly
on a bitmap. Any DirectDraw surface can be treated as a drawing surface for the
graphical device interface (GDI), which is the set of functions used to display
text and graphics in conventional programming for Windows. When the Moby Dick
graphics are initialized, some text is put into the DirectDraw surface that holds
the master copy of the ocean chart. The text becomes part of the bitmap.
Whenever this image is blitted into the back buffer, or from the back buffer to
the primary surface, the text goes along with it.
Here’s the code that sets up the ocean chart and writes the text to it:
// create the chart surface
lpDDS_Map =
DDLoadBitmap(lpDD, MAKEINTRESOURCE(IDB_MAP), 0, 0);
// get a device context for it. This also locks the surface.
lpDDS_Map->GetDC(&hdc);
// set transparent mode so text won’t wipe out whole rectangle
SetBkMode(hdc, TRANSPARENT);
// "create" (i.e. choose) the desired font
hFont = EzCreateFont(hdc, "Times New Roman", 150, 0, EZ_ATTR_ITALIC, 0);
// select it into the device context
SelectObject(hdc, hFont);
// write the text
TextOut(hdc, 50, 50, "Here be Whales", 14);
// delete the font object.
DeleteObject(SelectObject(hdc, GetStockObject(SYSTEM_FONT)));
// release the device context and unlock the surface
lpDDS_Map->ReleaseDC(hdc);
Looks pretty simple, right? Actually, much of the functionality is hidden in
the call to EzCreateFont (in TTFONT.CPP), a function lifted pretty much
intact from Charles Petzold’s Programming Windows 95. I refer you to
pages 217-37 of that excellent book for a full treatment of selecting TrueType
fonts and formatting text output.
Large and small system fonts
When you use the standard system font under MS-DOS, you can design the
screen confident that an end user will see the same thing you see on your
development machine. Not so in Windows. Quite apart from the fact that your
game will be played in different screen resolutions, a user may choose large or
small system fonts for the higher resolutions and any system fonts in your
program will be scaled accordingly. I have seen computer games that overlook
this fact, with the result that text is not properly aligned or it simply
doesn’t fit in its window when displayed on a system using large fonts.
With the certainty that at least the standard TrueType fonts are going to be
available on any Win32 system, there really isn’t much reason to use system
fonts in your game. But if you do, be sure to design and test for both the
large and small sizes.
Animation with DirectDraw
In the first of these articles I promised that I would eventually implement
a DirectDraw graphics system for Moby Dick. I’ve made good on my promise, but
I’m not going to go into DirectDraw in depth. For one thing, it’s a big
subject; for another, there’s no shortage of articles and books on it.
Is it necessary to use DirectDraw? If your game has heavy animation, don’t
even think of the alternatives. DirectDraw provides a huge gain in performance
over the GDI or WinG—performance equal to or even exceeding that possible under
MS-DOS, depending on which hardware features are available on the user’s
machine. As I said at the beginning of this article, however, the runtime files
take up a lot of real estate, so DirectX is not yet a practical tool for
shareware game developers.
From the standpoint of conversion of existing MS-DOS graphics code to
DirectDraw, here are a couple of things to keep in mind.
- You don’t need any special
code to blit with transparency. Not only does DirectDraw handle this
automatically, but, if you choose, it will even figure out the color key
(that is, transparent color) by checking the upper-left pixel of the
sprite. See DRAW.CPP in the Moby Dick files for an example.
- Memory management is a
different kettle of fish under Windows and DirectDraw. You can forget
about expanded memory, extended memory, high memory, low memory, and all
that. But you will want to get up to speed on the way DirectDraw takes
advantage of memory on the graphics card. You can optimize performance by
making sure frequently used images get stored on the card rather than in
system memory.
One unusual characteristic of Moby Dick for Windows as a DirectDraw
application is that it is not full screen. Since most DirectDraw tutorials
focus on full-screen games, I’ll point out a few things that are different
here:
- You can’t flip surfaces in a
windowed application. (DirectDraw flipping is similar to video-page
flipping under MS-DOS.) Bitmaps have to be copied from one surface to
another.
- It follows from the point
above that there can’t be any background (that is, flipping) surfaces
attached to the primary surface. You can’t use the DDSD_BACKBUFFERCOUNT
flag when creating the primary surface.
- The primary surface doesn’t
know anything about the client area of your application. The Blt
method writes to screen coordinates, so it’s up to you to get the client
area’s location and size with GetClientRect and ClientToScreen.
- To keep anything from being
drawn outside the client area, you need to create a clipper object and
attach it to both your window and the DirectDraw primary surface.
Installation
Installation of any software under Windows is a bit trickier than under
MS-DOS because of the way resources are shared. A well-mannered Windows-based
game will also register itself and provide an uninstall utility to clean up
after itself in the event that (heaven forbid!) users want to kick it off the
hard drive. (See Teri Schiele’s article listed in the bibliography for an
overview of the requirements for installing a program in Windows.)
It’s not a good use of your development time to create an installation
program from scratch or attempt to port over your old one when there are so
many alternatives available, mostly involving small changes to sample code. To
begin with, if you’re using DirectX, you will almost certainly want to take
advantage of DirectSetup, which comes free with the DirectX SDK; this is the
most bulletproof way of making sure that the DirectX runtime files are properly
installed along with your own files. If you’re not using DirectX, you may
already have another installation utility that shipped with your development
system.
If you don’t have an installation system or simply want to see what others
are available, you can check out the Yahoo!
listings.
Online Documentation
I’m a little hesitant about plunging into this section, because I don’t want
to be seen as encouraging game publishers not to supply printed documentation.
Personally, when I buy a game I like find something in the box besides a CD and
a registration card. Give me a substantial printed booklet—preferably with lots
of tables, photos, diagrams, background information, and designer’s notes—that
I can carry around and study at odd moments. Sure, put the last-minute
information into a readme file, but don’t leave me feeling like I bought the
cake without the icing.
Despite my own feelings, it’s a fact of life that publishers are ruled by
“cost of goods,” and it’s becoming more and more common to see only the basics
of game play covered in a printed manual (if one is provided at all), while the
finer points are covered in readme files or some other form of documentation on
a CD. This trend is bound to gather momentum in the Windows environment, where
online information can easily be presented with rich formatting and graphics.
In this section I’ll suggest a few alternatives for documentation that are
not readily available in the MS-DOS world.
Windows Help
You can produce Windows Help files in Microsoft Word or any other word
processor that provides full support for Rich Text Format (RTF); this does not
include WordPad, which does not allow page breaks or hidden text, both of which
are necessary in Help source files. You also need a Help compiler, usually
included with Windows development tools; in the case of Microsoft Visual C++®,
it is the Help Workshop (HCW.EXE) that also simplifies the creation of
graphical hot spots and tables of contents.
RTF
Your game is for Windows 95. Every copy of Windows 95 comes with WordPad.
WordPad reads Rich Text Format (.RTF) files. Therefore your on-disk
documentation can be in .RTF format.
The concept is simple, but for some reason—perhaps WordPad’s limited support
for .RTF?—it hasn’t caught on and developers keep using plain ASCII text for
their readme files. In doing so, they lose the benefits of varied font sizes
and styles, paragraph spacing, and other formatting that can make the text both
easier to understand and more attractive.
You can use Microsoft Word or WordPad to create RTF files. Remember, though,
that any page breaks and margins you create in Word will be lost when the file
is displayed in WordPad.
HTML
The Hypertext Markup Language (HTML) is rapidly becoming much more than a
World Wide Web formatting tool. Microsoft is moving the Windows Help system to
HTML, and as Internet Explorer becomes more tightly integrated with the Windows
desktop, the day is probably not far off when HTML becomes the dominant tool
for online documentation.
Using the HTML Help tools from Microsoft, you can provide documentation in
HTML format without worrying about whether the user has a Web browser. The
latest information is available in the "Authoring" section of the Microsoft
SiteBuilder Workshop at http://www.microsoft.com/workshop/.
If you want to convert files from another format to HTML, or vice versa,
check out the list of filters at http://www.utoronto.ca/webdocs/.
This site also links to lists of HTML editors and other utilities.
PDF
Another alternative for richly formatted online documentation is the
Portable Document Format (PDF) supported by the Adobe family of Acrobat
products. The format is very much oriented toward display and printing. One
advantage of .PDF files is that they will look the same on different platforms.
However, you’ll have to install Acrobat Reader along with your game in order to
ensure that users can view the documents.
In the future, Internet browsers may have the ability to read .PDF files
directly, which will make this format a more attractive alternative for all
types of online documentation. For now, though, it is best suited for documents
with a lot of graphics and a need for precise layout.
You can get more information about Acrobat at http://www.adobe.com/acrobat/.
Acknowledgments
The supplied music is the “Sacred Dance of the Priestesses” from Giuseppe
Verdi’s Aida and is used with the permission of Rajat Mathur, the
transcriber. The file was obtained from http://www.prs.net/midi.html,
a wonderful source of classical music in MIDI format.
Bibliography
Edson, Dave. "Get World-Class Noise and Total Joy from Your Games with
DirectSound and DirectInput." Microsoft Systems Journal 11
(February 1996) (MSDN Library, Periodicals). It only covers version 1 of
DirectSound, but it is still a useful introduction.
Gery, Ron. "The Palette Manager: How and Why."(MSDN Library,
Technical articles)
Norton, Michael J. Spells of Fury: Building Windows 95 Games Using
DirectX 2. Waite Group Press, 1996.
Petzold, Charles. Programming Windows 95. Microsoft Press, 1996.
Schiele, Teri. "Windows 95 Application Setup Guidelines for Independent
Software Vendors." (MSDN Library, Technical articles)
Thompson, Nigel. Animation Techniques in Win32. Microsoft Press,
1995. Covers sounds (CD audio, waveform, and MIDI) as well as animation and
palettes.
Peter Donnelly has been a game designer since 1972 and a programmer since
1984. He has authored four "paper" games with historical and fantasy
themes, and his computer design credits include three interactive comic-book
adventures and one full-scale adventure game. He has also developed shareware
games and game editors for MS-DOS and Windows. |