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
|