Peter Donnelly
Microsoft Corporation
November 25, 1996
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.
Click to open or copy the files in the Moby 3 sample application.
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.
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.
I haven’t made any enhancements to the MS-DOS version of the game, so this time it’s not included with the sample files. It’s still in the packages that come with "Moving Your Game to Windows, Part I: Tools, Game Loop, Keyboard Input, and Timing" and "Moving Your Game to Windows, Part II: Mouse and Joystick Input."
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.
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.
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.
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.
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.
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.
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.
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.
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.
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.
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:
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.
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.
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.
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.
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.
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/.
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.
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)
Klopfenstein, Doug. "Basics of DirectDraw Game Programming."(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.