GameFrame for Java 0.9.4 Class Library Library Developer Documentation - Introduction
Version 0.9.1 02. Nov 1999 by Pasi Keränen, javanerd@geocities.com
Version 0.9.2 20. Dec 1999 by Pasi Keränen, javanerd@geocities.com
Version 0.9.3 02. Apr 2000 by Pasi Keränen, javanerd@geocities.com
Version 0.9.4 16. Jul 2000 by Pasi Keränen, javanerd@geocities.com
This document is aimed at persons wanting to contribute to the library development. It is a short introduction to the inner workings of the GameFrame for Java (GF4J) library and how to make your own engines to the library.
Index
1. THE IDEOLOGY BEHIND IT
2. THE DETAILS OF IT ALL
1. THE IDEOLOGY BEHIND IT
I've been following the Java language from it's first introduction (1995 I believe) and it has been my own fantasy to be able to program games with this easy to use language that also enables the games to run on different platforms. I've also played quite a lot of Java games on the web and although some of them have been very good indeed, I've been annoyed by the limiting factors:
Most of these limitations are caused by the fact that the game is running as an Java Applet on a HTML page. Some of them are caused by the limitations of the Java platform itself.
- No full screen graphics: while using the desktop resolution of 1024*768 the games were a post stamp sized blip on my screen even when they would have been full screen under resolution of 320*240.
- No joystick support: I've bought several game controllers during the years and paid good money on them and now I wasn't able to use them. Most of the games didn't even allow me to define my own keys and thus lead to frustration over trying to control the game with unfamiliar key combinations (like o=left, p=right, a=up, z=down and m=fire).
- No music and/or no sounds: the movies usually use music to enhance the experience, all commercial games use music, but Java games on the web have no music whatsoever. You'll be lucky if they have sound support that works under the Java VM that your browser uses.
- Keyboard support done the wrong way: some good games were ruined by improper keyboard support that used the callback methods from the KeyboardListener to add or decrement the controlled characters x/y coordinates. This causes the odd effect of key-repeat rate and pause before key-repeat stepping in and ruining your gaming experience. When you push the "go left" key, the guy jumps one pixel to the right and then stops for a second and then zooms along with incredible speed. Awful and that is the reason I've removed the callback input support from the library, to guide aspiring game developers to do input the right way: inside the games main loop and synchronized to the speed of the game itself.
- No hiscore table: most games like Asteroids or Space Invaders rely on the fact that you play them again and again to get yourself on the top of the hi-score board. And when you've done that someone else tries to do the same thing. Of course implementing hi-score tables with Java Applets is next to impossible if you don't own the server. This leads to the question of feasibility of Applet games.
I started to think about a way around these limitations. First of all I scrapped Applet support from the library, implementing Applet support was almost impossible because most web browsers seem to get offended even if you are just trying to find out what kind of Java VM the Applet is running on and refuse to run the Applet at all. Next I went "to the dark side" and found out that under Microsoft Java you could do full screen graphics with DirectDraw, play CD quality sounds using DirectSound and use your game controller by using DirectInput. After reading some literature on programming patterns I had found a way to make this all work and still be platform independent. What was left was the task of finding out what features should I support in the library and how to exactly implement them.
The features chosen to be implemented in the GF4J are more or less chosen by the least common dominator between JDK 1.1.x, JDK 1.2.x and Microsoft Java SDK 3.x. Some non-essential features like joystick input and MIDI music support are to be included in the library later on as these are nice to have features and their absence is non-fatal to the game application. If you want to add a new feature to the library by yourself, you should consider if it can be implemented in pure Java under JDK 1.1 and JDK 1.2, if not then I urge you to leave that feature out.
One way to get around this restriction with some features might be to have some kind of fallback functionality available to the user, e.g. I've had some thoughts about adding semi-transparent bitmaps to the library, the only problem is that these can be implemented only under JDK 1.2 and there is no knowledge about the speed of the semi-transparent blitting available. So I've had an idea that I should have the method that loads these semitransparent bitmaps have a fallback threshold value. That value would be the ALPHA channel value after which a pixel can be interpreted as fully transparent under platforms that don't support semi-transparency. This would be simple and flexible, but I've still decided not to include (at least yet) this feature in to the library, as support for the semi transparency would be so minimal (only under JDK 1.2.x).
Some features have been removed for reasons of good practice. E.g. the input listener interfaces were removed for several reasons. First of all they allowed the game programmer to do the input in the wrong way (see chapter 1.1 above). Secondly they introduced the multithreading programming risks to the game (and most users don't even notice this) as the callback methods are invoked by the event dispatcher thread that is NOT the thread the game is running on. GF4J is NOT thread safe so allowing the library itself to introduce new threads into the game while itself being not thread safe was a bad idea to begin with.
The GF4J library is based on the factory programming pattern. In factory pattern you wrap the classes that are to be manufactured behind an interface (in this case the EngineFactory interface) and then write a factory class (in this case GameFrame class) that manufactures those classes according to some preferences (in this case based on settings in the GameFrameSettings and the Java runtime the library is running on).Due to some restrictions I've had to break the engine factories pure interface approach a bit by introducing two static methods that each EngineFactory implementation has to implement. The first is the getInstance(GameFrameSettings settings) method and the second is the isRunnableOnPlatform() method. The first is needed to make sure the instances are only gotten by giving the settings object (this also makes it easy to implement the EngineFactory as a singleton) and the second is needed to make sure the EngineFactory can actually run under the current platform before instantiating it. More about this is explained in chapter 2.1. EngineFactory Enumeration.
The single factory pattern wasn't enough for GF4J though. It actually uses double factory as EngineFactory itself is also a factory that manufactures sound, graphics and input engines. The engines are also thus defined as interfaces. This all interfaces approach also gives the best flexibility as the implementations can do whatever they want to do internally to implement the interface functionality.
A good example of this flexibility is how the JDK 1.1.x GraphicsEngine implementation implements the Bitmap and the DrawableBitmap as internal classes (CBitmap & CDrawableBitmap), but Microsoft Java version (CFullscreenGraphicsEngine) actually separates the bitmaps based on whether they are transparent or not and has them defined in external classes (CDDBitmap & CDDAlphaBitmap).
1.4. The Platform Independence
Some people might not like that the library has implementations for the Microsoft Java platform (which is more or less JDK 1.1 minus Remote Method Invocation API plus some Win32 only classes). But this was the easiest way for me to give initial support for full screen graphics and some other features as I have no decent C++ compiler at home (and even if I had, I wouldn't be that thrilled to start using it). Also when using GF4J library it is just another platform to run on nothing more, nothing less.
Platform independence should be provided by the JDK 1.1 and JDK 1.2 implementations of the engines, but that doesn't guarantee anything unless the game programmer takes care of what JDK API calls he uses, some are not supported in JDK 1.1 and some are deprecated in JDK 1.2. Also I'm hoping that someday someone will provide native engine implementations for Macintosh and Unix (especially Linux) environments. After that 99.99% of worlds computers should be covered with native support and no one is stopping the users of the rest of worlds computers doing a native implementation for their systems. The idea is: the more the merrier. :)
2. THE DETAILS OF IT ALL
2.1. EngineFactory Enumeration
The GameFrame class is the main access point to the library, when it is instantiated it stores the given settings and enumerates through all the available engine implementations. The enumeration is done using these steps:
After this the GameFrame class is in usable state and its methods can be invoked.
- Find all subdirectories under gameframe\engines subdirectory (that holds the byte code classes from GF4J package).
- Check all these subdirectories for *.class named files.
- For each found class file check what interfaces it implements.
- If it implements EngineFactory interface, try to instantiate by trying to invoke the static getInstance( GameFrameSettings settings ) method. If no such method is found or other errors occur while invoking the method discard the class file in question.
- If the class can be instantiated check whether the EngineFactory in question can run on the current platform by invoking the static isRunnableOnPlatform() method. If the EngineFactory can't run on current platform discard the class.
- If the EngineFactory can run on the current platform, add it to the vector of engine factories placing it using the enumerated number gotten from getSuitabilityForPlatform() method. Higher suitability means higher (closer to index 0) placement inside the vector.
- When all subdirectories are scanned choose the EngineFactory class at first index of the vector that contains all the EngineFactory implementations and store the reference to it.
After the GameFrame class has found the best EngineFactory for the current platform it gets the graphics, sound and input engines from it. This is done transparently via GameFrame class to not confuse the user with EngineFactory and to centralize the try-catch block to the place where it should be (upon creation of the engines). The user never actually sees the EngineFactory class at all, all he sees is the GameFrame class that seems to give the engines.
I have tried to flag some methods as being "fast enough for interactive use", these methods are the ones that are meant to be used inside the games main loop and you should spend more effort on optimizing these to execute as fast as possible. Other methods are to be used during game initialization, level change etc.
EngineFactory implementations should implement the requirements stated in the EngineFactory classes JavaDoc documentation for each method. I've tried to make these comments as strict as I've could, but in case you can't figure out exactly what should happen when user invokes some method you can try to use the JDK 1.2.x engine factory as the reference implementation. This implementation is probably the "fullest" of the two pure Java implementations. If you still can't figure out some detail, you can always contact the original author at javanerd@geocities.com and ask for advice.
2.3. EngineFactory Finalization
When the game is about to exit it can call the exit() method of the GameFrame class to make sure everything is freed up properly. This in turn finalizes the currently used EngineFactory implementation and this should finalize all the used engines and those should finalize all bitmaps, samples etc. that they have used.
User shouldn't be able to finalize individual engines as this can wreck havoc on some implementations.
Copyright ©1999-2000 Pasi Keränen, javanerd@geocities.com
Re-use with the GameFrame for Java library builds
allowed if the history field in the beginning of the
document is left intact (only adding of entries
is allowed, no removing).