In this tutorial we show how to create an online multiplayer game based on preset tables.

 

 Our thanks goes to Matteo Morelli for the development of the Swift version of the sample project.

 

We assume that the developer already owns an account into the ArenaDaemon web platform and

owns the knowledge for creating a new application and retrieving the correspondent appKey.

For more information about how to create a developer account and for setup a new application,

please refer to the chapter "Create an appKey".

 

When the developer wants to create an online multiplayer application, the very first thing to do is

to create tablesThe tables are entities to which the players can register in order to participate

to an online match.

A table has several properties one of which is maxPlayers that specifies the number of the seats

available for the match.

 

A table can be created through the ArenaDaemon web interface, in Dashboard/manage apps section

of a specific application :

 

multiplayer_create_table

 

 

 

We now create a new table called 'Classic' with 2 seats available :

 

multiplayer_create_table2

 

 

 

NOTE

 

The LIVE and SANDBOX checkboxes specify the environments to which the table belongs.

If the developer runs the mobile application in the SANDBOX environment (this can be done by

initializing the BDArenaConnector unique instance passing NO to the second parameter of the

intializeWithAppKey:runInSandboxEnvironment: method) only the tables for which the SANDBOX

checkbox has been flagged, will be shown in the list of the available tables.

The same logic is applied for the LIVE environment.

 

 

Now that a table has been created, the coding of the mobile appication can start!

 

Create a new Xcode project and include the SDK as shown in chapter SDK Installation and setup.

 

The next step is to create :

  • UIViewController that brings a UITableView (we'll give it the name 'TablesViewController') through which the players will register to the tables.
  • UIViewController that manages the match once started (we'll give it the name 'MatchViewController'). 

multiplayer_create_controllers

 

 

Before doing any action with the daemons, the local player needs to be authenticated so we need

to add the following code to the AppDelegate class :

 

 

- (BOOL)application:(UIApplication *)application

        didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{

 

        .....

        // init BDArenaConnector and register current object to observers

        [BDArenaConnector initializeWithAppKey:@"YOUR_APP_KEY" 

                runInSandboxEnvironment:YES];

        [[BDArenaConnector getInstance] registerEventsObserver:self];

        [[BDArenaConnector getInstance] requestAuth];

        .....

 

        return YES;

}

 

- (void) dealloc{

        // unregister current object from observers

        [[BDArenaConnector getInstance] unregisterEventsObserver:self];

}

 

#pragma mark - BDArenaConnectorDelegate

 

- (void) arenaConnector:(BDArenaConnector*)connector

        authReceivedForLocalPlayerWithData:(BDArenaPlayerData*)playerData

        alreadyMet:(BOOL)alreadyMet

        isOffline:(BOOL)isOffline{

 

        TablesViewController* tablesController = [[TablesViewController alloc]

                initWithNibName:@"TablesViewController"bundle:nil];

        UINavigationController* navController = [[UINavigationController alloc]

                initWithRootViewController:tablesController];

 

        self.window.rootViewController= navController;

}

 

- (void) arenaConnector:(BDArenaConnector*)connector

        authFailedForLocalPlayerWithError:(NSError*)error{

        // handle errors

}

 

 

 

Now we can implement the TablesViewController by adding an NSMutableArray to store

the list of available tables and two integers for storing the number of

active players and active matches:

 

 

#import <UIKit/UIKit.h>

#import <BDArenaConnector/BDArenaConnector.h>

 

@interface TablesViewController : UIViewController

        <UITableViewDataSource, UITableViewDelegate>{

        NSMutableArray* tablesArray;

        NSInteger              numberOfActivePlayers;

        NSInteger              numberOfActiveMatches;

}

@property (nonatomic, strong) IBOutlet UITableView* table;

 

@end

 

 

 

In order to be notified about SDK events, in the initWithNibName:bundle: method

of TablesViewController we add :

 

 

- (id)initWithNibName:(NSString *)nibNameOrNil

        bundle:(NSBundle *)nibBundleOrNil {

 

    self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];

    if (self) {

        // Custom initialization

        tablesArray= [[NSMutableArray alloc] init];

 

        // add current controller to observers

        [[BDArenaConnector getInstance] registerEventsObserver:self];

    }

    return self;

}

 

 

 

Once the controller is deallocated, we need to remove it from observers :

 

 

- (void) dealloc{

        // remove current controller from observers

        [[BDArenaConnector getInstance] unregisterEventsObserver:self];

}

 

 

 

The first thing to do now, is to connect to Multiplayer daemon : to achieve this, we implement

the viewDidAppear method as follows :

 

 

- (void) viewDidAppear:(BOOL)animated{

 

         // check if already connected to multiplayer daemon.

         if([[BDArenaConnector getInstance].playConnector isConnected] == NO)

                  [[BDArenaConnector getInstance].playConnector connect];

}

 

 

For managing the results of the above requests, we need to implement

arenaPlay:requestDidFail:arenaPlayConnectionEstabilishedarenaPlayConnectionFailedWithError: and 

arenaPlay:tablesListReceived:activeMatches:activePlayers: 

 

 

- (void) arenaPlay:(BDArenaPlayConnector*)connector requestDidFail:(NSError*)error{

        // handle error here

        // ...

 

        // refresh tables list after 1 sec

        [[BDArenaConnectorgetInstance].playConnector 

                performSelector:@selector(getTablesList) withObject:nil afterDelay:1.0];

}

 

- (void) arenaPlayConnectionFailedWithError:(NSError*)error{

        // handle error here

        // ...

 

        // refresh tables list after 1 sec

        [[BDArenaConnectorgetInstance].playConnector 

                performSelector:@selector(getTablesList) withObject:nil afterDelay:1.0];

}

 

- (void) arenaPlayConnectionEstabilished{

        // connection estabilished. request tables list

        [[BDArenaConnector getInstance].playConnector getTablesList];

}

 

- (void) arenaPlay:(BDArenaPlayConnector*)connector

        tablesListReceived:(BDArenaTablesArray*)tables

        activeMatches:(NSInteger)activeMatches

        activePlayers:(NSInteger)activePlayers{

 

        // tables list received. fill tables Array

        [tablesArray removeAllObjects];

        [tablesArray addObjectsFromArray:tables];

 

        // store active matches and players

        numberOfActivePlayers = activePlayers;

        numberOfActiveMatches = activeMatches;

 

        // refresh tableView

        [table reloadData];

 

        // refresh tables list after 1 sec

        [[BDArenaConnector getInstance].playConnector 

                performSelector:@selector(getTablesList) withObject:nil afterDelay:1.0];

}

 

 

For showing retrieved tables list, in the tableView:cellForRowAtIndexPath: we use two UILabel and an UIButton.

The data are accessed in the following way :

 

 

- (UITableViewCell *)tableView:(UITableView *)tableView

        cellForRowAtIndexPath:(NSIndexPath *)indexPath{

 

        NSString* cellId = @"CellId";

        .....

 

        // retrieve table's data at indexPath

        BDArenaTableData* tableData = [tablesArray objectAtIndex:indexPath.row];

 

        // set table name

        symbolicNameLabel.text = tableData.symbolicName;

 

        // set number of registered users and total number of available seats

        seatsAvailableLabel.text = [NSString stringWithFormat:@"%d/%d",

                tableData.registeredPlayers,

                tableData.maxPlayers];

        

        // set text of the registration/unregistration button 

        (tableData.alreadyRegistered) ?

                ([registerButton setTitle:@"unregister" forState:UIControlStateNormal]) :

                ([registerButton setTitle:@"register" forState:UIControlStateNormal]);

 

        .....

        return cell;

}

 

 

 

In order to register the local player to a table for partecipating the match, we need to call

registerToTableWithId: and 

unregisterFromTableWithId:,

retrieving the results implementing 

arenaPlay:registrationSuccessfullyCompletedToTable:withMatchId:,

arenaPlay:alreadyRegisteredToTableWithId:,

arenaPlay:unregistrationSuccessfullyCompletedFromTableWithId:,

arenaPlay:unregistrationFailedFromTableWithId: and

arenaPlay:notRegisteredToTableWithId:

 

The application now looks as follow :

tutorial_multiplayer_screens

 

The match will start when both seats will be occupied by the players.

When this event will occur, the application will be notified through arenaPlay:matchDidStart: protocol method.

 

 

- (void) arenaPlay:(BDArenaPlayConnector*)connector

        matchDidStart:(BDArenaMatchData*)matchData{

        

        // allocate match view controller passing 'matchData' object through a custom init method

        MatchViewController* matchController = [[MatchViewController alloc]

                initWithNibName:@"MatchViewController" 

                bundle:nil

                withMatchData:matchData];

 

        // navigate to match

        [self.navigationController pushViewController:matchController animated:YES];

}

 

 

The MatchViewController presents an UITableView, through which are listed the participants

to the match, a set of UILabel and UIImageView.

 

Within this UIViewController, the developer must implement the main logic of his game

by communicating with remote players through sendMessage:ToPlayerWithAuid:inMatchWithId: or 

sendMessageToPlayers:inMatchWithId: methods.

 

If the local player wants to leave the current match, he needs to call leaveMatchWithId: method

before the application deallocates the MatchController.

 

For all the participants to a match, all the events can be managed by implementing 

arenaPlay:messageSuccessfullySent:inMatchWithId:,

arenaPlay:newMessageReceived:fromPlayer:inMatchWithId:,

arenaPlay:messageSuccessfullySent:toPlayerWithAuid:inMatchWithId:,

arenaPlay:matchSuccessfullyLeft:,

arenaPlay:leaveMatchWithId:didFailWithError:,

arenaPlay:playerWithAuid:leftMatchWithId: and

arenaPlay:playersListReceived:forMatchId:

 

This sample project implements a Tic-Tac-Toe game and the MatchController looks like follows :

tutorial_multiplayer_ttt

 


 

 

Sample project

Download the sample project for this tutorial [Objective-C | Swift].