TicTacToe Tutorial: Client

From EsWiki

Jump to: navigation, search

This is a tutorial that covers the creation of the client for tic-tac-toe

Contents

What You'll Learn

  • How to create a game of tic-tac-toe

Prerequisites

Download

If you wish to simply download the entire set of TicTacToe files (both server and client, source code and compiled), it is found in TicTacToeTutorial.zip.


Let's Get Started!

In the steps below you'll learn how to make the client for Tic-Tac-Toe.

The Plan

In this tutorial, we are going to look at creating the client side part of Tic-tac-toe.

The first thing we need to discuss is what the game will be like to the player.

  • The player joins the game from the quick join menu.
  • The player will see a list of users in the room and can chat with them.
  • Once two users have joined the room, the board is shown.
  • The players whose turn it is will make a selection, will show for both
  • The turns will alternate, and once the game is complete, it will reset shortly (time given for the player to leave)

The first thing we will look at is the UI we will add.


Creating the Visuals

In the Interface movieclip, add a new frame and label it TicTacToe.

Then create a new movieclip, call it TicTacToe and export it for actionscript. Add two layers to have a total of three layers, and label them Labels, Actions, and Content.

In the Content layer, add a List, TextArea, Button and TextInput and label them c_userList, c_chatBox, c_leaveGameButton and c_messageInput respectively. Next, draw a rectangle (it will be the tile for tic tac toe) and make it a movieclip, call it Tile (does not need to be exported for actionscript ). Inside the Tile movieclip add two new key frames on frames 2 and 3. On frame 2 add an X on top of the tile. On frame 3 add an O. Also add a stop command on frame 1.

Now in the TicTacToe movieclip, place 9 of these for the tic-tac-toe board. They will be labeled using the pattern tilexy, where x is the x-position and y is the y position. (Both start at 0). So the top left tile is tile00, top right tile is tile20, middle right tile is tile21, bottom left tile is tile02. Continue using this way to name them all.

Add 3 more frames to the TicTacToe movieclip, spacing them out a bit. Label frame 1 Stop, the next WaitingForPlayers, then DuringGame and the final ResetFrame. Add a stop(); command to frame 1. Remove the leave game button from the DuringGame frame, as we don't want to allow players to leave during the game. On the WaitingForPlayers frame add the code startWaiting(); and on the ResetFrame add gotoAndStop("DuringGame"). Remove the board (the nine tiles) from ResetFrame and the Stop frame. The ResetFrame frame will be used between games, to force the board to reset by going to a frame without the board.

Now that we are done the UI, lets move on to the code.

Creating the TicTacToe Class

Create a new actionscript class and call it TicTacToe. Have it extend Modules and implement GenericGame.

We are going to need these imports:


  	import com.electrotank.electroserver4.room.Room;
  	import com.electrotank.electroserver4.ElectroServer;
  	import com.electrotank.electroserver4.esobject.EsObject;
  	import com.electrotank.electroserver4.message.request.PluginRequest;
  	import com.electrotank.electroserver4.message.MessageType;
  	import com.electrotank.electroserver4.message.event.PluginMessageEvent;
  	import com.electrotank.electroserver4.message.request.PluginRequest;
  	import flash.events.MouseEvent;
  


And all these variables:


  	private var m_isDisplay:Boolean;
  	private var m_Main:Main;
  	private var m_currentRoom:Room;
  	private var m_userName:String;
  	private var m_isMyTurn:Boolean;
  	private var m_myTeam:int;
  	
  	public static var TAG_TILEX:String   				   				 = "tilex";   
  	public static var TAG_TILEY:String   			   					 = "tiley";   
  	public static var TAG_TEAMID:String   			   					 = "teamID";
  	public static var TAG_PLAYERNAME:String 		   					 = "playername";
  	public static var TAG_MESSAGETYPE:String 		   					 = "messagetype";
  	public static var TAG_ISTIE:String   				   				 = "istie";
  			  
  	public static var ACTION_TAKETURN:int   		   					 = 0;
  	public static var ACTION_GAME_OVER:int   		   					 = 1;
  	public static var ACTION_UPDATEBOARD:int 		   					 = 2;
  	public static var ACTION_SETUPGAME:int   		   					 = 3;
  					
  	public static var STATE_WAITING:int   				   				 = 0;
  	public static var STATE_IN_PLAY:int   				   				 = 1;
  	public static var STATE_GAME_OVER:int   		   					 = 2;
  					
  	public static var TEAM_X:int  		    		   					 = 1;
  	public static var TEAM_O:int   				   				   	 = 2;
  


As in the GuessTheNumber tutorial, a lot of these variables are on the server also, to ease the process of communication. The other variables will save information we need for the client side, which isn't too much as all of the logic is on the server.

The functions we know we will have to have are enableLogic,enableDisplay,disableLogic and disableDisplay since this class implements GenericGame. Lets look at those next.

Implementing GenericGame

The enableLogic function initializes a few variables and gets the name of the user saved on the Main instance. An event listener is added for all clicks (to see if the player pressed a tile) and one for listening for plugin messages.

The disableLogic function just removes the listeners that were set.


  public function enableLogic( toSet:Main ):void
  {
  	m_isMyTurn = false;
  	m_isDisplay = false;
  	
  	m_Main = toSet;
  	m_userName = m_Main.getUserName();
  	
  	addEventListener(MouseEvent.CLICK, tileClicked );
  	m_es.addEventListener( MessageType.PluginMessageEvent, "receiveMessage", this );
  }
  
  public function disableLogic():void
  {
  	//Remove listeners
  	m_es.removeEventListener( MessageType.PluginMessageEvent, "receiveMessage", this );
  	removeEventListener(MouseEvent.CLICK, tileClicked );
  }
  


The enableDisplay function retrieves and saves the current room (so it knows where to send messages to). It then enables the needed modules. It also enables the leave game room module if the frame is currently stop. If it just always added the leave game room module, the logic may have changed the frame from one that had the leave room button, which would cause an error.

The disableDisplay function disable the modules and removes the game from the display list. Note: The call to disable the leave game room is not included, since we do not know if the leave game room button exists when we exit (though we could keep track, we don't in this tutorial).


  	public function enableDisplay():void
  	{
  		m_isDisplay = true;
  		
  		//Not done til room has changed, get the room then
  		m_currentRoom = m_Main.getCurrentRoom();
  		
  		//Add specific modules
  		enableSendingPublicMessages( c_messageInput, m_currentRoom );
  		enableReceivingPublicMessages( c_chatBox );
  		enableReceivingPrivateMessages( c_chatBox );
  		enableDisplayOfUserList( c_userList, m_currentRoom );
  		
  		//If it is still the initial frame,
  		if(currentLabel == "Stop")
  		{
  			enableLeaveGameRoom( c_leaveGameButton, m_currentRoom, m_Main );
  		}
  		
  	}
  	
  	public function disableDisplay():void
  	{
  		//Remove modules
  		disableSendingPublicMessages();
  		disableReceivingPublicMessages();
  		disableReceivingPrivateMessages();
  		disableDisplayOfUserList();
  		
  		
  		m_Main.removeChild( this );
  	}
  


Finally, the disable function just calls both disable functions.


  	override public function disable()
  	{
  		disableLogic();
  		disableDisplay();			
  	}
  


Next we will look at the receiveMessage function.


Receiving Message from the Server

The receiveMessage function contains nearly all of the logic that is on the client side.

The function first gets the EsObject, which is the message, then makes sure the TAG_MESSAGETYPE property exists ( shows that it is likely the correct message, though we should not get incorrect messages ). Then, based on the message type, different actions are done.

If it is the setup game action, the players team is saved and the frame is changed to ResetFrame to reset the board. If the action is take turn and the name is this name of this player, it is their turn. When the action is update board, the designated tile's frame is changed to the correct one. Finally, if the game is over, it outputs the correct message depending on who won, and then goes to the WaitingForPlayers frame.



  	public function receiveMessage( e:PluginMessageEvent )
  	{
  		var message:EsObject = e.getEsObject();
  		
  		if( message.doesPropertyExist( TAG_MESSAGETYPE ) )
  		{
  			var messageType:int = message.getInteger( TAG_MESSAGETYPE );
  			
  			if( messageType == ACTION_SETUPGAME )
  			{
  				m_myTeam = message.getInteger( TAG_TEAMID );
  				output( "The game has begun!");
  				gotoAndStop("ResetFrame");
  			}
  			
  			else if( messageType == ACTION_TAKETURN )
  			{
  				var playerName = message.getString( TAG_PLAYERNAME );
  				if( playerName == m_userName )
  				{
  					//It is this players turn
  					m_isMyTurn = true;
  					output("It is your turn.");
  				}
  				else
  				{
  					//It is the other players turn
  					m_isMyTurn = false;
  					output("It is your opponents turn.");
  				}
  			}
  			
  			else if( messageType == ACTION_UPDATEBOARD )
  			{
  				var x:int = message.getInteger( TAG_TILEX );
  				var y:int = message.getInteger( TAG_TILEY );
  				var team:int = message.getInteger( TAG_TEAMID );
  				
  				this["tile"+x+y].gotoAndStop( team + 1 );
  			}
  			
  			else if( messageType == ACTION_GAME_OVER )
  			{
  				var isTie:Boolean = message.getBoolean( TAG_ISTIE );
  				if( isTie )
  				{
  					output( "The game was a tie! ");
  				}
  				else
  				{
  					var winner:String = message.getString( TAG_PLAYERNAME );
  					if( winner == m_userName )
  					{
  						output( "You won!" );
  					}
  					else
  					{
  						output( "You lost. ");
  					}
  				}
  				
  				gotoAndStop( "WaitingForPlayers" );
  			}
  		}
  	}
  


The startWaiting function, which called on the WaitingForPlayers frame just enables the ability to leave the game. As in earlier examples, the output function just adds the message to the chat box and scrolls as much as possible.


  	private function startWaiting()
  	{
  		enableLeaveGameRoom( c_leaveGameButton, m_currentRoom, m_Main );
  	}
  	
  	private function output(msg:String):void 
  	{
  		c_chatBox.htmlText += "<FONT COLOR = '#0099FF'>" + msg + "</FONT>";
  		c_chatBox.verticalScrollPosition = c_chatBox.maxVerticalScrollPosition;
  	}
  				


The final function we need to look at is the tileClicked function, called when the user clicks on anything in the TicTacToe movieclip.


Sending Message to the Server

The tileClicked function first makes sure that it is the players turn. If it is, it gets the name of what it clicked on and checks to see if it starts with the letters "tile" (which only the board tiles should). Then it makes sure the tile is still not chosen (still on first frame) and if it is not chosen, extracts the x and y coordinates from the name. The EsObject to send is created with the appropriate value and then the PluginRequest is sent to the server.


  	public function tileClicked( e:MouseEvent )
  	{
  		
  		//Make sure it is this players turn
  		if( m_isMyTurn )
  		{
  			
  		
  			//Check to see if they clicked on a tile
  			var clickedName:String = e.target.name;
  			var firstFourLetters = clickedName.slice( 0, 4 );
  				
  			if( firstFourLetters == "tile" )
  			{
  				//Check to see that it is still not chosen, ie on frame 1
  				//Note this will throw an error if another thing begins 
  				//with tile, but is not a movieclip
  				
  				if( e.target.currentFrame == 1 )
  				{
  				
  					m_isMyTurn = false;
  					var x:int = int( clickedName.charAt( 4 ) );
  					var y:int = int( clickedName.charAt( 5 ) );
  				
  					var message:EsObject = new EsObject();
  					message.setInteger( TAG_MESSAGETYPE, ACTION_UPDATEBOARD );
  					message.setInteger( TAG_TILEX, x );
  					message.setInteger( TAG_TILEY, y );
  					message.setInteger( TAG_TEAMID, m_myTeam );
  					
  					
  					
  					var request:PluginRequest = new PluginRequest();
  					request.setPluginName( "TicTacToe" );
  					request.setEsObject( message );
  					request.setRoomId( m_currentRoom.getRoomId());
  					request.setZoneId( m_currentRoom.getZone().getZoneId());
  					
  					m_es.send( request );
  		
  				}
  			
  			}
  			
  		}
  		
  	}
  



Updating the Main Class

All the needs to be done in the Main class is edit gameList and gameClassList in the initialize function to have TicTacToe in both lists.

And that's it, your done!

Finished

You are now finished another multiplayer game, congratulations!

Personal tools
download