In this column I'll give some general tips that are targeted at game developers but can
in fact benefit any Macintosh application. Many of the tips are illustrated in the
accompanying sample code (CopyBits ColorKarma) on this issue's CD. If you're
considering writing a game for the Macintosh, or you want to improve your existing
game or other application, these tips are for you. Here they are at a glance:
THE TIPS IN DETAIL
1. The Macintosh gaming market is wide open.
The Macintosh has infiltrated the homes, offices, and schools of every continent on the
planet, and it has matured enough to be ready for entertainment software of all sorts.
Hit Macintosh games have sold over 60,000 copies, and users are clamoring for more.
If you walk into a software store, though, you'll notice that there isn't a very large
selection of Macintosh game and entertainment software. Now is the time to take
advantage of the lack of competition and get into the entertainment market with your
games.
2. Bypass QuickDraw wisely.
Applications that bypass QuickDraw by accessing video memory directly may not work
on future Macintosh platforms. The Macintosh is evolving, and some of the changes may
be in the bus architecture, causing applications that write to video memory to break.
We're not saying this is going to happen any time soon, but it's a good idea to be ready
for it now so that your product will last in the marketplace.
A good compromise is to draw with custom drawing code into an off-screen GWorld, and
then use CopyBits to transfer it to the screen. This avoids accessing hardware directly,
and will always work. If you must write directly to the screen, it's important to
follow the guidelines set forth in "Graphical Truffles: Writing Directly to the Screen"
indevelop Issue 11, which states that you should always have a QuickDraw version of
your code. If your program uses QuickDraw (or QuickDraw GX), it will always be
compatible with every future Macintosh platform.
If you do bypass QuickDraw, your application should time the custom drawing code
versus QuickDraw at run time, and then choose the fastest routines. This way, the
fastest code will be used in cases where your code is running on a system that has an
accelerated version of QuickDraw, or perhaps a different CPU altogether.
3. Use CopyBits correctly.
I've heard many developers say that CopyBits is slow, that it can't achieve the frame
rates needed to do good games. If you use CopyBits correctly, however, youcan
achieve a high animation frame rate.
Understanding all of the factors that affect CopyBits performance is critical to
achieving high animation frame rates and still having processor bandwidth left over
for the rest of the game. The following tips on CopyBits speed have been collated from
many different sources, including Technical Notes, sample code, and other tomes of
QuickDraw knowledge. (See also tip 4 below.)
For more details on these principles, see the Tech Note "Of Time and Space and
_CopyBits."
4. The Scroll graphics smoothly.
This tip applies to games with scrolling maps, painting programs, and any application
that needs to scroll window content quickly and smoothly.
Many Macintosh applications suffer from flickering scrolling, caused by erasing the
previous image before drawing the new image. To reduce flicker, you should redraw
only the parts of the screen that change; don't erase anything first, unless absolutely
necessary. When you're designing your scrolling code or animation engine, the
philosophy to adopt is that every pixel should be touched onlyonce .
Another technique is to buffer your graphics into an off-screen GWorld: make all
changes in the GWorld and then transfer the image to the screen with CopyBits. This
can be slower, because the image is drawn twice, but it results in an especially smooth
update. An example of a game that uses CopyBits in this way is MacPlay's Out of This
World. This game draws into an off-screen GWorld using custom polygon-rendering
code (thus ensuring a high frame rate); then, when the image is completely rendered,
it's transferred to the screen with CopyBits (ensuring compatibility with future video
hardware). For more on this subject, see "Graphical Truffles: Animation at a Glance"
indevelop Issue 12 and "Drawing in GWorlds for Speed and Versatility" in Issue 10.
There's another method that isn't as smooth but uses less memory, and that is to use
ScrollRect. ScrollRect was changed in System 7: if you pass nil for the updateRgn
parameter of ScrollRect, it won't erase the area that has been uncovered, and you can
then use CopyBits in a second step to copy in the new bits. (In System 6, ScrollRect
will erase the area you're scrolling out of, causing the screen to flicker more.)The
sample code demonstrates these techniques, showing the tradeoffs between memory
footprint and smoothness/apparent speed.
5. Don't synchronize with the VBL interrupt.
Many developers have wanted to synchronize animation with the vertical blanking
(VBL) interrupt to eliminate tears when the next frame of animation is drawn before
the display hardware has completed the previous frame. It's possible to
eliminate tears from small-sized animations, but the overall application will run
more slowly because you'll be spending time waiting for the VBL period to start.
This results in a much lower animation frame rate, and the application also loses
processing power for the rest of the program. Note that QuickTime does not
synchronize with the VBL interrupt.
Another headache to consider with respect to synchronizing with the VBL interrupt is
that displays have different refresh rates, and each one's actual VBL period has a
different length. This means that for your program to have accurate frame rates on
different monitors, you'll have to time the refresh rate of the display you're animating
on.
To work around not being able to synchronize with the VBL interrupt, you should try
to interleave the animation processing so that you're never updating too many objects
at one time. The Time Manager will allow you to break the processing up into separate
tasks (see tip 8). If you're getting tears on objects, consider using fewer objects or
smaller ones.
6. Use Sound Manager 3.0.
The Macintosh Sound Manager has recently been enhanced (version 3.0). It now can
efficiently handle as many sound channels as memory and processor bandwidth can
take. This means four channels on a Macintosh LC (which used to handle only one
channel) and up to 16 or more on higher-end platforms. As a result, your
application can play sound and still have enough CPU bandwidth for other
animation and processing. The sample code demonstrates multiple sound channels
playing asynchronously while animating an image. Also see "Somewhere in
QuickTime: What's New With Sound Manager 3.0" in develop Issue 16 and "The
Asynchronous Sound Helper" in Issue 11. Many bugs have been fixed in Sound
Manager 3.0. You can now open a sound channel at the start of your program and
then continuously use SndPlay to play sounds through it, without disposing of the
channel between sounds. Previous versions of the Sound Manager had problems
playing sampled sounds like this, so many developers adopted the technique of
allocating and disposing of a new sound channel for each sound played.
On the AV Macintosh models, the Sound Manager uses the DSP. This requires the DSP
Manager to load a new component every time you open a new channel, and may require
disk access. So if you're running with Sound Manager 3.0 you shouldnot open and close
a sound channel for each sound played; doing so will cause your application to perform
less than optimally, especially on the AV models.
See the source code file GameSounds.c, which is part of the CopyBits ColorKarma
sample, for an example of a unit that manages asynchronous sound. If Sound Manager
3.0 or later is present, the code opens the sound channels at initialization and closes
them when the program quits; otherwise it opens and closes the channels as sounds are
played.
Sound Manager 3.0 also adds a new routine, named GetSndHeaderOffset, that makes it
easier to use a bufferCmd to play sounds. Using a bufferCmd is faster than using
SndPlay. See the sample code on the CD for an example.
Note that the sample code doesn't store the application's A5 register as part of the
callback command, so that the interrupt code can set the flag associated with the
channel. Instead it just stores a pointer to the flag. This allows the interrupt-time
callback to be very small, since it doesn't have to save, set up, and restore A5; it just
dereferences the pointer and sets the flag directly. The sample code gives an example of
playing a sound asynchronously with a completion callback. I use thistechnique in just
about any interrupt-time callback code I write, including VBL tasks, Time Manager
tasks, and Device Manager completion routines.
If you don't use the Sound Manager at all, you're taking an unnecessary compatibility
risk. Apple has always recommended against accessing the sound hardware directly.
Applications that violate this rule have broken in the past, and they will break again.
7. Learn when to use (or not use) Apple events.
Apple events have simplified interapplication communication, making it easy to add
value to your application. A game played against live human players is often
more fun than a game played against a computer. Just about every night you'll find
some Apple engineers huddled over their computers playing Bolo, Spaceward Ho!,
or other network games.
Consider Velocity's Spectre, a network tank game that has been very successful.
Spectre doesn't use Apple events; it uses custom DDP (datagram delivery protocol)
socket listeners at the lowest level of AppleTalk to achieve high performance. But if
your game doesn't require the same level of performance, you may benefit from the
ease of use of Apple events.
If you require more performance than Apple events can provide, one option is to use
the PPC Toolbox directly, which will allow you to still remain a step removed from
direct AppleTalk. See the PPC Toolbox chapter ofInside Macintosh Volume VI for more
information.
If you require even more performance, you can use AppleTalk Data Stream Protocol
(ADSP) directly, or one of the other AppleTalk protocols. ADSP is a higher-level
protocol that will allow you to do block transfers and not worry about losing packets
and packet order.
It's hard to determine which networking protocol to use ahead of time. From my
experience in using Apple events to synchronize animation and events between two
Macintosh computers, I would say that if your game is a more than two-player,
real-time arcade game, Apple events would probably not be the best solution. If your
game is a turn-based strategy-type game, like Spaceward Ho!, RoboSport, Strategic
Conquest, and many others, Apple events will work very well for you, no matter how
many players are in the game.
For a simple example of using Apple events as a game messaging system, see ZAM
1.a13 on the CD.
8. Use the Time Manager.
The Macintosh Time Manager is very useful for game developers. Animation code often
needs a heartbeat, to synchronize the timing and updates of every object. The
Time Manager lets you break down your code into discrete tasks that run at a steady
rate. This allows you to write modular code that updates smoothly.
One limitation of the Time Manager is that tasks fire at interrupt time, so they can't do
much more than set a flag to inform the regular event loop that it's time to do
something. The sample code on the CD shows how to place a wrapper around the Time
Manager that allows you to execute tasks at non-interrupt time.
9. Use the Memory Manager effectively.
The Macintosh Memory Manager is very flexible, and a boon to most application
programmers. However, for the game programmer it can be a performance
problem unless it's used wisely. In a game, you should preallocate as much of
your memory as possible. If you're using a dynamic object allocation scheme,
you should design one that preallocates the objects and keeps track of which ones
are in use or not. If you have many allocated blocks in a heap and then request a
new one, you could send the Memory Manager into thrashing mode where it will try to
move many blocks around to make space. This can cause your animation to be
jerky or your whole game to freeze for an instant. So the best thing to do when
performance matters is to minimize your use of the Memory Manager. To minimize
Memory Manager use, you should not only allocate as much of your memory up front
as possible but also avoid using relocatable blocks unless absolutely necessary.
This means avoiding game architectures that rely on the Memory Manager for
dynamic object allocation. Definitely allocate your nonrelocatable blocks first,
and allocate handles later. This prevents heap fragmentation and avoids sending the
Memory Manager into a tailspin.
Be aware that some parts of the Toolbox, like Apple events, expect Memory Manager
structures. However, if the rest of the program's memory is allocated wisely to
prevent heap fragmentation, even these allocations will happen quickly, with no
impact on game performance.
10. Use a compatible copy-protection scheme (if any).
It's a matter of great controversy whether software should be copy-protected at all. No
software protection scheme on the Macintosh has ever survived the talented efforts
of Macintosh hackers. There's always someone who will defeat your copy
protection, no matter how convoluted it may be. There are many who consider such
protection a puzzle and a challenge to break, so by putting it in you may be inviting
piracy.
But if you do decide you want some level of protection on your game, we strongly
recommend against a disk-based protection scheme, which is guaranteed to break your
program. Instead, we recommend using one (or a combination) of the methods
described here.
One method is to use serial numbers: when the software is installed, ask the user to
enter the serial number from the disk label, and then imprint the software with the
person's name. Another method involves requiring the user to enter a password from
the manual every once in a while. If you do this, it's a nice touch to allow users who
send you the registration card to disable the password dialog; once you have the
registration card, you can link the customer to a serial number.
Consider also making the following checks: At installation time, use Gestalt to
determine the characteristics of the machine you're installed on, and save these to
your preferences file. Also, use FindFolder to record the directory ID of the System
Folder, which is the same until a new System Folder is created. Every time your
application starts up, make the same Gestalt and FindFolder calls to check whether
you're running on the same machine; if not, have the user reinstall the software and
reenter the serial number, or reenter the password from the manual.
These techniques are the most compatible way to both protect your sales and minimize
the kind of frustration customers experience with other password- or disk-based copy
protection systems.
ARE YOU GAME?
That's not all! To help Macintosh game developers share tips, tricks, and information,
Apple has set up the Game Development Discussion folder on AppleLink (in Developer
Support: Developer Talk). This board is read by Macintosh game development
companies, other developers, and Apple engineers on a daily basis. Also, there's a
folder on this issue's CD, called Game Development, that contains special resources
Apple has put together to aid all game developers.
So if you're tired of the scant choices on the Macintosh Entertainment shelf in your
local software store, do something about it: write some games!
REFERENCES
BRIGHAM STEVENS (Internet vikingmind@aol.com) Since you last saw Brigham
here, he has spoken at the Worldwide Developers Conference on Macintosh game
development, moved to San Francisco, jumped out of a perfectly good airplane, become
addicted to flight simulators, spent 150 hours in a car with no stereo, tossed his
cookies on the steps of the Smithsonian Air and Space Museum in Washington DC, quit
Apple to start a game development/consulting company in San Francisco, and become a
vampire. You'll see some games from him later on in 1994. *
For more on VBL interrupts, see the Guide to Macintosh Family Hardware,
second edition.*
Sound Manager 3.0 is available for licensing, so you can distribute it with your
products to ensure that your customers and applications will receive its benefits. You
can reach Apple's software licensing department at AppleLink SW.LICENSE. *
If you create a demo version of a game by commenting out code or imposing a time
limit to game play, be aware that these kinds of demos are often hacked into full-blown
pirate applications. We strongly suggest that when you create a demo version, you take
out all nonessential pieces of code and sufficiently cripple the remaining software (so
that a hacker can't simply paste in missing resources to get a full, unprotected copy).
*
Thanks to Konstantin Othmer, Jim Reekes, and John Wang for reviewing this column.
And special thanks to the people who contributed to the information in this column,
including (but not limited to) C. K. Haun, Tony Myles, Craig Fryar, Mike Schlachter,
Bill Dugan of Interplay Productions, and the rest of the developers I've worked with
over the past couple of years.*