Table of ContentsThe Application ClassThe Splash Screen

The Menus

Every game needs a menu system, even if it only contains options to start a new game and exit. For Star Assault you'll have quite a complex set of menus to let the player start or resume a game, configure available options, change the key setup, and view credits and information on how to play. Figure 13.4 shows all these options.

Figure 13.4. The menu structure for Star Assault

graphic/13fig04.gif


The easiest way to create a menu is to derive a class from the LCDUI List and add items for each menu option. Figure 13.5 shows an example of this.

Figure 13.5. The resulting menu using a List object

graphic/13fig05.gif


When the user selects an option from the list, you simply undertake the action that corresponds to it. However, for each of the different menus you need to create another class derived from List. Seems like a bit of a waste, doesn't it? For Star Assault you'll have as many as five different menus, all of which are pretty much the same thingjust a list of options. Maybe there's a better way.

To solve this problem you can use a special menu class that can dynamically change its items and responses to selections. You can display any number of menus and items, all using the same class.

To manage the menus, the DynaMenu class (what a cool name, you're thinking) starts by defining a constant for all the menus.

public class DynaMenu extends List implements CommandListener
{
    // Values have large increments so you can fit in more entries without
    // renumbering everything.
    private static final int MAIN_MENU = 100;
    private static final int SETTINGS_MENU = 110;
    private static final int AUTOFIRE_TOGGLE_MENU = 111;
    private static final int VIBRATE_TOGGLE_MENU = 112;
    private static final int STARFIELD_TOGGLE_MENU = 113;
    private static final int KEYSETUP_MENU = 120;
    private static final int LEFTKEY_MENU = 121;
    private static final int RIGHTKEY_MENU = 122;
    private static final int FIREKEY_MENU = 123;
    private static final int CONFIRM_NEW_GAME_MENU = 130;
    private int currentMenu; // tracks which menu is current being displayed.

I've also added an integer field to keep track of which menu is currently being displayed.

To populate the list you use the List class' addElement method. Here's how to do this for the main menu:

public void goMainMenu()
{
    boolean savedLevel = World.getSavedLevel();

    Vector items = new Vector();
    items.addElement("New game");
    if (StarAssault.getApp().hasAGameRunning() || savedLevel)
        items.addElement("Resume game");
    items.addElement("Settings");
    items.addElement("Change keys");
    items.addElement("Help");
    items.addElement("About");

    String[] s = new String[items.size()];
    for (int i = 0; i < items.size(); i++)
        s[i] = (String) items.elementAt(i);

You'll see the setupMenu method later in this section.

    setupMenu("Main Menu", s, "Exit");

    currentMenu = MAIN_MENU;
}

You then need to call this from the constructor to switch to the main menu by default.

public DynaMenu()
{
    // Use an explicit list as it's the best type to reflect a menu (a simple
    // of items you can select from).
    super("", IMPLICIT);
    goMainMenu();
    setCommandListener(this);
}

This code also checks to see whether a saved game currently exists or whether a game is in progress. If it is, then you add the Resume Game option as well. The setupMenu method does the hard work of populating the list.

First you clear any items currently displayed, and then you add an item on the list for every entry in the string array. If the backCommandName is anything other than null,you add a back command using that name. This way you can have Exit on the main menu and Back on the internal menus.

public void setupMenu(String title, String[] choices, String backCommandName)
{
    // Set the title of the menu.
    setTitle(title);

    // clear the current list & command
    int s = size();
    for (int i = 0; i < s; i++)
        delete(0);
    removeCommand(back);

    // add the new choices
    for (int i = 0; i < choices.length; i++)
        append(choices[i], null);

    // add the back command, if they wanted one
    if (backCommandName != null)
    {
        back = new Command(backCommandName, Command.BACK, 1);
        addCommand(back);
    }
}

Using this code you can now add setup methods for the other menus.

public void goSettingsMenu()
{
    setupMenu("Settings", new String[]{"Auto fire", "Shake", "Star field"},
               "Back");
    currentMenu = SETTINGS_MENU;
}

public void goKeySetupMenu()
{
    setupMenu("Key Setup", new String[]{"Left", "Right", "Fire"}, "Back");
    currentMenu = KEYSETUP_MENU;
}

public void goKeyNumMenu(String title, int menuId, int currentNum)
{
    setupMenu(title, new String[]{"Num-0", "Num-1", "Num-2", "Num-3", "Num-4",
                                    "Num-5", "Num-6", "Num-7", "Num-8", "Num-9"}, null);
    // Set the selected item on the menu to match the currently configured key.
    setSelectedIndex(currentNum, true);

    currentMenu = menuId;
}

When you select an option related to a setting, you need to present two options (either yes and no or on and off). To handle all of these cases, you add a couple of generic menus.

public void goToggleMenu(String title, int menuId, boolean on)
{
    setupMenu(title, new String[]{"On", "Off"}, null);
    if (on)
        setSelectedIndex(0, true);
    else
        setSelectedIndex(1, true);

    currentMenu = menuId;
}

public void goYesNoMenu(String title, int menuId, boolean on)
{
    setupMenu(title, new String[]{"Yes", "No"}, null);
    if (on)
        setSelectedIndex(0, true);
    else
        setSelectedIndex(1, true);

    currentMenu = menuId;
}

All right, the real action in the DynaMenu class is where you respond to commands and switch to different menus. At first glance this might look a little complicated, but it's mostly just a big switch statement to handle selections. As a result of each selection, you either enact the results or switch to another menu.

public void commandAction(Command command, Displayable displayable)
{
    // If they hit the back key you need to step back from the current
    // menu to a prior one in the hierarchy.
    if (command == back)
    {
        if (currentMenu == SETTINGS_MENU) goMainMenu();
        if (currentMenu == KEYSETUP_MENU) goSettingsMenu();
    }
    // Execute an item on one of the menus.
    if (command == List.SELECT_COMMAND)
    {
        String selected = getString(getSelectedIndex());
        switch (currentMenu)
        {
        // All the options for the main menu.
        case MAIN_MENU:
            if (selected.equals("New game"))
            {
                // if they already have a game running then confirm that
                // they want to create a new game
                if (StarAssault.getApp().hasAGameRunning())
                {
                    goYesNoMenu("Are you sure?", CONFIRM_NEW_GAME_MENU, false);
                }
                else
                    StarAssault.getApp().createNewGame();
            }
            if (selected.equals("Resume game"))
            {
                // Check to see if they already have a game running, if they
                // do then resume that game by reactivating the game screen.
                // Otherwise you load the game from the RMS.
                if (StarAssault.getApp().hasAGameRunning())
                    StarAssault.getApp().activateGameScreen();
                else
                    StarAssault.getApp().loadGame();
            }
            if (selected.equals("Settings"))
                goSettingsMenu();
            if (selected.equals("Change keys"))
                goKeySetupMenu();
            if (selected.equals("Help"))
            {
                StarAssault.activateDisplayable(
                        new TextForm(this,
                                "Star Command Needs You! \nFight your way through " +
                                "the enemy defences to the warp tunnel exit on each
                                level." +
                                " Use 4 and 6 to turn and 5 to fire. "));
            }
            if (selected.equals("About"))
            {
                StarAssault.activateDisplayable(
                        new TextForm(this,
                                "StarAssault (C)opyright 2003, Martin J. Wells. All
                                rights reserved."));
            }
            break;

        case CONFIRM_NEW_GAME_MENU:
            if (getBool(selected))
                StarAssault.getApp().createNewGame();
            break;

            // SETTINGS...
        case SETTINGS_MENU:
            if (selected.equals("Auto fire"))
                goToggleMenu("Auto fire", AUTOFIRE_TOGGLE_MENU,
StarAssault.getApp().isOptionAutoFire());
            if (selected.equals("Shake"))
                goToggleMenu("Shake", VIBRATE_TOGGLE_MENU, StarAssault.getApp().
isOptionVibrate());
            if (selected.equals("Star field"))
                goToggleMenu("Star field", STARFIELD_TOGGLE_MENU,
StarAssault.getApp().isOptionStarField());
            break;

        case AUTOFIRE_TOGGLE_MENU:
            StarAssault.getApp().setOptionAutoFire(getBool(selected));
            StarAssault.getApp().saveSettings();
            goSettingsMenu();
            break;
        case STARFIELD_TOGGLE_MENU:
            StarAssault.getApp().setOptionStarField(getBool(selected));
            StarAssault.getApp().saveSettings();
            goSettingsMenu();
            break;
        case VIBRATE_TOGGLE_MENU:
            StarAssault.getApp().setOptionVibrate(getBool(selected));
            StarAssault.getApp().saveSettings();
            goSettingsMenu();
            break;

            // KEYS...
        case KEYSETUP_MENU:
            if (selected.equals("Left"))
                goKeyNumMenu("Left Key", LEFTKEY_MENU, StarAssault.getApp().getLeft
                KeyNum());
            if (selected.equals("Right"))
                goKeyNumMenu("Right Key", RIGHTKEY_MENU,
                StarAssault.getApp().getRightKeyNum());
            if (selected.equals("Fire"))
                goKeyNumMenu("Fire Key", FIREKEY_MENU, StarAssault.getApp().
                getFireKeyNum());
            break;

        case LEFTKEY_MENU:
            StarAssault.getApp().setLeftKeyNum(getKeyNum(selected));
            StarAssault.getApp().saveSettings();
            goKeySetupMenu();
            break;

        case RIGHTKEY_MENU:
            StarAssault.getApp().setRightKeyNum(getKeyNum(selected));
            StarAssault.getApp().saveSettings();
            goKeySetupMenu();
            break;

        case FIREKEY_MENU:
            StarAssault.getApp().setFireKeyNum(getKeyNum(selected));
            StarAssault.getApp().saveSettings();
            goKeySetupMenu();
            break;
        }
    }
}

The getBool method converts a string value like On/Off, Enable/Disable, and Yes/No to an equivalent boolean value. This is used in the menu items above when configuring options.

public static final boolean getBool(String s)
{
    if (s.equals("On")) return true;
    if (s.equals("Enable")) return true;
    if (s.equals("Yes")) return true;
    return false;
}

The getKeyNum method is another quick utility method used by the menu code to convert between a key number choice and an integer value.

public static final int getKeyNum(String s)
{
    String n = s.substring(4, 5);
    return Integer.parseInt(n);
}

In this example I'm also using a class called TextForm.This class lets you display a block of text on the screen and then return to the menuperfect for your Help and About screens. Since this has to be an independent displayable, you need to use a Form object.

public class TextForm extends Form implements CommandListener
{
    private Command back;
    private Screen home;

    public TextForm(Screen homeArg, String displayText)
    {
        // call the Form constructor
        super("");
        home = homeArg;
        append(displayText);

        // add the exit
        back = new Command("Back", Command.BACK, 1);
        addCommand(back);
        setCommandListener(this);
    }

    public void commandAction(Command command, Displayable displayable)
    {
        if (command == back)
            StarAssault.activateDisplayable(home);
    }
}

    Table of ContentsThe Application ClassThe Splash Screen