Corona Tic Tac Toe Tutorial

*We assure you that we do not spam. You may receive occasional emails from us.
 You can always unsubscribe.

Corona Realtime Turn Based Game Tutorial

Corona Sample Game

What are we going to do?
  • We have created a TicTacToe game using corona.
  • We will make it 2-player realtime multiplayer game using AppWarp Cloud.
  • We will create realtime turn based tic tac toe game using AppWarp TurnBasedRoom API.
  • TicTacToe is very simple game to know more about game Click Here.
  • The user will get real-time feedback about the other user’s performance adding to the excitement of the game.
Project setup

Download AppWarp Corona sample from git repository.

This repo contain SDK and sample. Open TicTacToe sample in corona simulator.

Getting your AppWarp application keys

Since you will be integrating with AppWarp cloud services, you need to get your application keys from ShepHertz developer dashboard AppHq. These keys identify your application zone on ShepHertz cloud service and are required so that AppWarp cloud can segregate messages belonging to different applications.

Follow the simple steps by signing up (free) and getting your application keys mentioned at AppWarp.

Now open main.lua file in the TicTacToe project and add the values there. For example

API_KEY = "YOUR_API_KEY_HERE"
SECRET_KEY = "YOUR_SECRET_KEY_HERE"
Running the Tic Tac Toe sample

Game Start Screen

Now that we’ve got everything ready, we can run the sample and see the game in action. Since this game is two player game you will need to run this on 2 emulators/devices simultaneously to play multiplayer game.

After you tap to connect button, the game will connect to AppWarp and join a game room. Once inside the game room, the client will wait for the second player to join the room before the game will start.

Game Waiting Screen

Now you need to do the same from the second emulator/device. AppWarp matchmaking APIs will make this second user join the same game room and the game should begin. Since in this example we are using AppWarp Turn Based API so once both user connect one user start the game. To know more about AppWarp TurnBasedRoom Click Here. User can send his move and receive Opponent move in realtime. This in-game realtime communication is the power of AppWarp.

Game will be considered completed in these three condition

  1. User Left: In this case one player left the game other player will be considered as winning user as his opponent has left the game.
  2. Game Complete: If any user completes the game.
  3. Game Draw: If no space left to complete move.
  4. Time Over: Since this is turn based game if user doesn’t send his move in turn time(30 sec here) then this user will be considered as looser.

Game Over Screen

How does the integration with AppWarp work

Starting the game

First you need to initialize the WarpClient singleton with your application keys (main.lua)

appWarpClient = require "AppWarp.WarpClient"
appWarpClient.initialize(API_KEY, SECRET_KEY)

Next you need to call the gameLoop in every frame

       local function gameLoop(event)
       appWarpClient.Loop()
       end
       Runtime:addEventListener("enterFrame", gameLoop)
    

Note that AppWarp SDK provides its functionality through asynchronous APIs. This means you simply add the corresponding request listeners to the WarpClient instance to receive responses and notifications.

The file (ConnectScene.lua) has all the code we need to do this step. It will make connection requests, room requests and zone requests (to create a room if required). So we add the corresponding listeners in OnStart()

public WarpController()
{
	...	
        appWarpClient.addRequestListener("onConnectDone", scene.onConnectDone)  
        appWarpClient.addRequestListener("onDisconnectDone", scene.onDisconnectDone)  
        appWarpClient.addRequestListener("onJoinRoomDone", scene.onJoinRoomDone)  
        appWarpClient.addRequestListener("onCreateRoomDone", scene.onCreateRoomDone)  
        appWarpClient.addRequestListener("onSubscribeRoomDone", scene.onSubscribeRoomDone)  
        appWarpClient.addNotificationListener("onUserJoinedRoom", scene.onUserJoinedRoom)
}

Now that the listener is setup, we can go ahead and connect. To connect to AppWarp cloud, the client needs to pass in a unique username. In the sample I will just use a random string The random username string is generated using os.clock().

appWarpClient.connectWithUserName(USER_NAME)

The result of connection is provided in the following callback

function scene.onConnectDone(resultCode)
  if(resultCode == WarpResponseResultCode.SUCCESS) then
    statusText.text = "Joining room.."    
    appWarpClient.joinRoomInRange (1, 1, false)
  else
    statusText.text = "onConnectDone: Failed"..resultCode;
  end
end

If its successful, we go ahead and try to join a room. To join the room, we use the JoinRoomInRange method with parameters (1,1) which request the server to put the client in a room with exactly 1 user in it. If this fails we will create a new 2 player room and join that.

function scene.onJoinRoomDone(resultCode, roomId)
  if(resultCode == WarpResponseResultCode.SUCCESS) then
    appWarpClient.subscribeRoom(roomId)
  elseif(resultCode == WarpResponseResultCode.RESOURCE_NOT_FOUND) then
    -- no room found with one user creating new room
    local roomPropertiesTable = {}
    roomPropertiesTable["result"] = ""
    ROOM_ADMIN = USER_NAME
    appWarpClient.createTurnRoom ("TicTacToeRoom", ROOM_ADMIN, 2, roomPropertiesTable, 30)
  else
    statusText.text = "onJoinRoomDone: failed"..resultCode
  end  
end

Once the room is joined (either now or after creating a new one), the client needs to subscribe it. This is required to receive notifications from the room (required in game play). These concepts are explained in details here. If room has two users then we start the game otherwise wait for other user to join this room.

appWarpClient.subscribeRoom(roomId)
function scene.onSubscribeRoomDone(resultCode, roomId)
  if(resultCode == WarpResponseResultCode.SUCCESS) then
    ROOM_ID = roomId;
    if(isNewRoomCreated) then
      waitForOtherUser()
    else
      startGame()
    end
  else
    statusText.text = "subscribeRoom failed"
  end  
end

Game Play

The code for the game play is in the file (GameScene.lua). If user enter on this screen this means that both user are in room. One user(RoomAdmin) get start game screen. As user tap on screen game is started. Player will has to play his game and he will also get update of other user.

To start the game call startGame() API

appWarpClient.startGame()

As the player send his move to complete game, remote player also get notification of his opponent’s moves so it can also render the movement see in (GameScene.lua)

function updateUI(i, j, t) 
  local group = display.newGroup();
  ARRAY[i][j] = t
  GAP = 256/3
  DRAW_X = START_X + (j*GAP) + GAP/2 - OBJECT_WIDTH/2
  DRAW_Y = START_Y + (i*GAP) + GAP/2 - OBJECT_WIDTH/2
    if( t == "0" ) then
      object = display.newImage( "button_AI.png", DRAW_X, DRAW_Y )
      IMAGE_ARRAY[i][j] = object
    elseif ( t == "X" ) then
      object = display.newImage( "button_exit.png", DRAW_X, DRAW_Y )
      IMAGE_ARRAY[i][j] = object
    end
end

The move are sent using sendMove() API.

appWarpClient.sendMove(i .. "/" .. j .. "#" .. "")-- here i and j are array position. "/" and "#" are used as separator
 statusText.text = "Move Sent"

The moves sent to the room are provided through the onMoveCompleted callback. In this callback we parse the data associated with the message and identify the sender. We react on the messages accordingly.

function scene.onMoveCompleted(sender, roomId, nextTurn, moveData)
  ....
  if(sender ~= USER_NAME) then
    if(string.len(moveData)>0) then
      i = string.sub(moveData, 0, string.find(moveData, "/")-1)
      j = string.sub(moveData, string.find(moveData, "/")+1, string.find(moveData, "#")-1)
      data = string.sub(moveData, string.find(moveData, "#")+1, string.len(moveData))
      if(TYPE == "X") then
        updateUI(tonumber(i), tonumber(j), "0");
      elseif (TYPE == "0") then
        updateUI(tonumber(i), tonumber(j), "X");
      end
      ....
    else 
      handleFinishGame("WIN", "TIME_OVER")
    end
  end
end

Game Over

When the game finish we simply sendMove with game status. As other user receive notification it update its UI depending upon message received.

As the game finish other user get notification update the screen on GameScene.lua depending upon the data to show the reason to finish game.

function handleFinishGame(result, detail) 
  if(isGameRunning == false) then
    return
  end
  if (result == "WIN") then
    statusText.text = "Congrats! Game Won"
  elseif(result == "LOOSE") then
    statusText.text = "Oops! Game Loose"
  elseif(result == "DRAW") then  
    statusText.text = "Match Draw"
  end
  ....
  
end

We also need to leave and unsubscribe the room as well as remove the listener and if the game is not in running mode then we also delete rooms. Since in this game we are using AppWarp dynamic rooms, its good practice to delete them once used (Empty dynamic rooms will anyway be automatically deleted after 60 minutes).

isGameRunning = false
  isGameOver = true
  appWarpClient.stopGame()
  appWarpClient.unsubscribeRoom(ROOM_ID);
  appWarpClient.leaveRoom(ROOM_ID);
  appWarpClient.deleteRoom(ROOM_ID);
  ROOM_ID = ""
  appWarpClient.disconnect()

The user can tap and go back to the Main Scene from here and we will restart the process. However this time we can simply start by finding a room (as we will already be connected).

Summary

In this article we saw how we can develop a multiplayer game using AppWarp Corona SDK. We saw how clients connect to AppWarp, join and play in game rooms. The integration concepts are independent of the use of corona and can be applied for any platform application as we have all supported platform SDK available for AppWarp.