|
Experiences building Java games for the
Internet |
The most obvious approach to building games on the Internet is to create a Java applet. Since Java is such a young language, it is reasonable to wonder whether or not it is possible to successfully build a full fledged game in Java. This article discusses our experiences in using a custom game framework to build single player Java arcade games. This article focuses on:
![]()
speed
![]()
Java virtual machine implementations and how it affected our development
![]()
piracy, which has always plagued the software industry, especially the gaming
sector
There are several published sources for dealing with latency speed for multi-player games, so they will not be discussed here.
Speed
Perhaps the most often-heard complaint of Java developers is that Java is slow. Is it too slow for a good game? The answer depends upon several factors. The first and most obvious factor is the hardware itself. CPU and bus speed, cache size, RAM, and operating system all affect speed. Next, and equally important, is how the user is running the applet. For example, using a web browser instead of the appletviewer program can cause a hit to performance due to increased overhead when running inside a browser. Using an interpreted Java Virtual Machine (JVM) is slower than a JVM with a Just In Time (JIT) compiler which compiles code before it is executed. Additionally, some JIT compilers are faster than others. The final factor is what the game is trying to accomplish. 3D perspectives are much more costly to render than 2D sprites, and the number of moving objects can have a dramatic impact on performance.
Unfortunately, with so many factors to consider, it is impossible to say that Java is or is not acceptable for fast action games where speed is an issue. We have managed to create some games which are acceptably playable, but not all games will turn out that way. There are a few tips which can help make success attainable.
The first thing is to determine which objectives to emphasize. Most developers want their game to be available to as wide an audience as possible. At the same time, they want their game to be really "cool". Typically "coolness" is achieved with fancier graphics, better sound, and more action. This means beefier hardware requirements and subsequently a smaller audience. A balance must be chosen, which will vary depending upon the game and marketing decisions. We chose a 90 Mhz Pentium running Netscape Navigator 3.0 as our target platform for a typical shoot-em-up game, Winvaders. The reasons for this are twofold: a reasonably wide audience and the performance problems in Navigator 3.0.
First, a 90 Mhz Pentium is rapidly becoming the entry level machine for most users and it would be pointless to attempt authoring a sufficiently interesting game for slower hardware.
Second, Navigator 3.0 is still by far the most popular browser on the market, and we wanted our game to be played by the largest audience possible. However, we discovered that the Java virtual machine in Navigator 3.0 was noticeably slower than Internet Explorer 3.0 in executing our game. Thus, if we could optimize the game to be playable under Navigator 3.0, then the game would be more than playable under IE 3.0. This is not a broad statement claiming that Navigator 3.0 is inherently slower than IE 3.0. With our games, which require constant screen updates, this was the case. (In fact, appletviewer even beat out Navigator 3.0). There are published benchmarks on the relative performance of the two browswers.
It is also important to decide what is an acceptable frame rate. Movies run at 24 frames per second, television just under 30. Those are great targets for smooth motion, but they can be difficult to achieve. Since the illusion of motion seems to hold up down to about 10 frames per second, some developers might be willing to accept a lower frame rate. Sprite motion becomes jerky and game response seems unacceptably sluggish when the frame rate gets slower than about 10 fps, so that should be an absolute minimum target rate, with higher frame rates preferred.
As it turns out, it is possible to produce a Java game with an acceptable frame update speed, but it takes some work. To avoid screen flicker, we chose the popular technique of double-buffering. Graphics that need to be updated are first drawn to an offscreen buffer and then copied to the screen in a tight loop. This technique can result in slower performance since screen updates happen twice. It is possible to speed up the process of copying the rendered frame to the screen, though, if you keep track of the changed areas of the off-screen buffer ("dirty rectangles") when you draw them. Then, when it comes time to flush the graphics to the screen, only copy the changed parts, rather than the entire buffer. Since some parts of the buffer may overlap, it is wise to coalesce these areas into a single area to be flushed. (See fig. 1)

Figure 1.
Just because dirty rectangles overlap, though, does not mean that they should be coalesced. If, for example, they only overlap by one pixel, you will end up copying twice the needed number of pixels to the screen. In the worst case--where all the sprites are in a diagonal line--you could end up copying the whole buffer when as little as 5% has actually changed. After a game is written and works properly, part of the performance tuning should be to adjust the algorithm which decides whether or not to coalesce rectangles. We found that a properly tuned algorithm can make the difference between a game which is playable and a game which is impossibly slow. In practice, achieving balance will depend upon the game and the sprites it uses.
One drawback to this optimization is that when an applet has been obscured and then is re-exposed, the static areas of the screen will remain an unattractive unpainted shade of gray. The current Java AWT is not very good at telling applets what areas need to be redrawn. It tends to be an all or nothing affair, and is worsened because the AWT asks for redraws fairly often. Since redrawing the entire buffer is expensive and really drags down the performance, we made a compromise. Occasionally, at a point less obvious to the player, the whole applet will update the entire screen area whether it needs it or not. The end of a level is, for example, a good place to do this, especially for games where levels can be completed quickly. By doing this, the game will remain partially unpainted for only a short period of time, and the speed advantages of this optimization are not lost. Hopefully future iterations of the AWT will allow for more elegant solutions to this problem.
Another optimization which directly affects game play is when the game's resources, such as sprites, are loaded. Java loads images and sounds in a "lazy" fashion, which means they aren't retrieved until they are needed. Since the first time an image or sound tends to turn up is during game play, this policy makes for pauses in game play that are quite irritating to a player in the heat of action. The solution is to force the loading and caching by using the MediaTracker class. It allows the applet to watch the progress of the resource and wait for it to finish loading.
The speed of a game during play is not the only speed an Internet game developer needs to worry about. There is also the downloading speed. Since applets are designed to be a one-shot deal, they must download quickly or the players will become bored and leave the game before it finishes downloading. The obvious solution is to reduce the size of the applet and the images and sounds associated with it. The next step is to bundle up all the Java .class files and other resources into a single file. Internet Explorer can download a .cab file, Navigator downloads uncompressed .zip files, and Java 1.1, coming soon to browsers near you, defines a .jar file. By putting all the resources into a single file, the browser will not have to make as many connections to download your applet and this will result in a faster download. Additionally, .cab and .jar files can be compressed to further reduce the size of the applet. Another reason to do this is that the Internet can at times be flaky. The chances of a single download making it through unscathed seem to be a lot better than the chances of grabbing all the individual files one at a time. Since the current tags to support these "bundled" applets are different for each type of bundle, it is possible to use all of them, providing your players with a happier downloading experience no matter which browser they choose.
For more information about these and other optimization techniques, refer to the various white papers found on our web site (www.globalobjects.com/).
Many other optimization techniques are also possible, but will be more dependent upon the game itself. The keys are to make sure all the expensive operations are done before the game starts, don't needlessly repeat calculations, and make the minimum number of calculations required to obtain the desired effects. Obviously, the quality of a design will impact applet speed immensely.
Finally, for development purposes, we used Internet Explorer on a 200 MHz Pentium Pro machine since it allowed for faster testing of the game. Once the applet functioned correctly, then optimization and tuning was undertaken. If optimizations are planned for and written from the start, this final phase of development can be completed quickly and painlessly. It is recommended that a developer not subject themselves to the pain of a slow machine during the initial phases of the game, since that could increase development time considerably.
JVM implementations
Assuming that a developer can make their game run at the desired speed does not mean all their problems are solved. Java is still a young language. There are many different compilers and virtual machines available, each with its own particular quirks.
As an example, we encountered a situation where an if/then statement was incorrectly evaluated by the virtual machine in Internet Explorer. The result was an enemy sprite which would chase the player in Navigator but run away from the player in IE! The original code looked like this:
if (x > ((Applaroids)owner).player.x) {
velocity_x -= GRAVITATIONAL_PULL;
} else {
velocity_x += GRAVITATIONAL_PULL;
}
It turns out that once the problem was pinpointed the fix was simple:
double track = ((Applaroids)owner).player.x;
if (x > track) {
velocity_x -= GRAVITATIONAL_PULL;
} else {
velocity_x += GRAVITATIONAL_PULL;
}
Although both should do the same thing, only the second one actually works as expected in both JVMs. Of course, with the rapid release schedule of various browsers, this bug will probably be gone by the time the reader attempts to duplicate it.
As Java matures, newer bugs--in any browser--will keep turning up. It is a good idea to keep track of discovered bugs and work-arounds by watching the Usenet news groups featuring Java. A lot of debugging time can be saved. Remember that just because it doesn't work does not mean it is your fault! It could be the JVM or your compiler, or an interaction between the two. To avoid embarrassment, though, make sure that the bug is definitely not yours before blaming somebody else!
Piracy concerns
One particular concern for commercial developers in particular is applet piracy. It is easy to download an applet part by part, collect the parts, and then put them on your own web page. There is currently nothing that can keep a determined hacker from doing this. One current technique is to have the applet refuse to start if it has been downloaded from an "unauthorized" server. This is done by checking the URLs of the applet's code base and the referring HTML page.
Unfortunately, a determined hacker can easily beat this "protection". There are Java decompilers available which will decompile a Java applet into compilable Java source code. Since Java uses no preprocessors and has a very specific byte code format, the decompilers have surprisingly good output. There is nothing to stop a hacker from decompiling your applet, removing the protection, and recompiling it. The only defense available is a code obfuscator which can be used to make the source less readable. Unfortunately, that certainly won't stop a determined hacker. Furthermore, if the URL you check against in your code is a static string, a hacker may just edit the .class file in an editor to change the URL! To get around this, it is possible to encrypt the URL and decrypt it in the running program.
Since it is possible to decompile the source to applets, there is also the danger of the applet's source code being pirated and used by competitors to create their own games. The sad truth is that right now there is absolutely no way to prevent a determined hacker from doing this. Any Internet game developer has to accept this possibility and choose a marketing scheme that will reduce the damage if they are attacked in any of these ways. If this is unacceptable, then Java is not the answer.
The only (partial) solution would be to use a product such as Asymetrix's SuperCede which allows developers to turn Java programs into compiled .exe programs for systems running Win32. Doing this will keep the source code safe and allow for stronger protection schemes. It will also remove the Java security features players may prefer and it removes the ability to play the game in a web browser. In effect, it nullifies most of the reasons for choosing Java in the first place.
Conclusion
It is possible to create fun Java games with fast, exciting action and reasonable performance, but issues of protecting intellectual property and limitations due to Java security could harm the ability of commercial game developers to build the games they would most like to build. Because of the immaturity of the language and tools, developers should expect to run into occasional snags, but usually they are not serious enough to cause the applet development to fail. Before development is begun, we recommend that developers plan to spend at least half the development time working around the various Java quirks and turning the applet's performance.
We wrote Winvaders in approximately four days. The applet code was done in a single day and the remaining time was spent creating graphics, testing, tuning, and debugging. As it turns out, quite a bit of that time was spent locating and working around bugs in browser JVMs such as the bug mentioned above. Planning ahead for these sort of setbacks will help keep schedules on time and still allow developers to produce fantastic games to feed the excitement-hungry Internet audience that awaits.
Don Yacktman is Vice President of Technology and Alex Nghiem is President of Global Objects Inc., an Atlanta-based consulting and training company specializing in Java and other OO technologies. The authors may be reached at don@misckit.com, alexdn@globalobjects.com.
Copyright © 1997 Global Objects Inc. All rights reserved.