Subscribe

RSS Feed (xml)

Powered By

Powered by Blogger

Google
 
xnahelp.blogspot.com

Jumat, 04 April 2008

Managing Game States

When we think about our overall game design we should consider the different states our
game will be in. When we made the SimpleGame in Chapter 11, we just used an enumerated
type that we could pick from to change our game state. This was sufficient for that
game, although the method we are going to discuss now would still fit it well without
adding a lot of complexity to the game code.
A common question from those starting out making games is this: “How should I structure
my game code?” This is a valid question with many different answers, each having
its own merit. As with every other aspect of programming, tasks can be in completed
many different ways. For this discussion, we use a structure that is not very hard to implement
and yields some very nice results in terms of flexibility.
Consider the following as a concrete example of the problem we are trying to solve:
1. Game is loaded and the start or title screen is displayed.
2. Player presses A or Start so the start or title screen is removed and the main menu
screen appears.
3. Player selects single player game, the main menu is removed, and a submenu is
displayed.
4. The game is a trial game, so a warning message is also displayed prompting the
gamer to purchase the game or continue with limited play. The message is displayed
on top of our submenu.
Managing Game States 309
15
5. The player accepts the message and the submenu no longer has the message
obstructing the view.
6. The player selects Quick Game from the Single Player menu. The Single Player menu
is removed and we start the level by displaying a start level loading screen.
7. When the level finishes loading, the start level screen is removed and we load up
our game’s scene.
8. The player pauses the game and we bring up a paused screen that overlays our
paused game play, which is a little blurred in the background.
Most of those state changes could be done with a simple enumerated type, but there are a
couple of situations where a simple enumerated type is not sufficient, such as when we
display our message in a modal dialog box that does not allow the gamer to continue
until he or she closes the box. However, we do not want to lose sight of the state we were
just in. We might use this same message dialog box at other times in our game for a tutorial
or hints, and so on. Another example is when the gamer pauses the game. We could
just switch our current state with our paused state and then return to our playing state
when the pause button is toggled by the gamer, but then we could not display our
current game’s scene in a blurred (or grayscaled) manner behind our pause screen.
We are going to create a method that will allow us to handle both of those situations and
give us the flexibility of having access to the different states from within each state. We
are going to create a game state manager class that will control a stack of our different
game states. Instead of just switching between states, we are going to implement a stack.
WHAT IS A STACK?
The typical real-world example computer scientists use to explain a stack is a stack of
plates. We push and pop items on and off of the stack. In our plates example, we
push a plate onto the top of the stack. We can then pop a plate off the top of a stack
(but we need to be careful so we don’t break it). It is a last in, first out (LIFO) method
of processing. We do need to make sure we do not try to pop off a plate if the stack is
empty, but that is easy enough to handle.
We are going to use a stack so we can easily handle situations like adding a pause menu
on top of our currently playing scene state. This way we can continue to draw our scene
even though we are paused. We do not want to update our scene, though, as our enemies
would keep coming for us, our timer would continue to count down, and so on. So when
we pause, we really do want to pause our game play, but we still want to draw our game
scene in its paused state. In fact, we might want to use some of the postprocessing techniques
we learned about to blur out, turn gray, or change our scene some other way when
we are in a paused state. By using a stack we can accomplish any of those things.
Using a stack for game state management is also beneficial when trying to handle things
like dialog boxes, as it effectively pauses the game to give players a hint, tell them the
demo’s time is up, and so on. Another benefit is multiple menus. We can push a menu
310 CHAPTER 15 Finite State Machines and Game State Management
state on top of our game play if the user backs out (or pauses) and then offer an options
menu and sound options or controller options under that. With a stack we have the flexibility
to leave our previous menus displayed, or have our screen replace them. With a
simple switch statement on an enumerated type this would not be possible.
Fortunately, with all of this flexibility we do not increase the complexity of our code very
much. The principle is an easy one to grasp. We are going to have a game state manager
that will manage the different states in our game. It will be implementing a stack to hold
our states. Each state will contain logic that determines what happens next. The states
themselves will drive the game flow.
Each state will inherit from a base game state abstract class. This abstract class will inherit
from the DrawableGameComponent class. Each game state that inherits from our abstract
state will be a game service. There are two reasons for this. One is that we want our state
objects to be singleton objects because we do not want more than one instance of our
state created. The second reason we are making it into a game service is because our game
state manager will need access to the states.
Our game state manager also inherits from GameComponent because it is a game service
that our states need a reference to. The game state manager will include an
OnStateChange event. Normally, other objects would register for an event like this.
Instead, we are going to expose the event handler in our game states and have our game
manager manage the event registration process for our game states. The exposed event
handler in our game states will be called StateChanged.
This StateChanged method in our game state class can be overridden but by default it will
simply check to see if the current state of the game is itself. If it is, it will set its Enable
and Visible properties to true; otherwise it will set them to false. So by default, when a
state is loaded, but not at the top of the stack, it will not update itself nor will it draw
itself. All active game states will have this event handler executed whenever our game
changes state. Each state could also search the stack to see if it contains some other state
and if so do some different processing. Because our StateChanged event handler will be
protected, the actual game states can implement any functionality they want to as the
game state changes. We can see that this system is pretty flexible.
Because we want our objects to use the singleton pattern and we want our states to be
game services so each state can access other states if needed, we can combine those
two requirements because the game service collection can only have one object in its
collection with a particular interface. Therefore each one of our states will need its
own interface, but we saw earlier that this can be a blank interface. In our situation, we
are going to have an IGameState interface, but then each one of our states will have its
own interface that implements the IGameState interface.
We need to be able to make comparisons between our game states. Mainly, we need to
make comparisons between our game manager’s current state and a particular game state.
Enumerated types obviously lend themselves to comparisons easily, but what about actual
game state objects? Fortunately, we can just as easily compare our object references and
this is why it is important that there is only one instance of the object—so our reference
is always the same. We will create a property called Value, which is of type GameState.
This property allows us to perform the comparisons that we need.

0 komentar: