In this conclusion to a three-part series covering the three different kinds of deployment frameworks you can use with Java games, you'll learn how to deploy multiple Java applets as if they were all one applet. This article is excerpted from chapter two of Advanced Java Game Programming, written by David Wallace Croft (Apress; ISBN: 1590591232).
The main class,MultiApplet, extends the Swing version of theApplet class,JApplet. It imports a number of other classes from the CroftSoft reusable code library. It implements theLifecycle interface with itsinit(),start(),stop(), anddestroy()methods so that it can be integrated into a genericLifecycle framework container.
public static final String DEFAULT_NEWS_NAME = "News";
//
private final String
appletInfo;
private final Pair [ ]
appletPairs;
private final String
newsName;
private final String
newsHTML;
private final String
newsPage;
The instance variableappletInfo is returned when thegetAppletInfo()method is called. VariablesDEFAULT_NEWS_NAME,newsName,newsHTML, andnewsPageare used to create an instance ofMultiAppletNews.
APairis a convenience class from packagecom.croftsoft.core.langthat I use to hold together a related pair ofStringobjects, usually a name-value pair. I like to use an array ofPairinstead of two separateStringarrays because then I do not have to worry about theString arrays accidentally being of unequal length. In theMultiApplet class, I use thenameto store the short name of a game applet to be displayed on a tab at the top of aJTabbedPane. Thevalueis the class name of the corresponding game applet.
private JTabbedPane jTabbedPane;
private Component appletComponent;
private boolean isStarted;
private int index;
private MultiAppletStub multiAppletStub;
ThisJAppletsubclass maintains an instance reference to aJTabbedPaneSwing component. TheJTabbedPaneis used to contain your game applets. When the user clicks a tab, a different game is loaded.
TheappletComponentis a reference to the game currently loaded. Note that it is an instance ofComponent, notApplet. The boolean flagisStartedprovides life-cycle state information. Theindexvariable indicates which tab is currently selected. TheMultiAppletStubinstance is used to propagate theAppletContextto the game applets.
if ( iconImage != null ) { jFrame.setIconImage ( iconImage ); } } catch ( Exception ex ) { }
The staticlaunch()method launches the program when it is not embedded in a web page as an applet. This allows it to be run as an executable JAR desktop application or as a Java Web Start application. It starts by creating a new frame and setting the frame icon image, that little picture at the top left corner of the frame. If it cannot find the image, it ignores it and moves on.
It then creates an instance of theMultiApplet and sets it as the content page for the frame. MethodFullScreenToggler.monitor(), described in Chapter 5, is used to allow the user to toggle between windowed and full screen mode. The static convenience methodlaunchFrameAsDesktopAppin classLifecycleWindowListenershows the frame and calls the appropriate lifecycle methods on theMultiAppletinstance when the user activates, deactivates, or closes the window.
Theinit()code makes theJTabbedPanefill the frame. The tabs run along the top and can be scrolled left and right when there are too many to fit in the width of the window. By using the scroll tab layout, you can have a large number of tabs, one for each game, without using up additional screen real estate.
jTabbedPane.add ( new MultiAppletNews ( newsHTML, newsPage, this ), newsName );
On the first tab, I put the special component calledMultiAppletNews. I treat this component differently from the others because it downloads a web page from my web site. I do not want it to be destroyed when a user clicks another tab because it would then have to download the web page again when the user comes back to it. For this reason, as documented in the following code, the code treats this particular panel differently from the others.
for ( int i = 0; i < appletPairs.length; i++ ) { jTabbedPane.add ( new JPanel ( ), appletPairs [ i ].name ); }
I then create all the other tabs as labeled with the short names of the games.
I add aChangeListenerto theJTabbedPaneso that I can tell when a user clicks a different tab. I end the initialization method by creating aMultiAppletStub instance.
Thestart()method ofMultiAppletdelegates to thestart()method of the currentappletComponentif it can determine that it is an instance ofAppletorLifecycle. Note that to be included withinMultiApplet, a game must extendComponentbut it does not have to extendAppletor implementLifecycle. An example of this might be a game where the updates are driven exclusively by user-input events such as mouse clicks instead of a continuously running animation thread.
Thestop()anddestroy()methods are similar. Thestart()andstop()methods mutate theactive state of theMultiAppletStub. These methods are usually called in response to windowing events such as window activated or deactivated, window minimized or maximized, or window closed.
If the user clicks a different tab and the currently selected game is running, thehandleStateChange()method stops it.
if ( index > 0 ) { jTabbedPane.setComponentAt ( index, new JPanel ( ) );
destroy ( );
appletComponent = null;
System.gc ( ); }
index = jTabbedPane.getSelectedIndex ( );
Anindexposition of zero indicates that the tab panel containingMultiAppletNewsis being displayed. Unless the currentindex position is zero, the currentappletComponentis destroyed and all references to it are removed. A garbage collection of system memory is forced at this point to reduce the chances that it will automatically run later and interrupt the animation of the newly selected game. Theindexposition is updated to point to the newly selected tab panel.
if ( index > 0 ) { try { appletComponent = ( Component ) Class.forName ( appletPairs [ index - 1 ].value ).newInstance ( );
If the newindexposition is not zero, the game is loaded into memory using dynamic linking.
If theappletComponentis an instance ofApplet, theAppletStubis set. If it is an instance ofJComponent, we can monitor it for keyboard events that toggle full screen mode. All the dynamically loaded classes must at least extendComponentso that they can be added to theJTabbedPane.
Theinit()method of the newappletComponentinstance is followed by a call to thestart()method of theMultiApplet. Note that theinit()method of the MultiAppletinitializes theMultiAppletbut itsstart()method initializes theappletComponent.