Last 10%

You'll need to do lots of things to polish a game or even a demo to make it more professional-looking. This includes adding things such as extra special effects, loading screens, menus, and intro sequences. One of the most important things is to keep your game as robust and well–behaved as possible. Make sure the game feels "solid" and doesn't have any quirky behavior. Eliminate all the bugs you can. If the user minimizes the app or switches to another app, you'll want to pause your game. Also, when the user clicks Exit, the game should actually exit, not just hide the game window and continue to hog the processor or network connection in the background. Try to leave the user's computer in the state it was in when the game started.

Effects

Don't forget about adding plenty of sound effects. Sound effects are one of those game elements that are sometimes noticeable only when they are missing. Here are some ideas on when to add sounds need for something like an adventure game:

  • When the player is hurt or dies
  • When the player jumps and falls
  • When a bot is hit or destroyed
  • When a lever is pulled or a door opens
  • When an item is picked up
  • When a background generator is on or another piece of machinery is buzzing or humming
  • For background environment noises, such as a brook gurgling, flames crackling, wind rustling through trees, birds chirping, ocean waves crashing, or rain drops spilling
  • For user interface sounds, such as when the user clicks a button, changes to the map view, or selects a different weapon, or when the UI is waiting for input
  • For "hint" sounds, such as when a bad guy is about to fire a weapon or the player is near a hidden cave
  • For negative feedback sounds, such as when the player tries to select a weapon that isn't available

Of course, these are ideas. You can come up with your own checklist of sounds you'd like in your game. Besides sound effects, visual effects really help refine a game and can also add a bit of the "wow" factor. Some visual effects you might want to add are listed here:

  • Particle explosions
  • Flames
  • Heat warping effects
  • Lightning
  • Dramatic flickering lights
  • Environment effects, such as rain, snow, and moving clouds
  • Object animations, such as running creatures and growing vines

It might sound crazy, but be sure not to add too many special effects. Effects should be fun and cool, but they shouldn't get in the way of game play. For example, you don't want so many simultaneous sounds that they are difficult to distinguish. You'll also want to avoid graphical effects that cover the entire screen, confusing or disorienting the user for no reason (unless that's the point of the effect!).

The Game State Machine

In most of the demos in the tutorial, the games started directly in the "game" mode and stayed there until they exited. There were no loading screens, intro sequences, menus, or help screens, all of which are be essential in making a mature game. When you want to add these different screens, you can treat the entire game as a state machine, as in Screenshot.

Screenshot A game state machine.

Java graphics 18fig01.gif


In this simple state machine example, the game starts with a loading screen and moves to an intro sequence (which could be a splash screen, an animation, or a demo of the game). Typically, you want to allow users to escape from any intro sequences like this so they don't have to sit through them every time they start the game. The next state is the main menu, and from there, the user can move to many of the other games states, such as help screens or setup. This is just a simple design. For your own game, you'll probably want something more detailed. For example, the main game state shown here could even be broken down into several different states: The game state could go from a level loading sequence to a level intro, to the game, and then to the level end sequence. Implementing state machines is easy enough, and you can do it similar to how you wrote other state machines in this tutorial. For example, you could do something like this when you handle input for a game:

if (state == STATE_INTRO) {
 // handle intro input
}
else if (state == STATE_GAME) {
 // handle game input
}
else {
 ...
}


Furthermore, you need these if/then statements when you update and draw each frame. As you can imagine, this code can quickly become tedious and confusing as more states are added to the game. Instead, you can just give each state its own object that handles things such as input, updates, and drawing, avoiding long if/then chunks of code. Start by making the GameState interface, shown here in Listing 18.1.

Listing 18.1 GameState.java
public interface GameState {
 /**
 Gets the name of this state. Used for
 the checkForStateChange() method.
 */
 public String getName();
 /**
 Returns the name of a state to change to if this state is
 ready to change to another state, or null otherwise.
 */
 public String checkForStateChange();
 /**
 Loads any resources for this state. This method is called
 in a background thread before any GameStates are set.
 */
 public void loadResources(ResourceManager resourceManager);
 /**
 Initializes this state and sets up the input manager
 */
 public void start(InputManager inputManager);
 /**
 Performs any actions needed to stop this state.
 */
 public void stop();
 /**
 Updates world, handles input.
 */
 public void update(long elapsedTime);
 /**
 Draws to the screen.
 */
 public void draw(Graphics2D g);
}


This is a fairly simple interface. The start() method should map any key and mouse bindings for this game state. The stop() method performs any cleanup when a state is done, such as pausing sounds or freeing resources. The update() and draw() methods update and draw each frame. checkForStateChange() returns the name of a state to change to, or null if it's not ready for a state change. For example, this method could return the string Main after the intro sequence is finished. One thing to note is that the loadResources() method is designed to be called in a separate thread outside the main thread. This way, resources can be loaded separately from the update/draw process. This also means a "loading" screen can easily be shown while all the resources are loading in the background. Ideally, you'll probably want this method to throw an exception if there is an error loading a resource so that the game engine can respond accordingly. The entire life cycle of a GameState is shown visually in Screenshot.

Screenshot The flow of a GameState object.

Java graphics 18fig02.gif


As an example, you'll create a GameStateManager class that manages the change from state to state and keeps track of the current state. Check it out in Listing 18.2.

Listing 18.2 Relevant Methods of GameStateManager.java
/**
 Sets the current state (by name).
*/
public void setState(String name) {
 // clean up old state
 if (currentState != null) {
 currentState.stop();
 }
 inputManager.clearAllMaps();
 // set new state
 currentState = (GameState)gameStates.get(name);
 if (currentState != null) {
 currentState.start(inputManager);
 }
}
/**
 Updates world, handles input.
*/
public void update(long elapsedTime) {
 // if no state, pause a short time
 if (currentState == null) {
 try {
 Thread.sleep(100);
 }
 catch (InterruptedException ex) { }
 }
 else {
 String nextState = currentState.checkForStateChange();
 if (nextState != null) {
 setState(nextState);
 }
 else {
 currentState.update(elapsedTime);
 }
 }
}
/**
 Draws to the screen.
*/
public void draw(Graphics2D g) {
 if (currentState != null) {
 currentState.draw(g);
 }
 else {
 // if no state, draw the default image to the screen
 g.drawImage(defaultImage, 0, 0, null);
 }
}


In this code, the draw() method delegates the work to the current state; if the state is null, a default image is drawn. You could make the default image be a splash image that says the game is loading. Besides updating the state, the update() method checks to see whether the current state signals that it is time to change to another state. Finally, the setState() method actually switches the states. Notice that all the key and mouse maps are cleared from the input manager when changing to a different state. As an example, you'll create a simple splash screen game state, shown here in Listing 18.3. This GameState just keeps a splash image on the screen either for three seconds or until the user presses the spacebar, whichever comes first.

Listing 18.3 SplashGameState.java
public class SplashGameState implements GameState {
 private String splashFilename;
 private Image splash;
 private GameAction exitSplash;
 private long totalElapsedTime;
 private boolean done;
 public SplashGameState(String splashFilename) {
 exitSplash = new GameAction("exitSplash",
 GameAction.DETECT_INITAL_PRESS_ONLY);
 this.splashFilename = splashFilename;
 }
 public String getName() {
 return "Splash";
 }
 public void loadResources(ResourceManager resourceManager) {
 splash = resourceManager.loadImage(splashFilename);
 }
 public String checkForStateChange() {
 return done?"Main":null;
 }
 public void start(InputManager inputManager) {
 inputManager.mapToKey(exitSplash, KeyEvent.VK_SPACE);
 inputManager.mapToMouse(exitSplash,
 InputManager.MOUSE_BUTTON_1);
 totalElapsedTime = 0;
 done = false;
 }
 public void stop() {
 // do nothing
 }
 public void update(long elapsedTime) {
 totalElapsedTime+=elapsedTime;
 if (totalElapsedTime > 3000 || exitSplash.isPressed()) {
 done = true;
 }
 }
 public void draw(Graphics2D g) {
 g.drawImage(splash, 0, 0, null);
 }
}


When this game state starts, it maps the spacebar and main mouse button to the exitSplash game action. The state signals to change to the main state after it is done. Now it will be a lot easier to create different game states. Be sure to create everything you need, including setup and help screens.

Screenshot


   
Comments