While our framework borrows from some design patterns, we feel that games are a very expressive medium. We want you, the reader of this book, to think outside that proverbial, over-used box. While design pattern ideas lend them selves to all manner of application development, we don't want you to feel hemmed in to using any one set of ideas for creating their games. Our framework gives structure to the overall flow of a game but does not dictate how to code the actual game logic.
Exploring the Framework
The game framework is housed in a reusable package structure. A package structure at its very basic is an organized set of folders that will contain reusable classes. All of the standard class files for using the framwork will be in this package structure. We will update and extend this structure as we proceed through this book. Let's start by giving you an idea of what classes we will be creating for the framework.
The GameFrameWork.as class
The GameFrameWork.as class is the foundation of the framework. It contains a simple state machine and a basic game loop timer. Our state machine is a simple construct containing a set of state constants to switch the framework between states. States are handled by state functions inside the GameFrameWork.as file. We will employ a variable to reference the current state function. The game timer will use this refernce to call the current state function on each frame tick. We will create states for such things as the title screen, instructions screen, game play, game over, and more. When we create a new game our document class will be an instance of GameFrameWork called Main.as. This Main.as will have an init (short for "initialization") function that will set up the framework for use. This Main.as class will also act as a sort of message gateway between the game and the framework classes. If you are confused, don't worry; we'll be explaining this in greater detail soon.
The FrameWorkStates.as class
This FrameWorkStates.as class will simply contain a set of constant integer values representing the state machine states. When we first create the framework we will have 10 different state constants. All will begin with the STATE_SYSTEM designation. For example, we will have a state that displays the title screen. The state constant for this will be STATE_SYSTEM_TITLE. More states will be added as we progress through the chapters in this book.
The BasicScreen class and SimpleBlitButton helper class
The BasicScreen class will be used to display very simple game state screens. These screens can have a basic background color with transparency (if needed), as well as some text centered on the screen. Each screen can also have a button that needs to be clicked to move to a new state in the state machine. This class is so simple that it will probably never be used without major modification in your own commercial games. We present it here, in its very simple form, for utility purposes. The SimpleBlitButton helper class is used to create a clickable button with rollover and off states. The button is created completely in code with a simple color background for the over and off states to be sed in the BasicScreen class. We do this for simplicity and to demonstrate some Sprite blitting techniques (much more on these later in the book) using a set of BitmapData instances for the background color change of the button on rollover.
The ScoreBoard class and SideBySideScoreElement helper class
The ScoreBoard class displays information for the user during game play. Data such as the current game score, time remaining, and other useful information can be displayed to the user with a very basic look and feel by using this class. It can be used as is, but when you start to make your own commercial games, you might find it useful to extend this class with more functionality. This very basic class is used inside the framework to demonstrate how to use events from a Game instance to update a game score board. The SideBySideScoreElement helper class is used to display a text label and corresponding dynamic text field as a pair side by side on the screen. For example, it can be implemented by the ScoreBoard class to display the word Score followed by the user's current score.
The Game class
The Game class is a stub style class that all games in the framework will inherit from. It contains the basic variables and functions needed to work with the GameFrameWork class.
The Custom event classes
The framework makes use of events to communicate between classes. The instances of the Game class will use events to communicate with the ScoreBoard and the Main classes. We will create a custom Main.as class for each game. This Main class will be a sub-class (or child) of the GameFrameWork class. Some of the events we will use will be simple events. These are instances of the standard Flash Event class used for basic communication. By this, we mean events that don't need to send any data along with them. For example, instances of the Game.as framework class will have a constant variable called GAME_OVER. This constant will be used as the name of a simple Event instance that is fired off with the standard dispatchEvent function call. This example GAME_OVER event wil be used to tell the Main.as to move to the game over (STATE_SYSTEM_GAME_OVER) state in the main state loop when the current game has completed. We will also be creating three distinct custom event classes as part of the framework. These will be used to send specific data from the Game class instance to the Main.as (GameFrameWork.as instance) class. The Main.as will act on the data sent and if needed, pass the data to other framework classes. By doing this, we are using Main.as as a sort of message gateway.
The CustomEventButtonId.as class
This custom event will have the ability to pass an identification integer value to the listening function. It is used for cases where multiple buttons share the same listener function. It will be used in the framework (specifically in the GameFrameWork instance, Main.as) to allow BasicScreen instances to each share the same listener functions if they employ a SimpleBlitButton. You will see this when we examine the GameFrameWork class file. It can also be used for any game or application that needs to send a basic integer id along with an Event instance.
The CustomEventLevelScreenUpdate.as class
This custom event will be used by the Game.as class instance to send data to a BasicScreen instance we will create called levelInScreen. The levelInScreen will have the ability to display custom text between each level. The event listnener for this event will change this text with a value passed when the event is fired off. The Main.as (GameFrameWork.as sub-class) will listen for this event and pass the data to the levelInScreen.
The CustomEventScoreBoardUpdate.as class
This custom event will be used by the the Game.as class instance to update the values on the ScoreBoard class instance. The Main.as class will listen to for this event and pass the data on to the ScoreBoard.as class instance.
The framework package structure
We will be organizing all of the code we will create into a package structure. The reusable framework classes will be in one package, and the games we will create will be in a separate package. Let's to create these two package structures now.
The source folder
Choose a spot on your disk drive of choice, and create a folder called source. This will be the root or base level for all of the code and games we will create in this book.
The classes package
The reusable framework classes will be in a package called classes. Create a folder called classes inside the source folder from the previous step. You should now have a folder structure that looks a little like this: [source] [classes] Next, we will create the actual package that will contain all of the source code for the reusable framework classes. We will name this package com.efg.framework. To create this, you must first create a folder inside the classes folder called com, then a folder called efg inside the com folder, and finally a framework folder inside the efg folder. By the way, the "efg" is an abbreviation for the book title, Essential Flash Games. You should now have a folder structure that looks like this: [source] [classes] [com] [efg] [framework] When we start to create all of the class files necessary for the framework, they will all go into the framework folder. You will see that when we create these classes, the package name will look like this: package com.efg.framework {
The projects package
The second package we will create is called the projects package. You can start this right away by creating a folder inside the root source folder called projects. The projects folder is not going to be a straight package structure like the classes folder. It is organized in a manner to allow individual custom game development using the framework. Inside this projects folder, we are going to create a unique folder for each game in the book. The first game we are going to create is called stubgame. A stub is usually a function or class that contains very little (if any) usable code but is instead a simple placeholder. Our game will be slightly more than a placeholder but not much more. It will be used to demonstrate the basic functionality of the framework. Go ahead and create a folder called stubgame in the projects folder. You should have a projects set of folders that look like this:
[source] [classes] ... [projects] [stubgame] Next, we are going to create two folders, each to hold a different version of our game. Why are we going to do this? This book is meant to support Flash game development with a variety of tools. There are many popular methods to create Flash games with an assortment of tools and code integrated development environments (IDEs). We are going to focus on two such tools in this book: the Flash IDE (the one with the library, timelines, drawing tools, and so on all combined into a single tool) and Flash Develop (a very popular, free IDE made specifically for ActionScript development). You can use any tool with this book, but you will need to follow the documentation specific to your own tool when setting up projects. You will need to pay careful attention to linking the reusable code package structure to your games, because linking may vary depending on the Flash code editor environment you are using. Linking the package to a game is actually a very simple process, but it differs between the various code IDE versions. Jeff does most of his Flash game development Flash Develop using the free Flex SDK that Adobe provides. Steve, on the other-hand, uses the Flash IDE almost exclusively. We have combined our efforts on all of the chapter games to bring you code that will work with both a Flex SDK project and a Flash IDE project.
On that note, the next two folders you will create inside the stubgame folder are called flashIDE and flexSDK. You don't have to create both for any project. You just need to create the one that works with the tools you are going to use to create Flash games. Each is set up differently, so pay attention to the specifics of the one you will be using the most.
You should now have a projects folder that looks like this:
[projects] [stubgame] [flashIDE] [flexSDK]
The Flash IDE package structure
The Flash IDE package structure begins right inside the flashIDE folder. The package name is very similar to the classes package you saw in the last section. The package structure will be com.efg.games.[game name]. For instance, with the stub game we are going to create in this chapter, the package name will be com.efg.games.stubgame. Go ahead and create those folders now. You should have this package structure when you are complete: [projects] [stubgame] [flashIDE] [com] [efg] [games] [stubgame] [flexSDK]
The Flex SDK package structure
The Flex SDK package structure is very similar to the Flash IDE package structure with one small difference. Flash Develop and other Flex tools use a specific set of folders for organizing their own project structures. To accommodate these and still have a common package structure for our games, we must add the Flash Develop created src folder to the flexSDK folder. You will not have to create the src folder or the package structure by hand, as Flash Develop will create it automatically for you when you start a new project. In the section called "Setting up the game in Flash Develop," we will go into the details. For now, here is the way the structure will be laid out (including the Flash Develop specific folders such as bin, obj, and lib. If you have used Flash Develop to create a Flex SDK project, you will recognize the following structure: [projects] [stubgame] [flexSDK] [bin] [obj] [lib] [src] [com] [efg] [games] [stubgame] Notice that we have created the exact same package structure inside the src folder as we will use with the Flash IDE. The package name for our game will be com.efg.games.stubgame. The package name in the code for classes in both the Flash IDE and Flex SDK will be the same:
package com.efg.games.stubgame {
The Main.as and StubGame.as files
When we start to add files to the subgame package we will be creating two subclasses (or children) of framework classes that will be unique to our game. The Main.as will be created as a subclass (or child) of GameFrameWork.as framework class. The StubGame.as class will be a subclass (or child) of the Game.as framework class.
Starting a project using the framework packages
You have just seen the basic package structure for both the framework reusable classes and the projects we are going to create. Let's make use of this right away by creating a project for the stub game. The stub game will be very similar to the Chapter 1 game where the player is tasked with clicking the mouse ten times.
Creating the stub game project in the Flash IDE
Follow these steps to set up stub game using the Flash IDE:
- Start up your version of Flash. I am using CS3, but this will work exactly the same in CS4 and CS5.
- Create a .fla file in the /source/projects/stubgame/flashIDE/ folder called stubgame.
- In the /source/projects/stubgame/flashIDE/ folder, create the following package structure for your game: /com/efg/games/stubgame/
- Set the frame rate of the Flash movie to 30 FPS. Set the width and height both to 400.
- Set the document class to com.efg.games.stubgame.Main
- We have not yet created the Main.as class so you will see a warning. We are going to create this later in this chapter.
- Add the framework reusable class package to the class path for the .fla file
- In the publish settings, select [Flash] -> [ActionScript 3 Setting].
- Click the Browse to Path button and find the /source folder we created earlier for the package structure. Select the classes folder, and click the choose button. Now the com.efg.framework package will be available for use when we begin to create our game. We have not created the framework class files yet, but we will be doing this very shortly.
Creating the stub game project in Flash Develop
And these are the steps to create the same project using Flash Develop:
- Create a folder inside the [source][projects][stubgame] folder called [flexSDK] (if you have not already done so).
- Start Flash Develop, and create a new project:
-
- Select Flex 3 Project.
- Give the project the name stubgame.
- The location should be the /source/projects/stubgame/flexSDK folder.
- The package should be com.efg.games.stubgame.
- Do not have Flash Develop create a project folder automatically. Make sure the Create Folder For Project box is unchecked.
- Click the OK button to create the project.
- Go to the [project] -> [properties] -> [classpaths] menu item.
- Click the add class path button.
- Find the /source folder we created earlier, and select the classes subfolder.
- Click the ok button and then the apply button. You now have the basic structure to start creating projects inside the framework. We are now going to discuss a few topics concerning the structure of the framework classes and then move into building the reusable framework code. Here are a couple of things to note: For Flex Builder, Flash Builder, or other IDEs, please refer to the documentation provided for that product to create a new project and set the default compile class.
A common method of Flash development is to use the Flash IDE for assets and organization and Flash Develop for code editing. If this is your workflow of choice, you will want to follow the Flash IDE folder and package structure rather than the Flex SDK folder structure.
Creating game timers
There are two basic methods that most Flash developers implement when creating a frame-based game timer. By "frame-based," we mean a timer that uses the idea of a segment of time broken up into logical slices (or frames) to manage game logic. There are other types of methods for timing game updates, but we will make extensive use of time-slice or frame-based timers in this book. The basic game timer we will use most of the games will attempt to squeeze all processing and screen updates into each segment or frame. We will also explore a time-step timer and a sleep-based timer in chapter 11. The first timer method is the Event.ENTER_FRAME event timer. The standard Event.ENTER_FRAME event handler will attempt to run the game loop at the .swf file's set frame rate. This very handy game loop timer has been in use for a number of years. The second standard game loop timer method makes use of the Timer class. The Timer class is used to call the game loop at millisecond intervals specified by a delay interval. For example, if the millisecond delay interval is set to 100, the Timer instance would run ten times a second (there are 1,000 milliseconds in a single second). Our framework will begin by using this Timer instance game loop timer. We will do this so we can make use of the TimerEvent.TIMER updateAfterEvent function. As you will see, this function will help smooth out screen updates.
Defining "frame timer tick"
You will see the phrases "frame timer tick," "timer tick," and "frame tick" used in this book. When we refer to a "tick" or a "frame tick," we simply mean one frame's worth of processing. When we run a game at 30 frames per second, we have 30 ticks or 30 frame ticks. This also means that we only have 33.33 milliseconds (or 1,000/30) inside each tick to do all of our processing.
State Machines
A traditional state machine at its very basic is a mechanism that controls the state, or current actions a system can perform. Sometimes this is called a finite state machine. Finite state machines have traditionally been used to model complex mathematical computations and more recently artificial intelligence. The "finite" in the name refers to the fact that the system can only be in a single state at any one time. Our game framework is built on a simple state machine pattern that employs a separate code function or method for each state. There are many other styles of state machines; some use entire classes for each individual state (sometimes called an object-oriented state machine) and some use a simple switch:case statement block called on each frame tick to control state. We will use a third type that borrows from these two. We call our state machine pattern a function reference pattern. Unlike the object-oriented state machine, our machine will contain a separate method or function for each state inside a single framework class. Each of these state functions will control elements in the framework such as instances of the BasicScreen and Game classes. We will use a switch:case statement to move between states. Unlike the afore mentioned very simple switch/case state machine structures that call this switch/state control structure on each frame tick, we only need to call it when we are switching states. The switch:case we call will simply change the function reference we call on each frame tick. The GameFrameWork.as will contain the state machine that controls overall game flow. This will move our game system between states defined in the FrameWorkStates.as file.
Each individual game package we create will contain a Main.as (in the game's own package structure) that will extend GameFrameWork.as. We will also create a unique Game.as child class for each game. The Game class children that we create can also employ their own internal state machines based on the function reference pattern when needed.
Richard (Squize) Myles of www.gamingyourway.com was one of the first to offer the idea of a function reference state machine for ActionScript 3 on his well-respected blog.
The FrameWorkStates.as class file
This class file is a simple collection of constants that will define the states for the game framework. They will be consumed by the GameFrameWork.as class file. The following code listing shows the entire code for this file; you will want to create this file according to the package structure in the previous section: package com.efg.framework { /** * ... * @author Jeff and Steve Fulton * */ public class FrameWorkStates { public static const STATE_SYSTEM_WAIT_FOR_CLOSE:int = 0; public static const STATE_SYSTEM_TITLE:int = 1; public static const STATE_SYSTEM_INSTRUCTIONS:int = 2; public static const STATE_SYSTEM_NEW_GAME:int = 3; public static const STATE_SYSTEM_GAME_OVER:int = 4; public static const STATE_SYSTEM_NEW_LEVEL:int = 5; public static const STATE_SYSTEM_LEVEL_IN:int = 6; public static const STATE_SYSTEM_GAME_PLAY:int = 7; public static const STATE_SYSTEM_LEVEL_OUT:int = 8; public static const STATE_SYSTEM_WAIT:int = 9; } } The first thing you should notice about this class is the package name in the first line. It conforms to the file system structure we created earlier. No matter if you are using a version of Flash, Flex Builder, Flash Builder, Flash Develop, TextMate, or even plain old Notepad, this package name will be the same. The package name is not depended on the code development environment but the chosen file structure for organizing the code. Save this file in the location we created previously. /source/classes/com/efg/framework/FrameWorkStates.as
The state variables
The state variables are constants that the game loop state machine will use to move between game states. We have set up the most common basic states in this sample file, but you will be able to create as many as you need. As we progress through the chapters, more will be added as necessary.- STATE_SYSTEM_TITLE: This state is used to display a basic title screen with an OK button for the user to click to move on. Once the instructions are on the screen, the state will change to the next state.
- STATE_SYSTEM_WAIT_FOR_CLOSE: This one waits until the OK button is clicked for any instance of the BasicScreen class.
- STATE_SYSTEM_INSTRUCTIONS: This state is used to display basic instructions with the same OK button as in the SYSTEM_TITLE state. It also changes to the STATE_SYSTEM_WAIT_FOR_CLOSE state until the OK button is clicked.
- STATE_SYSTEM_NEW_GAME: This state will call the game logic class and fire off its game.newGame() function. It does not wait but moves on to the NEW_LEVEL state right away.
- STATE_SYSTEM_NEW_LEVEL: With this state, we can call the game.newLevel() function to set up a new level for the given game.
- STATE_SYSTEM_LEVEL_IN: This state is used to display some basic information, if needed, for the beginning of a level. In this basic game, we simply display the level screen and wait a few seconds before moving on. The wait is accomplished by changing state to the STATE_SYSTEM_WAIT state for the specified number of frame ticks.
- STATE_SYSTEM_GAME_PLAY: This one simply calls the game logic class's runGame function repeatedly and lets the game take care of its own logic and states.
- STATE_SYSTEM_GAME_OVER: The game over state displays the basic game over screen and waits for the OK button to be clicked before moving back to the instructions screen. It quickly changes state to the STATE_SYSTEM_WAIT_FOR_CLOSE until the OK button is clicked.
- STATE_SYSTEM_WAIT: This state waits for a specified number of frames and then fires off a simple custom event constant called WAIT_COMPLETE.
The GameFrameWork.as class file
The GameFrameWork.as will be that parent of our game's document class. Main.as (the game's document class) for our games will extend this class and call functions to modify the framework for each unique game. The entire code listing is provided at the end of this section. We will explore it in detail once your have had a chance to type in the code. The location for this file in the package structure is /source/classes/com/efg/framework/GameFrameWork.as
The GameFrameWork.as will be the parent class to the Main.as class we will use for our games, and it's shown in the following listing. In later chapters, we will add some functions to this file and even create one that uses a completely different timer. The Main.as will subclass this class with the extends syntax and override the blank stub init function we are about to create. This class will also contain all of the state functions that coincide with the state variables in the FrameWorkStates class. All of the functions in the GameFrameWork.as are public so all can be overridden by the Main.as if needed. In this way, we can customize the behavior of the state functions if we need to.
For example, in later chapters, we will want to play music on the title screen. The function call to play the music will need to be added to the systemTitle state function. Not all games will need this though, so we will not add it to the GameFrameWork.as file's systemTitleFunction. Instead, we will create a new version of the function in Main.as to override the one in GameFrameWork.as. The new one will play the sound needed and then call the systemTitle function inside GameFrameWork.as with the super.systemTitle() function call.
Source Listing 1
The class imports
The class import section contains the necessary Flash core classes needed for the Main class. Notice the package name coincides with the package structure we created earlier in the chapter for the framework: package com.efg.framework {
We also must import all of the classes needed for the framework to run. You will see this put to use shortly.
The variable definitions
The variable definition section defines all of the global scope variables for the class. These include all of the variables needed for the state machine, screens, and the game timer. We will make use of constants to define the current state and a set of variables to hold the state information. These have been be defined on the FrameWorkStates.as file we created in the last section. More states can be added to the basic ones, but these will be sufficient for many games that we will create in this book. There are two special states that are used for the system and wait for button clicks or things like animations to complete. These are the STATE_SYSTEM_WAIT_FOR_CLOSE and STATE_SYSTEM_WAIT respectively. We will also make use of a generic function called systemFunction that will hold the current state function to call in our game loop. Combined with this, we use a set of integer variables to hold the value of the current state (currentSystemState), the last state (lastSystemState) and the next state (nextSystemState) for processing purposes. These states should not be confused with an actual game pause function. This will be handled in a different manner and added to the framework in Chapter 11.
If you are using the Flash IDE and have any assets in the library that need to be exported in the first frame, you must extend MovieClip and not Sprite even if you don't plan to use the main time line for anything else. We have extended MovieClip for the GameFrameWork so it will work with both Flex SDK and Flash IDE projects.
The state control variables
The control variables keep the system functioning within the context of the current state. The main control variable is an instance of the Function class called systemFunction. This function holds a reference to the current function that will be repeatedly called on each frame tick. This saves us from having to evaluate a switch:case statement on each frame to decide which function to call. The function is changed to a new reference by called the switchSystemState() function and passing a new constant that represents the new state. Optimization! switchSystemState() is the first of many optimizations we will make to the game framework. All of these optimizations will make the Flash games run much more efficiently. These efficiencies in the game framework will allow the actual game code to perform more complex operations and still run with a reasonable frame rate. The currentSystemState integer variable holds a number representing the constant of the current state the system is running. The nextSystemState contains the constant integer value for the state to transition to after this state is complete. The lastSystemState variable holds the integer constant of the previous system state. This is used in the rare occurrence that the game loop needs to return to the previous system state. The lastSystemState variable will become useful when we use a shared state such as STATE_SYSTEM_WAIT. The STATE_SYSTEM_LEVEL_IN state will implement a 30-millisecond delay before moving on to the STATE_SYSTEM_GAME_PLAY state. The nextSystemState will be STATE_SYSTEM_WAIT and the lastSystemState will be STATE_SYSTEM_LEVEL_IN. When the 30-millisecond wait time has expired, the waitCompleteListener function will be called. It will use the lastSystemState to determine where the processing was before the wait was started. You'll see this in detail later in this chapter when we examine the waitCompleteListener function.
The background fill variables
All Flash applications have a background color of some type. No matter what game we are going to be creating, the framework can control this background color. You should never rely on the background color setting in HTML for your Flash application's background color. This leaves the HTML embed code with control over major aspect of your application. If you are creating a viral game to be placed on game portals, you will lose control of your game's background color, and it will default to what ever the game portal operators have selected as the standard background color in their embed code. The framework allows you to override the HTML settings here by placing a simple colored Bitmap behind the entire application. We will simply define a BitmapData object called appBackBitmapData and a Bitmap object called appBackBitmap that will be used to place the BitmapData onto the displayList. We will not define the background in the GameFrameWork.as file, but rather the Main.as subclass of the GameFrameWork will set the background if needed in its init function override.
The timer variables
The timer will control the frame rate of the game and help smooth out the display by employing the TimerEvent.updateAfterEvent method. The frameRate variable will be defined in Main.as as the number of frame ticks per second we want our game timer to run. The most important thing to note is that we are making use of the built-in Timer class (gameTimer). We are not using the standard EnterFrame event. This allows us to create our own frame rate for the game and specify it in the frameRate variable. By doing this, we can control game timer tick rate independent of the .swf file's frame rate (FPS). The .swf file can have a frame rate setting of 25 (for example), but the game can run at a 30 frame ticks a second. To do this, we first set the frameRate to the desired number of frame update ticks we want per second (30). When Main.as calls the startTimer function (described in detail when we get to the section on the init function), the gameTimer is put into action. First, we will calculate the timerPeriod value as 1000 / frameRate. With the updateAfterEvent function call (in the runGame function), we help to smooth out the render of the screen by asking the Flash display engine to update at the rate of the Timer, not the frame rate set in the .swf. So, going back to our example, if the game SWF is set to run at 25 FPS, and the frameRate is set to 30 ticks, the updateAfterEvent will help to smooth out the rendering of the screen by asking the Flash display engine to update at the timer tick rate (30), not the .swf file's FPS setting (25).
The timerPeriod will be passed into the Timer instance and the game will attempt to run at this rate. We say "attempt" because if the game includes too many on screen moving objects or more logic than can be computed inside the timerPeriod number of milliseconds (or a combination of both), then there will be a noticable slowdown in the game screen updates. In later chapters, we will add functionality to the runGame function to mitigate some of these issues.
The screen definition variables
The screen definition variables create instances of our BasicScreen class. This is a rather simple class that allows a single positional text box and an OK button on the screen. We will use this simple screen for the title, instructions, level, and game over screens. We will customize each screen when we create the init function override in our game's Main.as class. Note that the levelInText is a special variable. Setting this string to a default value will allow the leveInScreen to display some default text along on each new level. This text can be combined with dynamic text to create a screen that says something like Level 1 with the word "Level" being the default text and the number "1" being the dynamic text. public var titleScreen:BasicScreen; public var gameOverScreen:BasicScreen; public var instructionsScreen:BasicScreen; public var levelInScreen:BasicScreen; public var levelInText:String; public var screenTextFormat:TextFormat; public var screenButtonFormat:TextFormat; We also create two TextFormat objects that will be used for defining the format of the text on the screens and the format of the button text.
The ScoreBoard variables
The scoreBoard instance of the ScoreBoard class will handle a simple heads up display (HUD) for the user with information such as the current score. It is a simple framework class that will be customized in the init function override in each game's Main.as class. The changes will depend on the game that is to be created. We also define a TextFormatObject for the basic look of the text for our scoreboard text: scoreBoardTextFormat.
The Game object variable
The Game object, represented by the variable simply named game, is an instance of the Game class. The Main.as class's init function override will inistantiate this. For example, the StubGame.as we will create for the game in this chapter will be a child of the Game.as base class. It will override some of the Game.as base classes and hold the custom logic for the game. //Game is our custom class to hold all logic for the game. private var game:Game;
The wait variables
These variables are used for the simple wait period in the STATE_SYSTEM_WAIT state. waitTime can be set to a different value each time it is used. 30 is the default. We have set the frame rate for our application framework to 30 frames per second, so this would be a one second wait time. 30 frame ticks equals 1 second of time in our game timer if the frame rate is set to 30. waitCount is incremented each frame when a wait is occurring. When waitCount==waitTime, the control moves to the next state. //waitTime is used in conjunction with the STATE_SYSTEM_WAIT state // it suspends the game and allows animation or other processing to //finish private var waitTime:int = 30; private var waitCount:int=0;
The constructor function definition
The constructor for GameFrameWork.as does not contain any code. It is simply a placeholder. We will subclass GameFrameWork.as to create the unique Main.as for each game. The Main.as constructor will contain code to call the init function override. public function GameFrameWork() {}
The init function definition
The init() function is simply a stub to be overridden by the Main.as subclass of GameFrameWork.as.
The setApplicationBackGround function definition
This function accepts in parameters to create a basic back ground for the game. The width, height, transparency Boolean, and color values for the back ground are passed and used to instantiate the appBackBitmapData and place it on to the display list.
The startTimer function definition
This function will be called by the Main.as subclass inside its init function. It will use the frameRate variable to create the timerPeriod. Since the timerPeriod must be expressed in milliseconds (1,000/1 of a second equals a timerPeriod of 1000 or a single second), we simply divide the frameRate into 1,000 to get the number of times per second that the timer must run. In the case of a frameRate that is set to 30 ticks for example, the timerPeriod would be 33.33.
The runGame function definition
The runGame function is the core of the state machine. Once the systemFunction has been set with the switchSystemState (discussed next) function call, the runGame function will call it repeatedly at the set timerPeriod rate every 33 milliseconds (or so) for or frame rate of 30. public function runGame(e:TimerEvent):void { systemFunction(); e.updateAfterEvent(); } The e.updateAfterEvent() function call tells the Flash player to make an extra screen update after the frame tick is over, rather than waiting for the next system frame update to occur. System frame update events happen based on the SWF's stage frame rate. If we don't call e.updateAfterEvent here, the screen would not be updated until an actual system frame update event occurs. By using this, we smooth out the look of the screen updates to coincide with out selected gameTimer delay value.
The switchSystemState function definition
While the runGame function is the core of the timer, the switchSystemState() function is the core of the simplified state machine. It is passed a constant value for the state. Using that value, it switches the systemFunction reference accordingly. The switchSystemState function is used to change the current systemFunction of the timer for the next frame timer tick. A reference to one of the state constants is passed into the function, and it acts on it to change the systemState variable. It also changes the lastSystemState and nextSystemState variables. As a refresher, here are the constants from the variable definition section of this FrameWorkStates.as class:
public static const STATE_SYSTEM_WAIT_FOR_CLOSE:int = 0; public static const STATE_SYSTEM_TITLE:int = 1; public static const STATE_SYSTEM_INSTRUCTIONS:int = 2; public static const STATE_SYSTEM_NEW_GAME:int = 3; public static const STATE_SYSTEM_GAME_OVER:int = 4; public static const STATE_SYSTEM_NEW_LEVEL:int = 5; public static const STATE_SYSTEM_LEVEL_IN:int = 6; public static const STATE_SYSTEM_GAME_PLAY:int = 7; public static const STATE_SYSTEM_LEVEL_OUT:int = 8; public static const STATE_SYSTEM_WAIT:int = 9; We first set lastSystemState = currentSystemState, so we can have a reference if needed to switch back to previous state. This might occur in circumstances where we need to jump to the STATE_SYSTEM_WAIT state for a period of time and then jump back to the state we were in before the wait. The systemLevelIn function is a good example of this. Once we get to the systemLevelIn function, we want to wait a specified number of milliseconds before removing the levelInScreen from the display. Once the wait is over, the WAIT_COMPLETE event is fired off. The waitCompleteListener function will need to know what the previous systemState was before the wait so it can determine what to do next. We then set currentSystemState = stateval. The stateval was passed when we called the switchSytemState function. This forces the switch/case statement to set the current systemFunction to the function we want to repeatedly call in our loop. We will now start with the first function state the loop calls, the systemTitle function.
The systemTitle function definition
The systemTitle function sets up the display of the title screen and then jumps to a common state used for all button clicks that close the BasicScreen windows STATE_SYSTEM_WAIT_FOR_CLOSE We have not looked at the set of functions that control the STATE_SYSTEM_TITLE, STATE_SYSTEM_INSTRUCTIONS, SYSTEM_LEVEL_IN, and STATE_SYSTEM_GAME_OVER states in detail yet, so let's do that now. There are two basic screen types represented:
- Those that wait for a click of the OK button: titleScreen, instructionsScreen, and gameOverScreen
- Those that wait a predefined time to display the screen before moving on: levenInScreen First, we add an event listener to listen for the OK button clicked event to call the okButtonClickListener function that is shared among all of the screens. After that, we switch the systemState to the constant STATE_SYSTEM_WAIT_FOR_CLOSE. We then set the nextSystemState to be called after okButtonClickListener is fired off. This the first use for one of the three custom event classes we will create:
titleScreen.addEventListener(CustomEventButtonId.BUTTON_ID, okButtonClickListener,false, 0, true);
All of the BasicScreen instances share the same okButtonClickListener function. The custom event passes the id value of the BasicScreen instance to this listening function. The id value is used to determine which screen the button was on and then moves the state machine to a new state based on this evaluation.
The systemTitle, systemInstructions, and systemGameOver functions all look very similar. We'll take a look at those in a bit. First, let's examine the waitForclose and okButtonClickListener functions.
The systemWaitForClose function definition
The systemWaitForClose() function is associated with the STATE_SYSTEM_WAIT_FOR_CLOSE state. It simply does nothing until the OK button on a screen is clicked. This is the simplest function that you will encounter in this book. It does absolutely nothing! It is just a placeholder that the game loop can call while waiting for the OK button to be clicked.
The okButtonClickListener function definition
The okButtonClickListener function is used to determine what to do when an OK button on one of the various BasicScreen instances is clicked. It switches to the nextSystemState when complete. This "listener" function is only fired off when it receives a CustomEventButtonId.BUTTON_ID event.
No matter which OK button was clicked (on any of the three screens that we will define with them), we remove the event listener before we change state. Even though the same listener is used for both the title and the instructions screens (for example), and we remove it from the title screen before adding it again in the systemInstructions function. We do this to be sure we never have extra listeners hanging around. Unused listeners waste memory and can slow down processing. This function shows how we can share a single listener for the OK button click on three different screens (title, instructions, and game over). We switch on the id value passed from CUSTOMEVENT_OK_CLICKED.
No matter which screen we were on, the last line of the function calls the switchSystemState function and passes in the nextSystemState variable.
The systemInstructions function definition
The systemInstructions function is used to display the instructions screen to the user. It is associated with the STATE_SYSTEM_INSTRUCTIONS state. It is called a single time and then processing is passed to the STATE_SYSYEM_WAIT_FOR_CLOSE state. The systemInstructions function is very similar to the systemTitle function. In fact, they are almost identical with a few minor changes. We first add the systemInstructionsScreen to the displayList with an addChild call.
We also setup a listener for the same CustomEvent that the titleScreen used and we switch to the STATE_SYSTEM_WAIT_FOR_CLOSE state. Again, this state does nothing but let the system wait for the CustomEventButtonId.BUTTON_ID on an instance of the BasicScreen class. Finally, we switch to the STATE_SYSYEM_WAIT_FOR_CLOSE on to wait for the OK button to be clicked. We also set the nextSystemState to be evaluated and used once the OK button is clicked.
The systemGameOver function definition
The systemGameOver function displays the gameOverScreen. It is associated with the STATE_SYSTEM_GAMEOVER state and waits for the OK button click shared with the other BasicScreen instances. We take a look at the systemGameover function now, because it uses the BasicScreen instance gameOverScreen in a similar manner as the titleScreen and instructionsScreen instances. The systemGameOver state is set in Main when the Game class instance sends out the simple custom event called GAME_OVER. In the next section, we will see the Main class set up to listen for this event from the Game class.
The sytemGameOver function follows the exact same format as the systemTitle and systemInstructions functions. There is one difference though: it takes care of removing the Game class instance, game, from the display list with a call to the removeChild function.
The systemNewGame function definition
The systemNewGame function is associated with the STATE_SYSTEM_NEW_GAME state. It is called one time and then moves on to the STATE_SYSTEM_NEW_LEVEL state. Its purpose is to add all of the event listeners for communication between the Game class and some other framework classes (such as the ScoreBoard class). It also calls the game.newGame function to allow the Game instance to do its own internal new game related processing. When setting up a new game, we first add our Game class instance (game) to the display list with the addChild function call. This will display the Game.as Sprite (or MovieClip) on the screen. Next, we set up some basic communication between the Game.as and the Main.as classes. We do this by creating four event lsteners. The first two we set up are custom event class instances (classes we will discuss in detail later in the chapter).
The CustomeEventLevelScreenUpdate class allows the passing of a text String instance with the event. The String is used in the Game class to pass the level number (or any text) back to the Main.as class. The Main.as class updates the levelInString variable with the passed in text. We will see this listener function shortly.
The CustomEventScoreBoardUpdate class is used to update the ScoreBoard (another class we will discuss later in this chapter). This event passes data back to the scoreBoadUpdateListener indicating which field on the ScoreBoard to update and what the new value will be. For example: If we wanted to update the player's score, we would pass back the name of the score field (probably "score") and the value of the player's score (example, 5000).
We also create two simple event constants called GAME_OVER and NEW_LEVEL. These will not pass any data back to the listening functions so they will be fired by passing the GAME_OVER or NEW_LEVEL constant into the dispatchEvent function. We do not need custom classes for these types of events.
The systemNewLevel function definition
The systemNewLevel function is associated with the STATE_SYSTEM_NEW_LEVEL state. Its purpose is to call the game.newLevel function and allow the game to start its own internal new level processing. The systemNewLevel function doesn't do much inside Main. It is merely there to call the Game classes' newLevel function. This function will be demonstrated when we get to the simple stub game example. It is used to initialize variables and difficulty for the new level. When it is complete, it switches the system state to STATE_SYSTEM_LEVEL_IN.
The systemLevelIn function definition
The systemLevelIn() function is associated with the STATE_SYSTEM_LEVEL_IN state. It displays a new level message for 30 frame ticks and then moves processing on to the STATE_SYSTEM_GAME_PLAY state. Using systemLevelIn is by no means the only method of forcing the state machine to wait. There are a number of third party custom classes and tools such as TweenMax that can be used for the synchronization of clips and tweens between screens and states. We have added this simple wait state to the state machine for it to be complete framework. The state machine is designed to be easily updated with new states and system functions. Feel free to implement any custom or third-party library or tools that will make your job easier.
The systemLevelIn function is used to allow the developer to display some sort of special text or animation as a precursor to each game level. It employs the use of the STATE_SYSTEM_WAIT state. It is set right after the levelInScreen is added to the display list. The levelInScreen is a BasicScreen instance that does not use the OK button. Instead, it simply waits for the specified waitTime (30 frame ticks in this example) and then fires off the WAIT_COMPLETE simple custom event. The Main class listens for this event and calls the associated listener function. The text on this screen is a combination of the levelInText variable we created in the variable definition section and text passed through the CustomeEventLevelScreenUpdate event. The levelIntext can be set in the init function override of the Main.as (GameFrameWork.as child class). It will be combined with the text passed from the event to create the text on the levelInScreen. We will examine the listener function that does this shortly.
The systemWait function definition
The levelInscreen is very similar to the instructionsScreen, but instead of using the STATE_SYSTEM_WAIT_FOR_CLOSE state, it uses the STATE_SYSTEM_WAIT state. What's the difference? The STATE_SYSTEM_WAIT state calls a function that counts for a specified number of frame ticks before moving on to the nextSystemFunction rather than waiting for the click of the OK button. When the systemState is switched to the STATE_SYSTEM_WAIT state, it calls the systemWait function repeatedly (on each frame tick) until the waitCount is greater than the waitTime set in the systemLevelIn function. When the waitCount is reached, we dispatch a WAIT_COMPLETE event that calls the waitCompleteListener (see the next section). Currently, there is only one systemState that uses the systemWait function, so there is only one item in the case statement. We could add many states that use this systemWait function though, so we have it set up for later expansion. The waitCompleteListener function definition The waitCompleteListener function is triggered when the WAIT_COMPLETE event is fired off from the levelInScreen. It can be used for more screens by updating the switch:case statement.
Once the waitCompleteListener fires off, it switches on the lastSystemState because the currentSystemState is now the STATE_SYSTEM_WAIT. This allows us to share a single listener function for all uses of the WAIT_COMPLETE event. It then switches state to the nextSystemState when it calls switchSystemState(nextSystemState). In this example, the nextSystemState is systemGameplay.
The systemGameplay() function definition
The systemGameplay function is the heart of the gameTimer. It is associated with the STATE_SYSTEM_GAME_PLAY state. The game.runGame function is called on each frame tick when systemGamePlay is the systemFunction reference. The Game class instance (game) will handle all of the game's processing within the game.runGame function. private function systemGameplay():void { game.runGame(); }
The custom event listener functions
The last four functions inside GameFrameWork.as are listener functions for simple and complex custom events that are needed by the Game instance class to communicate with Main class, as well as the levelInScreen and scoreBoard class instances. We'll briefly explain each here so you have a complete version of all the Main.as code in one place. Their use inside those classes will be examined later as we go through the Game->ScoreBoard, and CustomEvent classes.
The scoreBoardUpdateListener function definition for Main.as
The scoreBoardUpdateListener receives updates from the Game class instance that are passed to the ScoreBoard through a CustomEventScoreBoardUpdate instance. The scoreBoardUpdateListener passes data, represented by a simple key/value pair of String object class instances to the ScoreBoard class instance. The element variable will contain a string value representing a constant that we will define in the Main.as (GameFrameWork.as child class). The constant will be the id string name of the element on the scoreBoard to update. The value variable passed will be a string representing the value to show on the scoreBoard for the corresponding TextField. It's constructed to allow the Game class instance and its associated classes to update the ScoreBoard class instance without having to maintain a reference to it. This allows the Game and its associated classes to remain decoupled from the Main.as and the basic framework classes. We will see the same thing with the levelInScreen a little later. Why is the ScoreBoard part of the framework and not part of the Game class? We wanted to have the ScoreBoard be part of the Main class game framework because the Game class instance is not the only object that might interact with the ScoreBoard. We do not implement it in this simple example, but the Main might have other data such as a system-held high score, links out, or even a frame counter in the ScoreBoard.
Why decouple the scoreboard from the game class instance? It is true that we could make calls directly to the ScoreBoard from Game by using the root or parent attribute of the Game class. While this is certainly possible, if we decouple the Game class from the ScoreBoard class, then the Game class will be reusable across multiple frameworks (even your own) with no need to use our framework. The new framework would just need its own ScoreBoard or the Game would need to implement its own.
The levelScreenUpdateListener function definition for Main.as
The levelScreenUpdateListener allows the Game class to update the text on the levelInScreen. Like the scoreBoardUpdateListener, the levelScreenUpdateListener function is used to pass data from the Game class instance to the levelInScreen. When the Game class updates the level value using its own newLevel function, the levelInScreen must also update its text so the correct level number will be shown on the screen during the systemLevelIn function. The predefined default String variable, levelIntext will be combined with the passed in value:
levelInScreen.setDisplayText(levelInText + e.text);
The gameOverListener function definition for Main.as
The gameOverListener listens for the Game.GAME_OVER simple custom event and then changes the framework state accordingly. When the Game class instance fires off the GAME_OVER simple custom event, the Main listens for it and runs this function. It cleans up all of the game-related listeners by removing them and then changes state to the STATE_SYSTEM_GAME_OVER state which was discussed earlier
The newLevelListener function definition for Main.as
The newLevelListener listens for the Game.NEW_LEVEL simple custom event and changes the state accordingly. The newLevelListener listens for the Game class instance to fire off the simple custom event, NEW_LEVEL. It then changes the systemState to the STATE_SYSTEM_NEW_LEVEL state.
Framework classes beyond Main
The framework does not rely on FrameWorkStates and the GameFrameWork classes alone. We are now going to define and explain the classes that make up the BasicScreen (and its helper classes), ScoreBoard, and the custom event classes. We have discussed all each of these briefly in the GameFrameWork.as class description. Now, we take a look at each in greater detail.
The BasicScreen class
All of the simple screens in this basic game framework are created using a very simplified BasicScreen class. The BasicScreen class can become a parent class for more elaborate screens, but in the basic framework, it is very simple. Each screen contains some text positioned on the screen, an OK button if needed and a background color. That is all. If the OK button is needed, there is an event inside the BasicScreen class that is fired off, and it, in turn, fires off its own event to tell GameFrameWork class that it has been clicked. This makes use of a custom event class instance (okButtonClickListener) that we will create in the next section. You should save this class file in the folder structure we created earlier in the chapter to correspond to the framework package.
/source/classes/com/efg/framework/BasicScreen.as
Here is entire code listing for this class.
Class import and variable definition section for BasicScreen
The class import section imports the necessary core Flash classes we will see in action shortly. The one custom class the BasicScreen class makes use of is the CustomEventButtonId class. We will dissect this class in detail later in the chapter, but for now, know that we dispatch an instance of it if the OK button is clicked on any of the instances of this BasicScreen.
The variable definition section of the BasicScreen class creates variables to hold data for the three optional pieces of the BasicScreen:
- The background color BackGroundBitmapData, which is associated BackGroundBimap
- The text to display on the screen, displayText
- The button that will fire off the CustomEventButtonId when clicked, which is an instance of another framework custom class we will create called SimpleBlitButton All three of these optional items are set with public functions inside the BasicScreen class. The Main.as class we create for each unique game will customize the BasicScreen instances by passing values to these public functions inside its init function override of the GameFrameWork's init function. One of the most interesting things about this BasicScreen class is the way we are creating this background for the OK button. We decided not to import or embed any external assets for this first game, because it would necessitate explaining how to do it in both the Flash IDE and Flash Develop (with the open source Flex SDK), and it's too early in this book for that discussion. Instead, we have created a very simple custom button class called SimpleBlitButton. The instance name for this button is okButton. We will discuss this class in detail in the SimpleBlitButton class deinition.
The final variable we need is the id integer. This is passed into the BasicScreen instance from Main. It is used in the switch:case inside the GameFrameWork classes' okButtonClickListener function to change state based on the screen whose OK button was clicked.
Class constructor function definition
The constructor for the BasicScreen class accepts in the basic information needed to set up a screen for display. A screen needs an id value passed in as an integer, as well as the information needed to create the background color for the screen. This is done by passing values needed to customize the backGroundBitmapData variable that was created in the variable definition section. A BitmapData instance needs these four pieces of data passed:- A width
- A height
- A Boolean (true/false) value to indicate whether or not the BitmapData will use transparency
- A color represented as an unsigned integer (uint). A completely transparent background can be set for a screen by passing in true for the isTransparent parameter and a 32-bit color value with an alpha channel set to 00. Colors are represented as AARRBBGG where the alpha (AA) values ranging from 0x00 (0) (completely transparent) to 0xFF (255) (completely opaque) can be passed in. The init function is used to set up the field for the display text (displayText) and the OK button (okButtonSprite). The OK button is added only if the passed in okNeeded is set to true.
The first actual line of code might be a little cryptic:
textformat1.align = flash.text.textFormayAlign.CENTER
When we set up the textFormat1 variable, we didn't set the alignment to center because not all uses of it will be centered. We can change the alignment on the fly, and we do so here before we apply it to the displayText field as its defaultTextFormat. Then, we simply add the text to the screen. In the first init call, there is no text to actually add to the screen. It is added with the setDisplytext function described next. The setDisplayText function is public and is called by the Main class when the BasicScreen instances (title, instruction, game over, and so on) are placed on the display list. This allows the text for the screen to be customized for each showing.
The createDisplayText function definition
By calling the createDisplayText function on a BasicScreen instance, we can turn on a