GameFrame for Java 0.9.4 Class Library Game Developer Documentation - Examples
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
Examples can be extracted from the examples.zip file that contains the source code files as well as the compiled byte codes. If you haven't setup your CLASSPATH to point to the location of GameFrame for Java (GF4J) library, you should just extract the example codes to the same directory where you extracted the GF4J distribution ZIP file into (remember to extract with subdirectories!). This document describes the examples one by one and also outlines the recommended order of going through the examples (which is the order the examples are listed in this document). This document also applies to all the Applet versions of the examples (BasicAppletExample, AppletExample1 etc.)
Index
1. THE BASICS
2. GRAPHICS EXAMPLES
3. INPUT EXAMPLES
4. SOUND EXAMPLES
5. OTHER EXAMPLES
1. THE BASICS
The examples use inheritance to shorten the needed initialization code for each example. This is said to be code reuse through inheritance and is considered bad practice. In this case though it is justified, as this keeps the amount of clutter in minimum for each subsequent example and this should help you to concentrate only on the subject at hand.
BasicExample.java / BasicAppletExample.java - Demonstrates how to initialize the GF4J library correctly. If executed it shows the graphics engine screen (a window or a full screen version, depending on the running platform) and waits for 3 seconds and then exits. This class is used as the base class of all other examples as it has the code to setup the library.
You should notice at least how the init(), executeCodeThatUsesGameFrame() and deInit() methods are meant to be overridden in other examples to do whatever the example wants to do. Note also that the overriding methods must call the super classes init() and deInit() methods to make this setup work properly.
Also noteworthy thing is how the command line parameters are stored in the constructor and then later on applied to the GameFrameSettings object in init() method. All subclasses also pass the given command line parameters to this base class to allow the user to use the standard GF4J library command line options (see the game_developer_intro.html for the full list of accepted command line options).
2.1.Graphics example 1 (extends basic example)
GraphicsExample1.java / AppletExample1.java - The first "real" example that demonstrates how to load bitmaps and display them using the graphics engine. It also shows how the DrawableBitmap can be used to make a ready made a simple tiled background that then can simply be drawn as the background of your game.
The GraphicsEngine is gotten in the init() method and the area of both the front- and backbuffers are painted using the black background color. Also in the init() the data directory wherefrom the images are loaded is set and the images are then loaded as Bitmap objects.
After loading the bitmaps we create a DrawableBitmap in the init() method and draw into it using the just loaded two Bitmap objects. The if clause inside the two for loops just ensures we get a nice checkerboard like image. Note how you can specify a DrawableBitmap as the destination for the drawing operation and how if you don't specify the destination for the drawing operation it will always draw to the current backbuffer of GraphicsEngine.
In the main method executeCodeThatUsesGameFrame() we just draw the DrawableBitmap to the backbuffer and then flip that backbuffer to be the frontbuffer and let the BasicExample's code wait for the 3 seconds. After that the example just exits.
Also a good thing to know is that every bitmap you create or load with GraphicsEngine is limited to the size of the current resolution you have specified. So if you are using resolution of 640x480 then the biggest Bitmap or DrawableBitmap you can have is of size 640x480.
2.2. Graphics example 2 (extends graphics example 1)
GraphicsExample2.java / AppletExample2.java - Expands on the graphics example 1 by loading two key colored bitmaps (two ghost images) that have a defined key color that is left transparent when the bitmaps are drawn to the backbuffer. Also demonstrates animation using the double buffering offered by the GraphicsEngine.
Again in the init() method we load the bitmaps, but this time around we use the loadAlphaBitmap() method that checks the first pixel of the bitmap (at coordinates 0,0) and uses that pixels color as the key color. If you take a look at the pinkGhost.bmp you will see that it has a white pixel at that location and a white background, so all that white background will be transparent when we now draw the Bitmap to the backbuffer in the executeCodeThatUsesGameFrame() method.
In the executeCodeThatUsesGameFrame() method we just have a for loop that is used to sweep the two ghosts from opposite sides to the other. As you can see we draw four pink ghosts and three wireframe ghosts to different y coordinates. At the end of the for loop we always flip() the current front- and backbuffer (which makes the backbuffer we just draw into visible). Note also that we don't have to clear the background as in the beginning of the for loop we draw the screen sized bitmap (that we made up in the graphics example 1) to the backbuffer which effectively erases the ghosts we draw the last time.
2.3. Graphics example 3 (extends graphics example 2)
GraphicsExample3.java / AppletExample3.java - Expands on the graphics example 2 and demonstrates how to load a font bitmap and draw some text to the backbuffer using either of the two methods available in GraphicsEngine.
The font bitmap is just a bitmap that has 16x16 array of equally sized cells that contain the different font letters. See the DefaultFontBitmap.bmp in the gameframe/images directory or the fadedFonts.bmp in the examples subdirectory resources to see how a font bitmap looks like. The placement of all the letters in a font bitmap is important as a simple indexing method based on the ASCII values of a letter in a String is used to retrieve the correct bitmap from the font bitmap. The size of a single letter is limited by the fact that no bitmap can be larger than the current resolution, so if e.g. you are using 640x480 resolution, the maximum size for a single letter is 640/16x480/16 which is 40x30 pixels. Note that the font bitmaps all letters must have equal size, otherwise it wont work as it should.
You must either load your own font bitmap or the default font bitmap (see below) before you try to draw text with the graphics engine. This is because the GraphicsEngine was designed to conserve memory and not load any font bitmaps by default.
In the init() method we simply load the font bitmap with the loadFontBitmap() method of GraphicsEngine class. Notice how if something goes wrong (e.g. the font bitmap can't be found) we use the default font bitmap by calling the loadDefaultFontBitmap() method.
In the beginning of the executeCodeThatUsesGameFrame() we use the getAsBitmap() method to get the given static text once as a DrawableBitmap that we can then use when drawing the text to the backbuffer. Then we get the description text of the currently used GameFrame implementation and turn it into an array of bytes that we then use later on (this is a bit faster than use the plain String when drawing the text) with the drawTextAt() method of GraphicsEngine. Then we just calculate some offsets, half screen heights and such to be able to calculate some beautiful way to move and draw the texts and the ghosts.
The logic of moving the ghosts is really simple and we use this same logic also later on. We just setup an array for both coordinates of ghosts (the current coordinates of the ghosts) and another set of arrays for the destination coordinates of the ghosts (the coordinates the ghosts are moving towards to). Then in the main loop we check each entry in the array (that means we check the current coordinates of one ghost against that ghosts destination coordinates) and manipulate the coordinates to move the ghost towards its destination. When a ghost arrives to its destination coordinates we randomly generate a new destination for it.
After handling the ghosts, we just draw the two texts (one from the array of bytes and the other from the bitmap) to the backbuffer and flip it to be visible.
2.4. Graphics example 4 (extends graphics example 3)
GraphicsExample4.java / AppletExample4.java - Expands on the graphics example 3 and demonstrates how to manipulate the loaded bitmaps with effects.
In the init() method we first get the input engine and the keyboard device, this allows us to wait until user presses ESC before exiting the application. It is not really the main point of this example, but you can see how this type of thing is done.
After that we create the two type of bitmap effects we are going to use and create an EffectGroup to hold both. It is usually beneficial to use the EffectGroup if you are going to apply several effects at a time as this allows you to apply all of them in one go (this means you don't create temporary Bitmap objects in the process).
The two nested loops just change the coloring of the pink ghost bitmap in NUM_COLORS different colors and rotate it 360 degrees in NUM_ROTATIONS steps. As a result we get a two dimensional array containing NUM_ROTATIONS columns and NUM_COLORS rows.
Then in executeCodeThatUsesGameFrame() we just use that 2D array to rotate the NUM_GHOSTS number of ghosts on the screen and change the color randomly after a ghost has rotated once a full 360 degrees. This is repeated until user presses the ESC key, which is read with the wasKeyDown() method as this doesn't require the key to be pressed at the exact moment we try to read it, but it is sufficient if the key has been down at some point of time after the last reading of the keys state..
Notice that the BitmapEffect in gameframe package is an abstract class and that you can actually write your own effect objects that apply you own effects to the bitmaps. Also notice how slow this process is, you should always try to apply your effects off-line (that means when the game is not actively running).
2.5. Graphics example 5 (extends graphics example 3)
GraphicsExample5.java / AppletExample5.java - Expands again on the graphics example 3 and demonstrates how to manipulate the loaded bitmaps by slicing and dicing them with the getSubBitmap() method.
In the init() method we first get the input engine and the keyboard device, this allows us again to wait until user presses ESC before exiting the application. Then we load a bitmap that contains 2x2 animation cells. This bitmap is then first split into upper and lower half and those halves are then again split into half to get the animation cells. Normally of course you would extract the animation cells directly from the original bitmap, but this example just shows that you can split already splitted bitmaps again.
From the gotten four bitmaps we build an array of bitmaps where we place the bitmaps in correct order.
In the executeCodeThatUsesGameFrame() we just then draw the animation cells one after another first from index 0 to 3 and then from 3 to 0 creating a ping-pong animation. The private drawFrame() method is just for drawing the screen full of animated bitmap evenly spaced.
2.6. Graphics example 6 (extends graphics example 3)
GraphicsExample6.java / AppletExample6.java - Expands again on the graphics example 3 and demonstrates how to manipulate the loaded bitmaps with your own custom BitmapEffect subclass.
In the init() method we first get the input engine and the keyboard device, this allows us again to wait until user presses ESC before exiting the application. Then we proceed to create the instance of the custom BitmapEffect subclass called VideoScreenEffect that we then use to process the pink ghost bitmap. The core of this example lies in the VideoScreenEffect code (at the end of the code listing). There you can see how the processData() method of the BitmapEffect subclass handles the pixels of the bitmap. The pixels are in the default Java color model of ARGB (means 8-bits of alpha, 8-bits of red, 8-bits of green and 8-bits of blue data per pixel) and can be thus easily handled. The pixel array you get is the same kind of array you use to create an bitmap with java.awt.image.MemoryImageSource class.
It should be noted that under DirectX implementations, the effect cloning involves loading the bitmap from file, processing it and then storing it to file again. This means that it is not recommended to use this feature of the library in highly interactive parts of your game (like in the main loop). It is meant to ease up the drawing of different sprite versions or to process e.g. background graphics to have different looks per level etc.
3.1. Input Example 1 (extends graphics example 2)
InputExample1.java - Expands on the graphics example and demonstrates how to read keyboard input.
In the init() method we get the InputEngine from GameFrame nothing special there, then we get the default keyboard device from the InputEngine. This method is the most useful as it returns the default keyboard in the case the system has many keyboard like devices attached. In the case there are several keyboard devices and you want to receive input from all of them (this scenario is very unlikely, but in theory possible) you can get an array of all keyboard devices with the getAllKeyboardDevices() method. It is notable that systems like Windows don't support reading several keyboards separately.
In the executeCodeThatUsesGameFrame() we just then use the wasKeyDown() method to check the ESC key press as this method returns true if the key has been down at some point of time since the last check. The isKeyDown() method requires that the key is held down at the exact moment we read its state, thus it is used in the if clauses that move the two ghosts.
3.2. Input Example 2 (extends input example 1 )
InputExample2.java - Expands on the input example 1 and demonstrates how to read mouse input.
In the init() method we get the default pointer (mouse) device from the InputEngine. Again this method is the most useful as it returns the default pointer device in the case the system has many pointer like devices attached. It is notable that system like Windows though they support plugging in several mice don't support reading several mouse devices separately.
In the executeCodeThatUsesGameFrame() we just then use the getX() and getY() methods to check the pointer location on the screen. Then we use the isButtonDown() method to check if user has pressed the primary mouse button (in the index 0) and if has we draw the pink ghost instead of the normal wireframe one. If we wanted to exit the program when user presses the second mouse button we could check it with wasButtonDown( 1 ) method call. Again the "was" version returns true even if the mouse button is not at the exact moment down, but has been since last read.
4.1 Sound example 1 (extends basic example)
SoundExample1.java / AppletSoundExample1.java - Expands on the basic example and demonstrates how to play a WAV sample.
In init() we just get the usual stuff: GraphicsEngine, InputEngine and SoundEngine. Then we proceed to get the keyboard and pointer devices, clear the back- and frontbuffers and load the default font bitmap so we can display text messages on the screen. We also setup the data directory for GraphicsEngine and SoundEngine. And finally load the explosion sample.
In executeCodeThatUsesGameFrame() we can then just show a message on the screen (Playing Explosion Sound Once) and then we simply play the sample by invoking its playOnce() method. It is notable, that there is no way to know if a sound (be it sample or music) has played completely or not so in this case we just wait the usual three seconds, before exiting.
4.2Sound example 2 (extends sound example 1)
SoundExample2.java - Expands on the sound example 1 and demonstrates how to load and play a music file looped.
In init() we just load the wanted music file.
In executeCodeThatUsesGameFrame() we show a message on the screen and then simply play the music by invoking its playLooped() method. Then we wait until user either presses ESC key to exit. If user presses SPACE key we wait until he releases it and then we play the explosion sound. This waiting of until SPACE key is released prevents the play() method of the sample being called "too" many times per second. It seems that you can crash the Java 2 runtime by making a tight loop (a while loop without thread sleeps) and calling the play() method of an AudioClip inside it repeatedly. We stop the music after the waiting loop just for being polite, the music would stop when we call the GameFrame.exit() method or if we finalize the SoundEngine.
4.3. Sound example 3 (extends sound example 2)
SoundExample3.java - Expands on the sound example 2 and demonstrates how to use the pan and volume settings of a sample. This example doesn't really work on Java 2 (JDK 1.2) platforms as Java 2 doesn't offer a way to control either volume or panning of a sound.
This time around we don't need a new init() method.
In executeCodeThatUsesGameFrame() we show a message on the screen and then setup the panning to full center (0.0F) and volume to full (1.0F) for the explosion sound. Then we simply poll the keyboard and adjust the pan and volume accordingly.
5.1. Timer example (extends graphics example 4)
TimerExample.java / AppletTimerExample - Expands on the graphics example 4 and demonstrates how to use the Timer class to measure the time one loop cycle takes.
In init() method we just create the timer.
In executeCodeThatUsesGameFrame() we use the same idea as in graphics example 4, but this time we have an array for holding the last NUM_TIMES_FOR_AVERAGE measured loop times. In each loop cycle we add the current loop time and calculate the average time one loop takes (this is in milliseconds). Then using this time we can calculate the average frames per second.
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).