[SmartFox] Board game (part 1)
[ August 10, 2004 ] by Marco Lapi, a.k.a Lapo
Article 9: a step by step guide on how to build a turn-based multiplayer game (part 1)


The related source file for this article is found in the "Examples/smartFoxTris/" folder under the name of "smartFoxTris.fla"

SmartFoxTris is our a complete mutiplayer version of the famous "Tic-Tac-Toe" game.

Here's a list of features that we're going to achieve:

» users will log in and join a room called "The Entrance" by default.
» users will be able to create a game room where 2 clients can play the tic-tac-toe game
» as soon as the user creates the game he/she will enter the new game room and wait for the second player
» when the 2nd player joins the game starts.
» the application will alternatively set the player's turn and after every move it will check if there's a winner
» the game ends when no more moves are available or one of the player makes a row of three.
» at the end of a game the user will be able to either start a new game or to go back in the main chat room.

[ THE APPLICATION ZONE ]
In the source FLA file you will notice that we have added a new frame label called "game": that's the frame where we will send the
movie clip playhead when we join a game room.

Now bring the playhead on the "connect" label and open the Actionscript panel (F9).
We didn't change much of the code here, however the zone name is different.

We wanted our game application to have one main room, called "The entrance", users will enter by default: in this room they will
be able to chat and create or join new games.

If you open the config.xml file in the SmartFoxServer Lite folder you can better see how this zone is configured:

 
<Zone name="sftris">
  <Rooms>
      	<Room name="The Entrance" maxUsers="50" isPrivate="false" isTemp="false" autoJoin="false" />
  </Rooms>
</Zone>
			

The zone is setup to contain only one room at start. Then clients will be able to dynamically create as many game rooms as they want from the smartFoxTris interface.


[ JOIN AND CHAT ]
Let's move on to the next label and open the Actionscript panel (F9)

As usual we will use the same application logic adopted so far, and you will notice that most of the code here is almost
identical to the previous examples.

The following line of code setup a flags that will tell us if the user is currently playing a game:

//----------------------------------------------------------
// Setup global vars
//----------------------------------------------------------
inGame = false // flag to see if we're currently playing a game

The "inGame" flag will not be changed until we move to the "game" label so we'll talk about it later.

Next we have a look a the onJoinRoom event handler:

smartfox.onJoinRoom = function(roomObj)
{
if (roomObj.isGame())
{
_global.myID = this.playerId
 
if (_global.myID == 1)
_global.myColor = "green"
else
_global.myColor = "red"
 
// let's move in the "game" label
gotoAndStop("game")
}
else
{
var roomId = roomObj.getId()
var userList = roomObj.getUserList()
 
resetRoomSelected(roomId)
 
_global.currentRoom = roomObj
 
// Clear current list
userList_lb.removeAll()
 
for (var i in userList)
{
var user = userList[i]
var uName = user.getName()
var uId = user.getId()
 
userList_lb.addItem(uName, uId)
}
 
// Sort names
userList_lb.sortItemsBy("label", "ASC")
 
chat_txt.htmlText += "<font color='#cc0000'>>> Room [ " + roomObj.getName() + " ] joined</font>";
}
}

The first thing that we have to do is checking if the client has joined a game room.
Why this? Because this time we're dealing with two type of rooms: "regular" ones and "game" ones, so each time a room is joined we have to check what type of room the user joined and take the appropriate action.

The isGame() method returns true if the current room is a game room.

The code used to handle the non-game room is always the same used before so we can analyze the part used
when a game room is detected.

The playerId variable is a new client API property that we haven't met before. It represent our unique player numeric id
in the room. This way we will be able to check which user is the client that is currently using the application and take the proper decision.

In this sample application every game room will have a capacity of 2 users so the playerId will only be assigned two possible
values: 1 or 2, depending on the room state.

Back to the code: we save the playerId in a _global variable and then, based on its value, we assign a player color and a player name. If playerId is 1 we'll be player 1 and we'll play with the green colored balls otherwise we will impersonate player 2 and
we'll use red balls.

Then the playhead is stopped on the "game" label.

Before we analyze the Actionscript in the game section we need to take a look at the new createRoom function:

function createRoom(name, pwd, max)
{
hideWindow("newGameWindow")
gameRoom = {}
gameRoom.name = name
gameRoom.password = pwd
gameRoom.maxUsers = 2
gameRoom.isGame = true
gameRoom.isTemp = true
smartfox.createRoom(gameRoom)
}

As usual the "create game" dialog is closed and then we proceed creating the object representing the room that we are going to
create. This time the isGame flag is set to true: this will tell the server that the room will hold a game.

What's the difference between a "regular" room and a "game" one? Almost no difference, however there is a slight difference in the behaviour when the room is empty.

If you remember from the "Advanced Chat" tutorial we said that "regular" rooms are removed only when the last user exits
and the room creator is not connected anymore in the zone. If the creator is still around the room will not be destroyed.

A game room behaves a little differently because it will be removed as soon as the last user leaves it. This is the ony difference.

Also it is important that game rooms are recongizable from the others so that you can group them together for example
in a different list box. This is exactly what we've done in this game demo.

Also the sendChatMsg function was slightly modified:

 
function sendChatMsg()
{
if (ingame)
{
if (_global.gameStarted && input_txt.text.length > 0)
{
smartfox.sendPublicMessage(input_txt.text)
input_txt.text = ""
}
}
else if (input_txt.text.length > 0)
{
smartfox.sendPublicMessage(input_txt.text)
input_txt.text = ""
}
}


In a moment we will see that that the game area also contains a small chat window where players can keep chatting while playing. The chat dynamic text field instance was named just like the one in the main chat to keep things simple.
However we have added some more checks to prevent text from being submitted if the game is not started yet.


[ THE GAME BOARD ]

It's time to dive into the fun stuff ;-) Move the playehead one the "game" label and inspect the game GUI:

We've added three new dynamic text fields that will show the names of the players and the one in the middle will show whose turn is. As mentioned before there's a new multiline text field with scrollbar for chatting and the most important movie clip is the game board

The board movie is made up of nine square clips each one called according its grid position: the top-left one is called sq_1_1 and the bottom right one is called sq_3_3

Now open the library (F11) and examine the item called "gridSquare", you will find it under the _GUI/_gameBoard folder.
The symbol is made up of a colored square, a button and an instance of the "ballElement" movieclip that can be found in the _Balls
library folder.

Now open this symbol and you will notice that it has three states corresponding to name of the labels: "off", "red", "green"

We'll be able to control each of these clips inside the game board to show the right item when a user clicks on of the
squares.



[ ROOM VARIABLES ]

Now that we have inspected the game GUI a little we could analyze the game logic code... but there's still one thing
we need to know before diving into the Actionscript.

You need to learn a little about "Room Variables" and the differences between the already known "User Variables"

The one and only big difference between the two is that Room Variables are values stored in the room object opposed of the user object.

Why do we need to save variables in the Room?

Here's how the game works: when a user enter the game room he/she will save a variable called "player1" or "player2" based on which player he is.

In order to start the game we need that both variables (player1, player2) exist in the room otherwise the game will be "paused" until both values are present.

By having a "central" place to save our server values it is very simple to keep track of the status of the game room.

Summing it up the "User variables" are great when you need to save user specific preferences regardless of which room the client is currently in. On the other hand "Room variables" are the right tool when you need to keep track of the status of the Room and this is always an important aspect in game Rooms.

Creating a "Room Variable" is similar to what we've seen so far

vObj = new Array()
vObj.push({name:"player" + _global.myID, val:_global.myName})
 
smartfox.setRoomVariables(vObj)

You create an array of objects with two properties each, called "name" and "value" and then call the setRoomVariables() API method to set one or more "Room Variables" in one shot.

As with the "User Variables" every change in the room variables will be notified to all the clients in that room and you will need to implement a function to handle the "onRoomVariablesUpdate" event, which we'll encounter in a moment.

That's all for this fist part: we have introduced a number of new features that we will use in the application.
In the next chapter we'll analyze the gameplay and we'll finish our first "turn-based" game! :-)

Lapo


    
 
 
Name: Marco Lapi, a.k.a Lapo
Location: Fossano, Italy
Age: 34
Flash experience: started out with Flash 4 back in 1999
Job: web designer/developer
Website: http://www.gotoandplay.it/
 
 
| Homepage | News | Games | Articles | Multiplayer Central | Reviews | Spotlight | Forums | Info | Links | Contact us | Advertise | Credits |

| www.smartfoxserver.com | www.gotoandplay.biz | www.openspace-engine.com |

gotoAndPlay() v 3.0.0 -- (c)2003-2008 gotoAndPlay() Team -- P.IVA 03121770048