Flash Multi-player Game Tutorial - TicTacToe

This tutorial teaches you how to start creating your own multi-player flash games!

This tutorial is based on a turn-based game called Tic-Tac-Toe.

Requirements

• Windows Operating system • JRE • Flash / Flex3 development environment / FlashDevelop (http://www.flashdevelop.org) • Free Pulse SDK package from http://www.gamantra.com/pulse • A complete listing is included in the package

Required Programming level

This tutorial assumes that you are already familiar with flash development and can program in AS3 with ease. It also helps if you are familiar with some Object Oriented Programming concepts.

To Start

You need to download the free pulse package and install it. This package contains… • Pulse Server • Many Samples (with source code) • Pulse Game UI Framework (with source code)

To Run the Sample

Start the pulse server from the installed directory. Start the game from the sample directory. Please note that with the free server version you can only run one game at any given time. If you like to try a different sample, you first need to close all the game client instances of the other game.

The Pulse UI Framework

The pulse UI framework is great way to start your new multi-player game, it lets you focus on your game and takes care of the UI for general screen flow login, room management, chatting, friend making, player display, etc. You will find the entire source for the framework in the download package; feel free to modify it as needed for your game.

Getting Started

The first thing to think about is how best way to share the game states among all the players in the room. Model the game For the tic tac toe game you would need to send the player action to the other player, meaning, the client has to send up the x and y position. The first thing to do then is to create a simple xml file that defines the game state you need during game play. There are two steps: 1. Create the XML file. 2. Generate the source using the PulseCodeGen located in the bin of installed folder.

<ObjectSchema>
<import>
<client import="pulse.gsrc.client.*" />
</import>
<class name="Put" parent="GameState" classId="601" >
<public>
<property index="0" name="PutRow" count="1" type="int"/>
<property index="1" name="PutColumn" count="1" type="int"/>
<property index="2" name="PutValue" count="1" type="int"/>
</public>
</class>
</ObjectSchema>
In the above schema file, we would like to communicate to other players of their actions, what is needed for this game is quite simple. The player choosing the spot (row and column) and the value, meaning an “X” or an “O”, the put value is determined at the start of the game depending on who actually created the game room (the game host).

After having designed game states are needed for the game, we need to run the above xml file through the PulseCodeGen tool. Following is a listing of batch file that will do exactly that.\ IF EXIST .\src\tictactoe\gsrc\client del .\src\tictactoe\gsrc\client\*.as CALL "%GAMANTRA%"\bin\PulseCodeGen.bat .\tictactoeSchema.xml tictactoe.gsrc .\src\tictactoe\gsrc IF NOT %ERRORLEVEL% == 0 GOTO ERROR ECHO Success! GOTO END :ERROR ECHO oops! :END Pause

The batch file convenient so if you changed the schema, the regeneration of source files is a quick process.

Start Coding

Once you have the generated files, you need to sub-class from 4 classes like shown below:

Overview of the sub-classes

TictactoeGame This is the main class where the game code begins. This is also the place where you tell the PulseUI of the specific classes that being over-ridden, for example, the following three classes.

TictactoeSkinner You need sub-class the skinner so as to read in the image (skin) files that will be used to draw all the UI for all of the screens.

TictactoeNewGameScreen It is optional to sub-class this, for this game however, we need to tell the pulse server that specific properties of the game room, such as, turn-based, with a maximum of two players per room, etc.

TictactoeGameScreen Lastly, the class where all the action happens inside of the game room, once the game has started.

Sub-class Details

TictactoeGame

This is the main class where the game code begins.

Overriding the constructor:

public function TictactoeGame() {
new TictactoeSkinner();
}

Constructor is a good place to instantiate the game specific skinner class, more on implementing the skinner class is discussed later. Override the getGameId method from PulseGame as follows:

public override function getGameId():String {
return "Tictactoe";
}

The pulse runtime needs a unique game id for each game, since many games could be hosted on a single server. This id is also used by server to match up other players playing the same game. Meaning players playing jigsaw will not be able to go into a room that is playing Tictactoe.

Overriding the start method:

import tictactoe.gsrc.client.GNetClientObjectFactory;

protected override function start():void {
var factory:GNetClientObjectFactory;
factory = new GNetClientObjectFactory();
m_netClient = new GameClient(factory, this);
s_instance = this;
super.start();
}

Start method is where it all begins. A factory object must be created as shown above. The factory class is one of the classes that is created for you during code generation. The Game Client should also be created with the instance of factory passed in. The new GameClient must be assigned to m_netClient field that is defined in PulseGame class. Only one GameClient instance is required. Finally, the super.start() should be called, so the splash screen is displayed and then fades away to take the player to the login screen. At this point the player will be presented with a login screen. The player can either enter a registered username and password or login as guest.

Creating custom classes for the game:

protected override function initGameScreen():void  {
m_gameScreen = new TictactoeGameScreen(this);
m_gameScreen.init();
}

The above two override are needed to create game specific screens, one for during new room creation and the other during game play.

Turn based: Since tictactoe is defined to be a turn-based game (see sub-class details of TictactoeNewGameScreen), the Pulse Server fires a callback to let the client know that it now the player’s turn, during this time, a visual hint should be displayed to the player.

public override function onPlayerTurn():void {
(m_gameScreen as TictactoeGameScreen).onPlayerTurn();
}

The logic here is simply to call the game screen to display the hint.

Server Communication:
public override function onGameStateAction(gameState:GameStateClient):void{
(m_gameScreen as TictactoeGameScreen).onPlayerMoved(gameState);
}

public function sendGameState(xPos:int,yPos:int,value:int):void {
var putMsg:PutClient = new PutClient();
putMsg.setStateType(GameConstants.GS_IS_UNIQUE);
putMsg.setPutRow(xPos);
putMsg.setPutColumn(yPos);
putMsg.setPutValue(value);
m_netClient.sendGameStateAction(putMsg);
m_netClient.nextTurn();
}

There are two methods that need to be implemented, one to receive the action of another player and the other to send the action to the other player. To receive the action of another player, we simply override and the onGameStateAction and pass it to the game screen. To send up the player action, we write a convenience method sendGameState that is called by the game screen object. During this time, we also tell the server that the player turn is done so the server can inform the next player’s turn. Note that the the PutClient was defined in the schema file and generated during code gen phase. For more information on the types of game states available, please refer to the pulse developer’s guide.

TictactoeSkinner

You need sub-class the skinner so as to read in the image (skin) files that will be used to draw all the UI for all of the screens.

     public class TictactoeSkinner extends Skinner {
[Embed(source="tictactoe\\rsrc\\Outline.png")]
private static var OutlineClass:Class;
[Embed(source="tictactoe\\rsrc\\ui.png")]
private static var UIClass:Class;
[Embed(source="tictactoe\\rsrc\\frame.png")]
private static var FrameClass:Class;

public function TictactoeSkinner() {

}

protected override function load():void {
m_outline = (new OutlineClass() as BitmapAsset).bitmapData;
m_ui = (new UIClass() as BitmapAsset).bitmapData;
m_frame = (new FrameClass() as BitmapAsset).bitmapData;
}
}

The custom skinner is quite simple. The above is complete listing for the class. The load method should be overridden to create BitmapAsset for the outline, ui and frame. These are protected fields defined in the Skinner class. The Skinner object will do all the hard work of cutting up the asset and piecing it altogether during the runtime.

TictactoeNewGameScreen

It is optional to sub-class this, for this game however, we need to tell the pulse server that specific properties of the game room, such as, turn-based, with a maximum of two players per room, etc.

public override function createNewRoom():void {
PulseGame.getInstance().setCreatingRoom();
var room:GameRoomClient = new GameRoomClient();
room.setRoomName(m_ti.text);
room.setMaxPlayerCount(2);
room.setRoomType(GameConstants.ROOM_TURN_BASED);
PulseGame.getInstance().getGameClient().createGameRoom(room);
}

The new game screen is required to be customized only for the purpose of defining the kind of room that is being created. The above is specific to the tic tac toe game. The code sets up the room name for others to see in the lobby, a maximum of 2 players are allowed into the room, and finally tell pulse that this room is a turn based game.

Pulse behaves accordingly based on these parameters, for more detailed information please refer to the dev guide and the API guide.

TictactoeGameScreen

Lastly, the class where all the action happens inside of the game room, once the game has started. The game code is implemented with two other classes not listed here… you may download the pulse package to browse the entire source code. The two classes are: TictactoeHotspot, a sub-class of sprite used for tracking the 9 regions of the game board and also onto which we add a child either “O” or “X” depending on which player. TictactoeGameStatus, a simple class for holding some constants. The init method is over-ridden to initialize hotspot areas and such. Done only once for the lifetime of the game client. The source is not listed here.

Initializing the Game Screen: The above init method is called only once, however, the player go in and out of room (game screen) many times. Every time, the a players enter the game room, the show method is called and hide is called, whenever, the player leaves the room.

public override function show():void {
super.show();
}

In this game, we choose not draw anything in the show method, but rather wait till the game starts. The super.show() calls the show method in the GameScreen class that displays the “go” or the “wait” sprite. The room creator is automatically made the host for the room and PulseUI will make sure to display the “go” for the host player and “wait” for all other players joining the room. If there is no special handling is required for your game, you may very well omit the override; we have it here just to explain things a bit.

The startGame method is called on all the player clients, when the host clicks on the “go” sprite. The field m_putValue is initialized which is used determines whether an “O” or “X” should be displayed.

public override function startGame():void {
super.startGame();
if ( PulseGame.getInstance().getGameClient().isGameHost() ) {
m_putValue = 1;
}
else {
m_putValue = -1;
}
addChild(m_borderSprite);
for each (var hotspotArray:Array in m_hotspotArray) {
for each (var hotspot:TictactoeHotspot in hotspotArray) {
m_borderSprite.addChild(hotspot);
}
}
m_isGaming = true;
}
The super implementation removes the “go” or the “wait” sprite. The game board is initialized. In the following hide method, we simply make sure we remove all the game screen related sprite.

public override function hide():void {
if (m_showingTurn) {
removeChild(m_turnSprite);
}

if(m_borderSprite.numChildren > 0) {
for each (var hotspotArray:Array in m_hotspotArray) {
for each (var hotspot:TictactoeHotspot in hotspotArray) {
if(hotspot.numChildren > 0)
hotspot.removeChildAt(0);
m_borderSprite.removeChild(hotspot);
}
}
}
if ( m_isGaming ) {
finishRound();
removeChild(m_borderSprite);
}
m_isGaming = false;
super.hide();
}

The lobby Hit method is called when the player chooses to quit the game screen. At this point we simply end the game. For this particular game implementation, there is no point in having one player be in the room after the other player has quit, so as soon as one player quits, we send back both the players to lobby. You may choose implement any custom behavior you like.

protected override function lobbyHit():void {
//Host send finish game and non-host sent enterlobby
if ( m_isGaming == true )
m_tictactoeGame.getGameClient().finishGame();
super.lobbyHit();
}

Now that we have seen screen management for the game board we will see how the game plays out…

Displaying Player Turn:

public function onPlayerTurn():void {
// show the turn indicator
m_showingTurn = true;
addChild(m_turnSprite);
}

This method is called from the PulseGame object. When the server indicates that it is player’s turn to play, we show the “Your Turn” sprite to the player. Letting the player making the move:

private function playAction(event:MouseEvent):void {
var gc:GameClient = PulseGame.getInstance().getGameClient();
if (gc.isMyTurn() == true) {
var selectedSprite:TictactoeHotspot;
selectedSprite = event.target as TictactoeHotspot;
if ( selectedSprite == null )
return;
var xPos:int = selectedSprite.col;
var yPos:int = selectedSprite.row;
if(m_hotspotArray[xPos][yPos].value == 0) {
m_tictactoeGame.sendGameState(xPos, yPos, m_putValue);
m_showingTurn = false;
removeChild(m_turnSprite);
}
}
}

We handle the mouse click, do a few checks, first is to check if it is indeed the player’s turn to move, if not we simply ignore it, second we check if the click is any of the empty (valid) spot, if everything checks out ok, we send the player action to the server and in turn the server will send the game state to the player itself and to the other player. When the player action is received by the client, the TictactoeGame (refer above) object calls the following method, the implementation here simply updates the screen and checks if the game was won, lost or tied.

public function onPlayerMoved(gameState:GameStateClient):void {
var putMsg:PutClient = gameState as PutClient;
showSprite(putMsg.getPutRow(),
putMsg.getPutColumn(),
putMsg.getPutValue());
checkGameEnd();
}

Good luck on your new multi-player flash game!