Multiplayer Aspects of the Game

In Chapter 17, "Tic-Tac-Toe: Your First Multiplayer Game," we discussed all of the basic multiplayer techniques that are used in Flash multiplayer games covered in this book:

  • Creating room variables on the server to keep track of when both players have arrived and when a player has left the game

  • Sending a move to your opponent and creating a property called type, which specifies the type of move being made, on the object you send to your opponent

  • Joining the game room at the right time

If you have not yet read Chapter 17, you should probably do so now, as we are not going to go over those topics again here. However, there is one multiplayer issue, not seen in tic-tac-toe, that we need to discuss in this section. After you have read through this, you will probably think "No duh!", but it took me a good six months to realize that what we explain here is a necessity.

Synchronization: A Multiplayer Problem and Solution

Imagine this situation. Frank and Estelle are playing a game of multiplayer 9-ball created in Flash. Frank has a top-of-the-line computer, the fastest available. Estelle, on the other hand, is using an old computer that she picked up back in college. It is so slow that she can only run one or two applications at a time.

Frank is Player 1, so he breaks. He hits the cue ball into the rack at top speed. Within about 30 seconds, he's pocketed two of the balls (from the hard break), and the rest of the balls roll to a stop. Estelle's computer is having trouble computing the ActionScript at the intended frame rate. All of the actions will be calculated precisely the way they are on Frank's computer; it's just that they will take two to three times as long. Around 1 minute goes by before the balls stop rolling on Estelle's computer. According to Frank's computer, after the first 30 seconds it is still his turn and the balls have stopped rolling, so he takes another shot. Do you see the problem? Frank just tried to take a shot before the balls have stopped on Estelle's computer. If we allow this to happen, then the multiplayer game will break down at that point. At the time of Frank's shot (30 seconds after the break), only one screen Frank's is showing the correct configuration of the balls. If he had waited a full minute, then Estelle's computer would have caught up and both screens would show the correct configuration of the balls.

What I have just described causes a synchronization issue a multiplayer game programmer's worst nightmare. I described the situation using two computers with extremely different processing speeds to highlight the discrepancy. More likely the two computers will be fairly similar in their specifications, but there will still be a few seconds during which the screens won't be completely in sync. For example, say both players have screamingly fast computers, but Frank has three Web windows open, plus Adobe Photoshop and Macromedia FreeHand. His fancy computer will be a little bit slower to process the information than Estelle's computer and will lag behind.

They say hindsight is 20/20, and I believe it. I don't know how I missed this issue for so long … but I did. You will encounter it when you create or play turn-based multiplayer games in which animation or some other timed factor plays a part. In games like tic-tac-toe, checkers, and chess, we would not see this issue. Now that you understand what the problem is, and before you let it get you down, let's talk about the solution. The solution is to not let a player move unless both players are ready. We do this in a very simple way. As you might remember from the tic-tac-toe chapter, we set room variables to help us determine when the users are in the game room or if one of them has left. We can also use room variables to store the status of each user's window. (By "window" I mean one user's instance of the entire game.) For example, when Player 1's screen is ready to send or receive a move, we set a room variable called player1stopped with a value of "yes". When Player 2's screen is ready to send or receive a move, we set the room variable player2stopped with a value of "yes". As soon as a player sends or receives a shot, his game automatically sets the server variable player1stopped or player2stopped with a value of "no".

Remember that whenever a room variable is created, modified, or deleted, that causes the onRoomVarChange event to be fired. When this event is fired, an object that contains all of the room variables is passed in. We check the values of player1stopped and player2stopped on this object. If they both have a value of "yes", then we are at a point where we can allow another move.

The Multiplayer Actions


If you don't already have pool.fla open, then open it now. This game file is identical to that of tic -tac-toe, except for the contents of the Game frame on the main timeline. Click that frame now, on the Actions layer. The ActionScript on this frame that is used to handle chatting is precisely the same as that used for the chat window in tic-tac-toe.

Double-click the movie clip in that frame to enter it. You will see three layers in this movie clip Multiplayer Actions, Actions, and Assets. The Assets layer contains all of the graphics and movie clips needed in the game. The Actions layer contains the ActionScript used for everything in the game except the transfer of data from player to player (for example, sending and receiving moves). The Multiplayer Actions frame contains all of the ActionScript needed to send and receive moves; create, change, and receive room variables; and initialize the game. It is the code on this frame that we will look at now. Most of this code will be just briefly mentioned because of its similarity to that used in the tic-tac-toe game.

Here is the ActionScript found at the bottom of that frame:

 1   ES = ElectroServer;  2   player = ES.player; 3   initializedYet = false; 4   ES.onRoomVarChange = roomVarChanged; 5   ES.moveReceived = moveReceived; 6   ES.joinGame(); 7   createVar("player"+player, "here"); 

In line 1 we simply create a shortcut to the ElectroServer instance of the ElectroServerAS object. This is done purely out of laziness (it's easier to type ES than to type ElectroServer!). Next we set a variable called player to store your player number. If you are the challenger, then your player number is 1; otherwise it is 2. In line 3, initializedYet is set to false. This is the variable that tells us whether both players have ever both been in the room. We start with a value of false, and then the first time player1 and player2 have values of "here", we set initializedYet to true. (This is the same thing we saw in the tic-tac-toe chapter.)

In line 4 we set the onRoomVarChange event handler to call roomVarChanged() when room-variable information comes in. Likewise, in the next line we set the moveReceived event handler to call the moveReceived() function when a move arrives. Now that all of the event handlers have been set, it is safe to join the game. We do so in line 6. Remember that the order in which you do this is very important. It is important enough for me to mention again: If you were to join the game before defining the event handlers, then you would run the risk of receiving data about the room or your opponent before you were equipped to handle it. In the final line we create a room variable for your player and give it a value of "here".

We are about to look at all of the functions on this frame. Before we do that, I want to mention the different types of moves in this game. You'll remember from the tic-tac-toe chapter that when we sent a move, we sent an object to our opponent. We added a property to the object called type, which stores a string value of the move type. In tic-tac-toe we only had two types, "move" and "restart". Here are the move types we have in 9-ball:

Shot This move sends your opponent the information that will replicate your shot on her screen, including the x and y positions of the ball, the speed at which it has been shot, and the angle at which it has been shot.

Place_cue When you have placed the cue ball from ball-in-hand, this move is sent to your opponent. When it's received, the cue ball position is updated on your opponent's screen. Currently this is only sent when the cue ball has been placed. However, you can modify it to be sent while you move the cue ball, for more of a real-time feel.

Restart This move is sent to your opponent when you click the Restart button. When your opponent receives it, the entire game will be restarted.

If you want to make this game more polished, you can add other types of moves. For instance, as a player rotates the stick around the ball or slides the stick away from the ball to adjust the power, you can send that information. These kinds of refinements can give the game more of a real-time effect. The three moves that have been implemented in this game are the minimum needed to get it working.

Now let's look at the moveReceived() function:

 1   function moveReceived(ob) { 2      if (ob.type == "shot") { 3         game.ball1.x = ob.x; 4         game.ball1.y = ob.y; 5         shoot(ob.speed, ob.angle); 6      } else if (ob.type == "place_cue") { 7         game.ball1.x = ob.x; 8         game.ball1.y = ob.y; 9         game.ball1.clip._x = ob.x; 10        game.ball1.clip._y = ob.y; 11     } else if (ob.type == "restart") { 12        restart(); 13     } 14  } 

This function is called when a move is received. An object containing the information your opponent sent is passed in. For the "shot" type of move, we extract the cue ball's x and y positions, and set its position based on them. The cue ball is represented by the ball1 object, which is stored on the game object. (You will see more about this later.) We then call the shoot() function, passing in the speed and angle. The shoot() function, which handles shooting the ball, will be discussed later. When a move is a "place_cue" move, we update the cue ball's position both in memory and on the screen. And if the move is a "restart" move, then we call the restart() function.

Now let's look at the three separate functions used to send these types of moves:

 1   function sendShot(speed, angle) { 2      var ob = {speed:speed, angle:angle, x:game.ball1.x,         y:game.ball1.y, type:"shot"}; 3      ES.sendMove(ES.opponent, ob); 4   } 5   function sendCuePlacement() { 6      var ob = {x:game.ball1.x, y:game.ball1.y,         type:"place_cue"}; 7      ES.sendMove(ES.opponent, ob); 8   } 9   function sendRestart() { 10     var ob = {type:"restart"}; 11     ES.sendMove(ES.opponent, ob); 12  } 

As you can see, sending a move is simple. First an object is created to store the information you want to send, and the object is sent. In sendShot(), in addition to the type property, we send the speed and position of the cue ball as well as the angle at which it was shot. In sendCuePlacement() we just send the ball's position in addition to the type. And in restart() we simply send the name of the move.

We create two types of room variables in this game: one to flag a player as being in the room, and one to flag a player's game instance to be able to send or receive a move. The latter resolves the synchronization issue covered earlier in this chapter. Here are the functions used to create variables:

 1   function createVar(name, value) { 2      ES.createVariable(name, value); 3   } 4   function flagStopped(val) { 5      createVar("player"+player+"stopped", val); 6   } 

The createVar() function simply takes the name and value parameters passed in and uses them to create a room variable. This function is called when the frame first loads, as we saw above, to set a variable for the player. If you are Player 1, then the variable set from your SWF to the room is player1 with a value of "here"; otherwise it is player2 with a value of "here". It is also called from flagStopped(). This function changes player1stopped or player2stopped to "yes" or "no" when appropriate. Let's say you are Player 1. As soon as shoot() is called, flagStopped() is then called, and player1stopped is given a value of "no". When all of the balls on the table come to a stop, player1stopped is given a value of "yes" by calling flagStopped() and passing in "yes". A player cannot move unless both player1stopped and player2stopped have a value of "yes".

That brings us to the roomVarChanged() function, which is called whenever a room variable is created, modified, or deleted.

 1   function roomVarChanged(ob) { 2      if (!initializedYet && ob.player1 == "here" &&         ob.player2 == "here") { 3          initializedYet = true; 4          startGame(); 5      } else if (initializedYet && (ob.player1 != "here"||         ob.player2 != "here")) { 6          popup.gotoAndStop("player left"); 7      } 8      if (ob.player1stopped == "yes" &&         ob.player2stopped == "yes") { 9          locked = false; 10         display.gotoAndStop(game.myTurn ? "myturn" :             "notmyturn"); 11     } else { 12        locked = true; 13        display.gotoAndStop("sync"); 14     } 15  } 

This function is almost identical to its tic-tac-toe counterpart. What's different is the second if statement. This is where we handle locking and unlocking the game screen. If both player1stopped and player2stopped have a value of "yes", that means it's OK for somebody to shoot. In that case, we set locked to false, which allows the current player to make a move. Otherwise we set locked to true because the balls on one of the screens are not yet stopped. In either case, we tell a movie clip with an instance name of sync to go to a specific frame. This movie clip tells us what's going on. If it is on the sync frame, then the movie clip displays the word "Synchronizing." If it is someone's turn, then it either displays "Your Turn" or "Not Your Turn."

You have now seen all of the actions that control the multiplayer nature of this game. Next we'll tackle the ActionScript for controlling the state of the game screen itself.

Macromedia Flash MX Game Design Demystified(c) The Official Guide to Creating Games with Flash
Macromedia Flash MX Game Design Demystified: The Official Guide to Creating Games with Flash -- First 1st Printing -- CD Included
Year: 2005
Pages: 163
Authors: Jobe Makar

Similar book on Amazon © 2008-2017.
If you may any questions please contact us: