TicTacToe

From Blue Mars Developer Guidebook

Jump to: navigation, search
There are security restrictions on this article

Contents

Overview

The Tic-Tac-Toe sample is another example that elaborates on the dynamic object mechanism, which will give you the ability to synchronize data across all clients over the network, and allow everyone to see the same results in Blue Mars. It will have a ownership conflict. But it will resolve in few second or reset both conflicted player.

Prerequisite

To use this example you should be familiar with the following concepts.

  • How to create a City.
  • How to use the Asset Browser.
  • How to set up an original entity.
  • Basic knowledge of Lua scripting.
  • Knowledge of the Dynamic Object framework and how your avatar is synchronized across clients via TKserver (wiki page to be added).


How to install TicTacToe in your City

Please follow these steps, described below, to install the TicTacToe sample.

  1. Download TicTacToe.zip.
  2. Open your City data with the City Editor.
  3. Use the Asset Browser to copy the data.
  4. Import the layer file.
  5. Test.


Download TicTacToe.zip

Click the link below to download TicTacToe.zip. We recommend that you save the file on your desktop first then unzip it.

File:TicTacToe.zip
This .zip file contains the following data:
  • Objects/*
  • Entities/SampleTicTacToeBoard.ent
  • Scripts/Entities/Minigames/TicTacToe/SampleTicTacToeBoard.lua
  • TicTacToeLayer.lyr

Open your City data with the City Editor

...Or you may create a new City for this sample.

Use the Asset Browser to copy the data

The Asset Browser will maintain all file paths for both material files(.MTL) and the layer file(.LYR), so you do not need to change the file paths by hand.
Please copy the unzipped TicTacToe data into your City folder by dragging.

  • Browse to your level directory in the Asset Browser
  • Browse to the TicTacToe folder in a Windows Explorer window
  • Select and drag everything inside the TicTacToe folder (Entities, Objects, Scripts folders and the TicTacToeLayer.lyr) to the Asset Browser at the same time
  • Reload your level before moving on to the next step


Import the layer file

You can import the "TicTacToeLayer.lyr" file from the Layer tab in the RollUpBar.
Every City has a different land size and coordinates, so after importing the layer file you may need to run Select All Objects
Image:TicTacToe_RollupBar_SelectAllObjects.png
then use "[Modify] -> [Go to selection]" menu Image:TicTacToe_GotoSelecteObject.png to see the TicTacToe board.

Test

As you may know, Dynamic Objects were designed to synchronizing data across clients. However, the City Editor does not have network connecting capabilities. Therefore, this example will only work with the Check in BlueMars mode.

Detailed explanation

Flow network

You can find the flow network for this TicTacToe board from the SampleTicTacToeBoard1 entity which is placed under the table.


  • In the flow network, you can see 9 ARItem entities in the upper side of the screen. Those are used for catching the mouse click event and passing the event to the SampleTicTacToeBoard entity. That entity will then synchronize the board information across clients by using the dynamic object mechanism.
  • Another ARItem entity in the bottom of the screen will trigger the reset callback of SampleTicTacToeBoard to reset all information.


Entity

We've provided one sample entity for this demo named SampleTicTacToeBoard, which is saved in Entities/SampleTicTacToeBoard.ent. You can access this entity from the Entity button in the Objects Rollup Bar.

Image:TicTacToe_Rollupbar_Entity.png

Diagram

There are only 2 states in this sample. The Click event will trigger a state change. Most of the time,the onUpdate routine runs once every frame and waits for the trigger.


Lua Script

The main part of this example is a lua script, which you can find in Scripts/Entities/Minigames/TicTacToe/SampleTicTacToeBoard.lua.

  • This entity toggles the status between "Circle" and "Cross" to indicate which game piece will be placed on the board at the next click.
  • When the user clicks the board, it will toggle the status of the entity and synchronize the status of game pieces on the board (PieceStateTable) via the UpdateState function of the dynamic object.
  • To use the UpdateState function, the user has to take ownership of the dynamic object to avoid conflict with another user.
  • The ARDynamicObject.Setup(SampleTicTacToeBoard) command will make this entity dynamic.
  • Description
for initialization
SampleTicTacToeBoard = {} Constructor.
PieceObjSetup() Part of OnInit but will be executed one frame later.
OnInit() Initialize function.
Game State (Turn base)
Cross Status "Cross" means that a cross game piece will be placed on the next mouse click event.
Circle Status "Circle" means that a circle game piece will be placed on the next mouse click event.
Synchronization Mechanism
SynchronizedParameters() This callback function will be called by the dynamic object mechanism once per second, but the timing may vary.
GamePieceUpdate() Will be called from the user click event to set the NeedUpdate flag which is used at OnUpdate of both states.
DrawTable() Update display of all game pieces on the table. This function is always called from OnUpdate of each status.
for flow network
Event_Click??() Plug for the flow network which will place the game piece on the table.
ResetGame() Plug for the flow network which will reset the table.

  • Source Code
-------------------------
-- Entity Definition
-------------------------
SampleTicTacToeBoard = {
   Properties = { },
   States = { "Cross", "Circle" },
   Editor = { },
}

local LinkTargetList =  -- Piece place holder object link. These name are linked from SampleTicTacToeBoard entity.
   { 
   "Pos_00" ,"Pos_01" ,"Pos_02" ,
   "Pos_10" ,"Pos_11" ,"Pos_12" ,
   "Pos_20" ,"Pos_21" ,"Pos_22" 
} ;


function SampleTicTacToeBoard:SynchronizedParameters()
-- define which slot value will syncronize. 
   return { PieceStateTable = self.PieceStateTable };
end

-------------------------
-- Initialization 
-------------------------

function SampleTicTacToeBoard:PieceObjSetup () 
 -- ARItem Entity Set from linked entity
   for i = 1, 9 do
      self.GamePiece[i] = self:GetLinkTarget(LinkTargetList[i]);
   end
   -- Rendering Object set
   self.PieceCircleCGF = self:GetLinkTarget( "Circle" ).Properties.fileModel;
   self.PieceCrossCGF  = self:GetLinkTarget( "Cross" ).Properties.fileModel;
   -- Asign rendering object to Entity Slot then use Entity:DrawSlot to choose 
   for i = 1, 9 do 
      self.GamePiece[i]:LoadObject(1, self.PieceCircleCGF) -- for Circle 
      self.GamePiece[i]:DrawSlot(1, 0)   -- CGF using in DrawSlot must have a same material file. 
                                         -- Object is switched, but material will stay. So muat use same material table for all object.
   end
   for i = 1, 9 do 
      self.GamePiece[i]:LoadObject(2, self.PieceCrossCGF) -- for Cross
      self.GamePiece[i]:DrawSlot(2, 0)
   end
end

function SampleTicTacToeBoard:OnInit ()
-- This function called only once when loaded, 
   self.GamePiece        = { } -- Table for Local Game Piece Obj for draw
   self.PieceStateTable  = { } -- Table for keep state locally and syncronize
   for i = 1, 9 do
      self.PieceStateTable[i] = 0 --State value :  0 = none , 1 = Cross, 2 = Circle
   end
   self.NeedUpdate = false  -- Update flag
   ARLazyUtils.OnNextFrame(      -- Execute on next frame,  System may not load entity yet.
      function ()
      self:PieceObjSetup();
      self:GotoState("Circle")
   end)
end

-------------------------
-- Upddate Functions 
-------------------------
function SampleTicTacToeBoard:GamePieceUpdate ()
-- This function will prepare update syncronization.
-- Local state updated with click event, but it is not propergated.
-- Take ownership of dynamic object then propergate to others.
   self:TakeOwnership() 
   self.NeedUpdate = true
end

function SampleTicTacToeBoard:StateUpdate (NewState)
-- It called from State Machine for every frame.
-- After click event, wait owner ship change, then propergate update.
-- If not as owner, then get update from others.
   if self:IsOwnedByLocalAvatar() then
      if self.NeedUpdate then -- update when have-ownership & NeedUpdate-ON 
	 self.NeedUpdate = false
	 self:UpdateState( NewState ,  { PieceStateTable = self.PieceStateTable }) -- Send updated status to others, BUT may failed,.
      end	
   elseif (self.NeedUpdate == false) and self.SyncParameters.PieceStateTable then -- syncronize from oppornent player state
      for i = 1,9 do 
	 self.PieceStateTable[i] = self.SyncParameters.PieceStateTable[i]
      end	
   end
end

function SampleTicTacToeBoard:DrawTable ()
-- It called from state Machine for every frame. for render pieces on table.
-- It uses Drawslot, Clickable object have both circle and cross object on slot.

   DEBUG ( 1, string.format( "Table = %s" , self:GetName()));
   for i = 1, 9 do
      DEBUG ( i+1, string.format( "State Value on %s = %2d:", self.GamePiece[i]:GetName() , self.PieceStateTable[i]));
      if     self.PieceStateTable[i] == 1 then
         self.GamePiece[i]:DrawSlot(1, 0)
         self.GamePiece[i]:DrawSlot(2, 1)  -- Draw Cross
      elseif self.PieceStateTable[i] == 2 then
         self.GamePiece[i]:DrawSlot(1, 1)  -- Draw Circle
         self.GamePiece[i]:DrawSlot(2, 0)
      else                                           -- No Draw
         self.GamePiece[i]:DrawSlot(1, 0) 
         self.GamePiece[i]:DrawSlot(2, 0)
      end
   end
end

-------------------------
-- Event Triggers
-------------------------
---  When click, update local color value if color = 0

function  SampleTicTacToeBoard:OnClickFunction (i)
-- it called after click piece on table.
-- only update with currently empty and avatar is inside of area.
   if (self.PieceStateTable[i] == 0) then
      self.PieceStateTable[i] = self.CurrentState; 
      self:GamePieceUpdate(); 
   else 
      CryAction.Persistant2DText("You can't replace piece.",ARDebugSize,{x=1,y=1,z=1},"ARDebugMessage", 3);
   end
end

--- Click event hundler 
function SampleTicTacToeBoard.Event_Click00(self) self:OnClickFunction(1); end
function SampleTicTacToeBoard.Event_Click01(self) self:OnClickFunction(2); end
function SampleTicTacToeBoard.Event_Click02(self) self:OnClickFunction(3); end
function SampleTicTacToeBoard.Event_Click10(self) self:OnClickFunction(4); end
function SampleTicTacToeBoard.Event_Click11(self) self:OnClickFunction(5); end
function SampleTicTacToeBoard.Event_Click12(self) self:OnClickFunction(6); end
function SampleTicTacToeBoard.Event_Click20(self) self:OnClickFunction(7); end
function SampleTicTacToeBoard.Event_Click21(self) self:OnClickFunction(8); end
function SampleTicTacToeBoard.Event_Click22(self) self:OnClickFunction(9); end

-- Reset button pressed
function SampleTicTacToeBoard.ResetGame(self) -- this function Called from FlowGraph need to be this Style
   -- it called after click on reset button.
   -- after reset new game will start with "Circle" state.
   --initialize slot value
   for i = 1, 9 do
      self.PieceStateTable[i] = 0
   end
   if ( "Cross" == self:GetState()) then -- New turn start from Circle always.
      self:GamePieceUpdate()
   else
      self:GotoState("Cross")
      self:GamePieceUpdate();
   end
end

-- Click Event Trigger setting for Entity (FlowGraph) 
SampleTicTacToeBoard.FlowEvents = {
-- This definition MUST locate after all functions defined.
   Inputs = {
      Click00 = {SampleTicTacToeBoard.Event_Click00, "bool"},
      Click01 = {SampleTicTacToeBoard.Event_Click01, "bool"},
      Click02 = {SampleTicTacToeBoard.Event_Click02, "bool"},
      Click10 = {SampleTicTacToeBoard.Event_Click10, "bool"},
      Click11 = {SampleTicTacToeBoard.Event_Click11, "bool"},
      Click12 = {SampleTicTacToeBoard.Event_Click12, "bool"},
      Click20 = {SampleTicTacToeBoard.Event_Click20, "bool"},
      Click21 = {SampleTicTacToeBoard.Event_Click21, "bool"},
      Click22 = {SampleTicTacToeBoard.Event_Click22, "bool"},
      Reset   = {SampleTicTacToeBoard.ResetGame, "bool"},
   },
   Outputs = { },
}

-------------------------
-- State Machine 
-------------------------
-- There are only 2 state. I try to make simple mechanism for this.
-- Machine moved between only "Circle" and "Cross" for next piece.
-- It will cause to ownership confliction. 
-- But state confliction will be resolved in few second or Reset event.


SampleTicTacToeBoard.Cross = {   -- Next turn is Cross
   OnBeginState = function (self)
		     self.CurrentState = 1
		     self:Activate(1)
		  end,    --- commma is nessesary
   OnUpdate     = function (self)
		     self:StateUpdate("Circle")
		     self:DrawTable()
		  end,
   OnEndState   = function (self)
		     self:ReleaseOwnership() -- it called just before chnaging state.  Expecting second update event was delivered.
		  end,
}			

SampleTicTacToeBoard.Circle = {
   OnBeginState = function (self)
		     self.CurrentState = 2
		     self:Activate(1)
		  end,   
   OnUpdate     = function (self)
		     self:StateUpdate("Cross")
		     self:DrawTable()
		  end,
   OnEndState   = function (self)
		     self:ReleaseOwnership()
		  end,
}

-------------------------
-- ETC
-------------------------

-------- Setup DynamicObject 
ARDynamicObject.Setup(SampleTicTacToeBoard)

--------- Debug use
  function DEBUG ( pos, str )
   HUD.Draw2DLabel( 20 , pos*10+20, 1, str)
  end

See Also


Problems with this wiki page? Contact us either by: Support Email or Support Ticket System

Blue Mars Guidebook Privacy Policy
Blue Mars Guidebook Community Guidelines

Personal tools