This is the second installment of the Minesweeper game mini-series. In this tutorial, you will learn how to fully implement the client side of the application. We will use a native iOS application that connects to the web servers, parses the data, and displays the user interface interaction.
Project Overview
Minesweeper Flag is a multi-player board game that is played between two opponents. Commonly, blue or red is assigned to each player. The board is composed by 256 equal squares, and each board has 51 mines placed in entirely random positions.
Review of Part 1
Before you start the second part of the series make sure you have the first part fully tested and implemented.
Step 1: Installing Cocos 2D
The first step is to download and install the Cocos 2D game engine. For this tutorial, we used Cocos 2D version 2.
Once downloaded, you need to install it and integrate it with Xcode. Unpack the file downloaded before and you will notice a file called install-templates.sh. Open a terminal window and run that file using the following command
|
1 |
./install-templates -f. |
Step 2: Create a New Project
Using Xcode, create a new project: File -> New -> Project.
Choose the Cocos2d v2.x on the left side menu and Cocos2d iOS on the right one.
Step 3: Configure Project Settings
Give your project a name and a company identifier. For this tutorial, we use the iPad device family option.
Step 4: Verify the Setup
If everything is correct you should see a window similar to the following one:
Step 5: Add New Classes
Now you should add two new Objective-c classes. To do this, access the menu option File -> New -> File.
Add the following classes:
- LoadingScene: This class will serve as a loading screen for the user while he / she is waiting for another player to join the game.
- GameScene: This is the core class of the game; is where the main logic is programmed.
Step 6: Add Resource Files
In order to correctly run the game you should add the resource files to your project. Here you can download the resource files used. Next, copy them to the resources folder.
Step 7: Create the HelloWorldLayer Class
Change your HelloWorldLayer header file to the following:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
#import <GameKit/GameKit.h> #import "cocos2d.h" @interface HelloWorldLayer : CCLayer {} @property (nonatomic, retain) NSURLConnection* connectionConnectGame; @property (nonatomic,retain) NSMutableData* responseData; @property (nonatomic,retain) NSString* URL; @property (nonatomic) int playerID; @property (nonatomic,retain) NSString* playerIDString; @property (nonatomic) BOOL launchFlag; @property (nonatomic,retain) NSString* gameID; @property (nonatomic,retain) NSString* turnID; +(CCScene *) scene; -(void) connectTheWebService:(NSString*)id; @end |
Change the HelloWorldLayer implementation file to the following:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 |
#import "HelloWorldLayer.h" #import "GameScene.h" #import "LoadingScene.h" #import "cocos2d.h" #import "AppDelegate.h" #pragma mark - HelloWorldLayer @implementation HelloWorldLayer @synthesize connectionConnectGame = _connectionConnectGame; @synthesize URL = _URL; @synthesize responseData = _responseData; @synthesize launchFlag = _launchFlag; @synthesize gameID = _gameID; @synthesize playerID = _playerID; @synthesize playerIDString = _playerIDString; @synthesize turnID = _turnID; +(CCScene *) scene{ CCScene *scene = [CCScene node]; HelloWorldLayer *layer = [HelloWorldLayer node]; [scene addChild: layer]; return scene; } -(id) init { if( (self=[super init]) ) { [self setLaunchFlag:FALSE]; // CHANGE THE LINK accordingly [self setURL:@"http://10.0.6.178:8080/Minesweeper_Flag/resources/generic/connectGame/"]; CGSize size = [[CCDirector sharedDirector] winSize]; // Background CCSprite * background = [CCSprite spriteWithFile:@"background.jpg"]; background.position = ccp(size.width/2,size.height/2); [self addChild:background]; CCSprite* flags = [CCSprite spriteWithFile:@"bandeiras.png"]; flags.scale = .30; flags.position = ccp(size.width - size.width/1.15,size.height - size.height/1.25); [self addChild:flags]; // Title Label CCLabelTTF *label = [CCLabelTTF labelWithString:@"Minesweeper Flags" fontName:@"Marker Felt" fontSize:64]; label.position = ccp( size.width /2 , size.height - size.height/4 ); [self addChild: label]; // Player ID generation NSDateFormatter *date = [[NSDateFormatter alloc] init]; [date setDateFormat:@"mm"]; int minute = [[date stringFromDate:[NSDate date] ]intValue]; [date setDateFormat:@"ss"]; int seconds = [[date stringFromDate:[NSDate date]] intValue]; int pID = minute + seconds + (rand() % 1000000); [self setPlayerID:pID]; NSString* playerIDInStringFormat = [NSString stringWithFormat:@"%d", _playerID]; [self setPlayerIDString:playerIDInStringFormat]; NSLog(@"ID DO PLAYER %@",_playerIDString); [self setResponseData:[NSMutableData data]]; // Menus [CCMenuItemFont setFontSize:38]; CCMenuItem *itemNewGame = [CCMenuItemFont itemWithString:@"New Game" block:^(id sender) { [self connectTheWebService:playerIDInStringFormat]; }]; CCMenu *menu = [CCMenu menuWithItems:itemNewGame ,nil]; [menu alignItemsVerticallyWithPadding:20]; [menu setPosition:ccp( size.width/2, size.height/2 - 50)]; [self addChild:menu]; } return self; } //connect the Web server method -(void) connectTheWebService:(NSString*)id{ NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:[[_URL autorelease] stringByAppendingString:id] ]]; _connectionConnectGame = [[NSURLConnection alloc] initWithRequest:request delegate:self]; } //Web server responses. - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response { [_responseData setLength:0]; } - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data { [_responseData appendData:data]; } - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error { NSLog(@"Connection failed: %@", [error description]); } - (void)connectionDidFinishLoading:(NSURLConnection *)connection { NSString* responseString = [[NSString alloc] initWithData:_responseData encoding:NSUTF8StringEncoding]; NSLog(@"HelloWorld responseString %@",responseString); NSArray* resultArray = [[NSArray alloc] init]; resultArray = [responseString componentsSeparatedByString:@","]; if ([[resultArray objectAtIndex:1] isEqualToString:@"no"]) { [self setGameID:[resultArray objectAtIndex:0]]; [self setLaunchFlag:TRUE]; } else { [self setGameID:[resultArray objectAtIndex:0]]; _playerID = [[resultArray objectAtIndex:1] integerValue]; [self setPlayerID:[[resultArray objectAtIndex:1] integerValue]]; [self setLaunchFlag:FALSE]; } if (_launchFlag){ GameScene* scene = [[GameScene alloc] initializeInternalDataWithPlayerID:_playerIDString andWithGameID:_gameID andWithTurnID:@"no"]; [[CCDirector sharedDirector] replaceScene:scene]; } else { LoadingScene* scene = [LoadingScene initializeWithPlayerID:_playerIDString andWithGameID:_gameID]; [[CCDirector sharedDirector] replaceScene:scene]; } } - (void) dealloc { [super dealloc]; } @end |
Step 8: Create the LoadingScene Class
Change your LoadingScene header file to the following:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 |
#import "cocos2d.h" @interface LoadingScene : CCLayer <UIGestureRecognizerDelegate>{ } @property (nonatomic, retain) NSURLConnection *connectionConnectGame; @property (nonatomic,retain) NSMutableData* responseData; @property (nonatomic,retain) NSString* gameID; @property (nonatomic,retain) NSString* playerID; @property (nonatomic) BOOL launchFlag; @property (nonatomic,retain) NSString* URL; +(CCScene *) scene; +(id) initializeWithPlayerID:(NSString*) playerid andWithGameID:(NSString*) gameid; -(id) initializeInternalDataWithPlayerID:(NSString*) playerid andWithGameID:(NSString*) gameid; -(void) connectTheWebService:(NSString*)id; @end |
Change your LoadingScene implementation file to the following:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 |
#import "LoadingScene.h" #import "GameScene.h" @implementation LoadingScene @synthesize responseData = _responseData; @synthesize connectionConnectGame = _connectionConnectGame; @synthesize gameID = _gameID; @synthesize playerID = _playerID; @synthesize launchFlag = _launchFlag; @synthesize URL = _URL; +(CCScene *) scene{ CCScene *scene = [CCScene node]; LoadingScene *layer = [LoadingScene node]; [scene addChild: layer]; return scene; } -(id) init{ if( (self=[super init] )) {} return self; } // Custom initializer +(id) initializeWithPlayerID:(NSString*) playerid andWithGameID:(NSString*) gameid { return [[[self alloc] initializeInternalDataWithPlayerID:playerid andWithGameID:gameid] autorelease]; } -(id) initializeInternalDataWithPlayerID:(NSString*) playerid andWithGameID:(NSString*) gameid { self = [super init]; if (self) { CGSize _size = [[CCDirector sharedDirector] winSize]; _gameID = [[NSString alloc] init]; [self setLaunchFlag:FALSE]; [self setPlayerID:playerid]; [self setGameID:gameid]; CCSprite* background = [CCSprite spriteWithFile:@"background.jpg"]; background.position = ccp(_size.width/2,_size.height/2); [self addChild:background]; CCLabelTTF* label = [CCLabelTTF labelWithString:@"Loading..." fontName:@"Marker Felt" fontSize:40]; label.position = ccp(_size.width/2, _size.height/2); [self addChild:label]; CCLabelTTF* label2 = [CCLabelTTF labelWithString:@"Waiting for another player" fontName:@"Marker Felt" fontSize:25]; label2.position = ccp(_size.width/2, _size.height - _size.height/1.80); [self addChild:label2]; [self setResponseData:[NSMutableData data]]; // CHANGE IT ACCORDINGLY [self setURL:@"http://10.0.6.178:8080/Minesweeper_Flag/resources/generic/player2Available/"]; [self schedule:@selector(update:) interval:2]; } return self; } -(void) update:(ccTime) dt{ [self connectTheWebService:_gameID]; } -(void) connectTheWebService:(NSString*)id{ NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:[[_URL autorelease] stringByAppendingString:id] ]]; _connectionConnectGame = [[NSURLConnection alloc] initWithRequest:request delegate:self]; } - (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response { [_responseData setLength:0]; } - (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data { [_responseData appendData:data]; } - (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error { NSLog(@"Connection failed: %@", [error description]); } - (void)connectionDidFinishLoading:(NSURLConnection *)connection { NSString* responseString = [[NSString alloc] initWithData:_responseData encoding:NSUTF8StringEncoding]; if ([responseString isEqualToString:@"nulo"]) { [self setLaunchFlag:FALSE]; }else { [self setLaunchFlag:TRUE]; } if (_launchFlag){ GameScene* scene = [[GameScene alloc] initializeInternalDataWithPlayerID:_playerID andWithGameID:_gameID andWithTurnID:_playerID] ; [[CCDirector sharedDirector] replaceScene:scene]; } else { [self schedule:@selector(update:) interval:2]; } } -(void)dealloc { [super dealloc]; } @end |
Step 9: Create the GameScene Class
Change your GameScene header file to the following:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
#import "cocos2d.h" @interface GameScene : CCLayer <UIGestureRecognizerDelegate> { NSString* turn; } @property (nonatomic) int gameGridSize, initialSpriteXXPosition, initialSpriteYYPosition, deslocamentoNoEixoXX, deslocamentoNoEixoYY, tamanhoSprite, playerBlueScore, playerRedScore, lastSpriteTouch; @property (nonatomic, retain) NSMutableArray *spritesMutableArray; @property (nonatomic,retain) NSMutableArray* touchedLocations; @property (nonatomic) CGSize size; @property (nonatomic) CGPoint initialSpritePositon; @property (nonatomic, retain) CCSprite* sprites; @property (nonatomic) int spritePositionInGrid; @property (nonatomic) int webServerResult; @property (nonatomic, strong) NSMutableData *responseData; @property (nonatomic, strong) NSString *resourceImage; @property (nonatomic, retain) CCSprite* playerBlueSprite; @property (nonatomic, retain) CCSprite* playerRedSprite; @property (nonatomic, retain) CCSprite* playerTurnSprite; @property (nonatomic, retain) CCSprite* youArrow; @property (nonatomic,retain) CCLabelTTF* playerBlueLabel; @property (nonatomic,retain) CCLabelTTF* playerRedLabel; @property (nonatomic,retain) NSString* gameID; @property (nonatomic,retain) NSString* userID; @property (nonatomic,retain) NSString* colorPlayer; @property (nonatomic,retain) CCSprite* positionTouchedSprite; @property (nonatomic,retain) NSString* turn; @property (nonatomic, retain) NSMutableString* gameBoardInfoS; +(CCScene *) scene; -(void)getMinesAtPosition:(int)pos; -(void)drawGameBoard; -(void)setInitialAppValues; -(void)setArrowonTheRightPlayer; +(id) initializeWithPlayerID:(NSString*) playerid andWithGameID:(NSString*) gameid andWithTurnID:(NSString*) turnid; -(id) initializeInternalDataWithPlayerID:(NSString*) playerid andWithGameID:(NSString*) gameid andWithTurnID:(NSString*) turnid; @end |
- 1 2
































