Crea un juego multijugador de Buscaminas: creación del lado del cliente
Spanish (Español) translation by Claudia Márquez (you can also view the original English article)
Esta es la segunda entrega de la miniserie del juego Minesweeper. En este tutorial, aprenderá a implementar completamente el lado del cliente de la aplicación. Utilizaremos una aplicación nativa de iOS que se conecta a los servidores web, analiza los datos y muestra la interacción de la interfaz de usuario.
Visión general del proyecto
Minesweeper Flag es un juego de mesa multijugador que se juega entre dos oponentes. Comúnmente, se asigna azul o rojo a cada jugador. El tablero está compuesto por 256 cuadrados iguales, y cada tablero tiene 51 minas colocadas en posiciones completamente aleatorias.



Vista previa del efecto Final
Revisión de la Parte 1
Antes de comenzar la segunda parte de la serie, asegúrese de tener la primera parte totalmente probada e implementada.
Paso 1: Instalación Cocos 2D
El primer paso es descargar e instalar el motor de juego 2D Cocos. Para este tutorial, usamos Cocos2d versión 2.
Una vez descargado, debe instalarlo e integrarlo con Xcode. Desempaque el archivo descargado anteriormente y verá un archivo llamado install-templates.sh. Abra una ventana de terminal y ejecute ese archivo usando el siguiente comando
1 |
|
2 |
./install-templates -f. |
Paso 2: Crea un nuevo proyecto
Usando Xcode, crea un nuevo proyecto: Archivo -> Nuevo -> Proyecto.


Elija Cocos2d v2.x en el menú de la izquierda y Cocos2d iOS en la de la derecha.
Paso 3: Configurar los ajustes del proyecto
Dale a tu proyecto un nombre y un identificador de empresa. Para este tutorial, utilizamos la opción de familia de dispositivos iPad.


Paso 4: Verificar la configuración
Si todo es correcto, debería ver una ventana similar a la siguiente:


Paso 5: Agregar nuevas clases
Ahora debería agregar dos nuevas clases de Objective-c. Para hacer esto, acceda a la opción de menú Archivo -> Nuevo -> Archivo.
Agregue las siguientes clases:
- LoadingScene: Esta clase servirá como una pantalla de carga para el usuario mientras espera que otro jugador se una al juego.
- GameScene: Esta es la clase principal del juego; es donde está programada la lógica principal.
Paso 6: Agregar archivos de recursos
Para ejecutar correctamente el juego, debe agregar los archivos de recursos a su proyecto. Aquí puede descargar los archivos de recursos utilizados. Luego, cópielos a la carpeta de recursos.
Paso 7: Crea la clase HelloWorldLayer
Cambie su archivo de encabezado HelloWorldLayer a lo siguiente:
1 |
|
2 |
#import <GameKit/GameKit.h>
|
3 |
#import "cocos2d.h"
|
4 |
|
5 |
@interface HelloWorldLayer : CCLayer {} |
6 |
|
7 |
@property (nonatomic, retain) NSURLConnection* connectionConnectGame; |
8 |
@property (nonatomic,retain) NSMutableData* responseData; |
9 |
@property (nonatomic,retain) NSString* URL; |
10 |
@property (nonatomic) int playerID; |
11 |
@property (nonatomic,retain) NSString* playerIDString; |
12 |
@property (nonatomic) BOOL launchFlag; |
13 |
@property (nonatomic,retain) NSString* gameID; |
14 |
@property (nonatomic,retain) NSString* turnID; |
15 |
|
16 |
+(CCScene *) scene; |
17 |
-(void) connectTheWebService:(NSString*)id; |
18 |
|
19 |
@end
|
Cambie el archivo de implementación de HelloWorldLayer a lo siguiente:
1 |
|
2 |
#import "HelloWorldLayer.h"
|
3 |
#import "GameScene.h"
|
4 |
#import "LoadingScene.h"
|
5 |
#import "cocos2d.h"
|
6 |
#import "AppDelegate.h"
|
7 |
|
8 |
#pragma mark - HelloWorldLayer
|
9 |
|
10 |
@implementation HelloWorldLayer |
11 |
|
12 |
@synthesize connectionConnectGame = _connectionConnectGame; |
13 |
@synthesize URL = _URL; |
14 |
@synthesize responseData = _responseData; |
15 |
@synthesize launchFlag = _launchFlag; |
16 |
@synthesize gameID = _gameID; |
17 |
@synthesize playerID = _playerID; |
18 |
@synthesize playerIDString = _playerIDString; |
19 |
@synthesize turnID = _turnID; |
20 |
|
21 |
+(CCScene *) scene{ |
22 |
CCScene *scene = [CCScene node]; |
23 |
|
24 |
HelloWorldLayer *layer = [HelloWorldLayer node]; |
25 |
|
26 |
[scene addChild: layer]; |
27 |
|
28 |
return scene; |
29 |
}
|
30 |
|
31 |
-(id) init { |
32 |
if( (self=[super init]) ) { |
33 |
|
34 |
[self setLaunchFlag:FALSE]; |
35 |
|
36 |
// CHANGE THE LINK accordingly
|
37 |
[self setURL:@"http://10.0.6.178:8080/Minesweeper_Flag/resources/generic/connectGame/"]; |
38 |
|
39 |
CGSize size = [[CCDirector sharedDirector] winSize]; |
40 |
|
41 |
// Background
|
42 |
CCSprite * background = [CCSprite spriteWithFile:@"background.jpg"]; |
43 |
background.position = ccp(size.width/2,size.height/2); |
44 |
[self addChild:background]; |
45 |
|
46 |
CCSprite* flags = [CCSprite spriteWithFile:@"bandeiras.png"]; |
47 |
flags.scale = .30; |
48 |
flags.position = ccp(size.width - size.width/1.15,size.height - size.height/1.25); |
49 |
[self addChild:flags]; |
50 |
|
51 |
// Title Label
|
52 |
CCLabelTTF *label = [CCLabelTTF labelWithString:@"Minesweeper Flags" fontName:@"Marker Felt" fontSize:64]; |
53 |
label.position = ccp( size.width /2 , size.height - size.height/4 ); |
54 |
[self addChild: label]; |
55 |
|
56 |
// Player ID generation
|
57 |
NSDateFormatter *date = [[NSDateFormatter alloc] init]; |
58 |
[date setDateFormat:@"mm"]; |
59 |
int minute = [[date stringFromDate:[NSDate date] ]intValue]; |
60 |
[date setDateFormat:@"ss"]; |
61 |
int seconds = [[date stringFromDate:[NSDate date]] intValue]; |
62 |
int pID = minute + seconds + (rand() % 1000000); |
63 |
[self setPlayerID:pID]; |
64 |
|
65 |
NSString* playerIDInStringFormat = [NSString stringWithFormat:@"%d", _playerID]; |
66 |
[self setPlayerIDString:playerIDInStringFormat]; |
67 |
NSLog(@"ID DO PLAYER %@",_playerIDString); |
68 |
[self setResponseData:[NSMutableData data]]; |
69 |
|
70 |
// Menus
|
71 |
[CCMenuItemFont setFontSize:38]; |
72 |
CCMenuItem *itemNewGame = [CCMenuItemFont itemWithString:@"New Game" block:^(id sender) { |
73 |
|
74 |
[self connectTheWebService:playerIDInStringFormat]; |
75 |
}];
|
76 |
|
77 |
CCMenu *menu = [CCMenu menuWithItems:itemNewGame ,nil]; |
78 |
|
79 |
[menu alignItemsVerticallyWithPadding:20]; |
80 |
[menu setPosition:ccp( size.width/2, size.height/2 - 50)]; |
81 |
|
82 |
[self addChild:menu]; |
83 |
}
|
84 |
return self; |
85 |
}
|
86 |
|
87 |
//connect the Web server method
|
88 |
-(void) connectTheWebService:(NSString*)id{ |
89 |
|
90 |
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:[[_URL autorelease] stringByAppendingString:id] ]]; |
91 |
_connectionConnectGame = [[NSURLConnection alloc] initWithRequest:request delegate:self]; |
92 |
}
|
93 |
|
94 |
//Web server responses.
|
95 |
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response { |
96 |
[_responseData setLength:0]; |
97 |
}
|
98 |
|
99 |
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data { |
100 |
[_responseData appendData:data]; |
101 |
}
|
102 |
|
103 |
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error { |
104 |
NSLog(@"Connection failed: %@", [error description]); |
105 |
}
|
106 |
|
107 |
- (void)connectionDidFinishLoading:(NSURLConnection *)connection { |
108 |
|
109 |
NSString* responseString = [[NSString alloc] initWithData:_responseData encoding:NSUTF8StringEncoding]; |
110 |
NSLog(@"HelloWorld responseString %@",responseString); |
111 |
|
112 |
NSArray* resultArray = [[NSArray alloc] init]; |
113 |
resultArray = [responseString componentsSeparatedByString:@","]; |
114 |
|
115 |
if ([[resultArray objectAtIndex:1] isEqualToString:@"no"]) { |
116 |
[self setGameID:[resultArray objectAtIndex:0]]; |
117 |
[self setLaunchFlag:TRUE]; |
118 |
} else { |
119 |
[self setGameID:[resultArray objectAtIndex:0]]; |
120 |
_playerID = [[resultArray objectAtIndex:1] integerValue]; |
121 |
[self setPlayerID:[[resultArray objectAtIndex:1] integerValue]]; |
122 |
[self setLaunchFlag:FALSE]; |
123 |
}
|
124 |
|
125 |
if (_launchFlag){ |
126 |
GameScene* scene = [[GameScene alloc] initializeInternalDataWithPlayerID:_playerIDString andWithGameID:_gameID andWithTurnID:@"no"]; |
127 |
[[CCDirector sharedDirector] replaceScene:scene]; |
128 |
} else { |
129 |
LoadingScene* scene = [LoadingScene initializeWithPlayerID:_playerIDString andWithGameID:_gameID]; |
130 |
[[CCDirector sharedDirector] replaceScene:scene]; |
131 |
}
|
132 |
|
133 |
}
|
134 |
|
135 |
- (void) dealloc { |
136 |
[super dealloc]; |
137 |
}
|
138 |
|
139 |
@end
|
Paso 8: Crea la clase LoadingScene
Cambie su archivo de encabezado LoadingScene a lo siguiente:
1 |
|
2 |
#import "cocos2d.h"
|
3 |
|
4 |
@interface LoadingScene : CCLayer <UIGestureRecognizerDelegate>{ } |
5 |
|
6 |
@property (nonatomic, retain) NSURLConnection *connectionConnectGame; |
7 |
@property (nonatomic,retain) NSMutableData* responseData; |
8 |
@property (nonatomic,retain) NSString* gameID; |
9 |
@property (nonatomic,retain) NSString* playerID; |
10 |
@property (nonatomic) BOOL launchFlag; |
11 |
@property (nonatomic,retain) NSString* URL; |
12 |
|
13 |
+(CCScene *) scene; |
14 |
+(id) initializeWithPlayerID:(NSString*) playerid andWithGameID:(NSString*) gameid; |
15 |
-(id) initializeInternalDataWithPlayerID:(NSString*) playerid andWithGameID:(NSString*) gameid; |
16 |
-(void) connectTheWebService:(NSString*)id; |
17 |
|
18 |
@end
|
Cambie su archivo de implementación de LoadingScene a lo siguiente:
1 |
|
2 |
#import "LoadingScene.h"
|
3 |
#import "GameScene.h"
|
4 |
|
5 |
@implementation LoadingScene |
6 |
|
7 |
@synthesize responseData = _responseData; |
8 |
@synthesize connectionConnectGame = _connectionConnectGame; |
9 |
@synthesize gameID = _gameID; |
10 |
@synthesize playerID = _playerID; |
11 |
@synthesize launchFlag = _launchFlag; |
12 |
@synthesize URL = _URL; |
13 |
|
14 |
+(CCScene *) scene{ |
15 |
CCScene *scene = [CCScene node]; |
16 |
|
17 |
LoadingScene *layer = [LoadingScene node]; |
18 |
|
19 |
[scene addChild: layer]; |
20 |
|
21 |
return scene; |
22 |
}
|
23 |
|
24 |
-(id) init{ |
25 |
if( (self=[super init] )) {} |
26 |
|
27 |
return self; |
28 |
}
|
29 |
|
30 |
// Custom initializer
|
31 |
+(id) initializeWithPlayerID:(NSString*) playerid andWithGameID:(NSString*) gameid { |
32 |
return [[[self alloc] initializeInternalDataWithPlayerID:playerid andWithGameID:gameid] autorelease]; |
33 |
}
|
34 |
|
35 |
-(id) initializeInternalDataWithPlayerID:(NSString*) playerid andWithGameID:(NSString*) gameid { |
36 |
self = [super init]; |
37 |
|
38 |
if (self) { |
39 |
CGSize _size = [[CCDirector sharedDirector] winSize]; |
40 |
_gameID = [[NSString alloc] init]; |
41 |
[self setLaunchFlag:FALSE]; |
42 |
[self setPlayerID:playerid]; |
43 |
[self setGameID:gameid]; |
44 |
|
45 |
CCSprite* background = [CCSprite spriteWithFile:@"background.jpg"]; |
46 |
background.position = ccp(_size.width/2,_size.height/2); |
47 |
[self addChild:background]; |
48 |
|
49 |
CCLabelTTF* label = [CCLabelTTF labelWithString:@"Loading..." fontName:@"Marker Felt" fontSize:40]; |
50 |
label.position = ccp(_size.width/2, _size.height/2); |
51 |
[self addChild:label]; |
52 |
CCLabelTTF* label2 = [CCLabelTTF labelWithString:@"Waiting for another player" fontName:@"Marker Felt" fontSize:25]; |
53 |
label2.position = ccp(_size.width/2, _size.height - _size.height/1.80); |
54 |
[self addChild:label2]; |
55 |
|
56 |
[self setResponseData:[NSMutableData data]]; |
57 |
|
58 |
// CHANGE IT ACCORDINGLY
|
59 |
[self setURL:@"http://10.0.6.178:8080/Minesweeper_Flag/resources/generic/player2Available/"]; |
60 |
|
61 |
[self schedule:@selector(update:) interval:2]; |
62 |
}
|
63 |
return self; |
64 |
}
|
65 |
|
66 |
-(void) update:(ccTime) dt{ |
67 |
[self connectTheWebService:_gameID]; |
68 |
}
|
69 |
|
70 |
-(void) connectTheWebService:(NSString*)id{ |
71 |
NSURLRequest *request = [NSURLRequest requestWithURL:[NSURL URLWithString:[[_URL autorelease] stringByAppendingString:id] ]]; |
72 |
_connectionConnectGame = [[NSURLConnection alloc] initWithRequest:request delegate:self]; |
73 |
}
|
74 |
|
75 |
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response { |
76 |
[_responseData setLength:0]; |
77 |
}
|
78 |
|
79 |
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data { |
80 |
[_responseData appendData:data]; |
81 |
}
|
82 |
|
83 |
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error { |
84 |
NSLog(@"Connection failed: %@", [error description]); |
85 |
}
|
86 |
|
87 |
- (void)connectionDidFinishLoading:(NSURLConnection *)connection { |
88 |
|
89 |
NSString* responseString = [[NSString alloc] initWithData:_responseData encoding:NSUTF8StringEncoding]; |
90 |
|
91 |
if ([responseString isEqualToString:@"nulo"]) { |
92 |
[self setLaunchFlag:FALSE]; |
93 |
}else { |
94 |
[self setLaunchFlag:TRUE]; |
95 |
}
|
96 |
|
97 |
if (_launchFlag){ |
98 |
GameScene* scene = [[GameScene alloc] initializeInternalDataWithPlayerID:_playerID andWithGameID:_gameID andWithTurnID:_playerID] ; |
99 |
[[CCDirector sharedDirector] replaceScene:scene]; |
100 |
} else { [self schedule:@selector(update:) interval:2]; } |
101 |
|
102 |
}
|
103 |
|
104 |
-(void)dealloc { |
105 |
[super dealloc]; |
106 |
}
|
107 |
|
108 |
@end
|
Paso 9: Crea la clase GameScene
Cambie su archivo de encabezado GameScene a lo siguiente:
1 |
|
2 |
#import "cocos2d.h"
|
3 |
|
4 |
@interface GameScene : CCLayer <UIGestureRecognizerDelegate> { |
5 |
NSString* turn; |
6 |
}
|
7 |
|
8 |
@property (nonatomic) int gameGridSize, initialSpriteXXPosition, initialSpriteYYPosition, deslocamentoNoEixoXX, deslocamentoNoEixoYY, tamanhoSprite, playerBlueScore, playerRedScore, lastSpriteTouch; |
9 |
|
10 |
@property (nonatomic, retain) NSMutableArray *spritesMutableArray; |
11 |
@property (nonatomic,retain) NSMutableArray* touchedLocations; |
12 |
@property (nonatomic) CGSize size; |
13 |
@property (nonatomic) CGPoint initialSpritePositon; |
14 |
@property (nonatomic, retain) CCSprite* sprites; |
15 |
@property (nonatomic) int spritePositionInGrid; |
16 |
@property (nonatomic) int webServerResult; |
17 |
@property (nonatomic, strong) NSMutableData *responseData; |
18 |
@property (nonatomic, strong) NSString *resourceImage; |
19 |
@property (nonatomic, retain) CCSprite* playerBlueSprite; |
20 |
@property (nonatomic, retain) CCSprite* playerRedSprite; |
21 |
@property (nonatomic, retain) CCSprite* playerTurnSprite; |
22 |
@property (nonatomic, retain) CCSprite* youArrow; |
23 |
@property (nonatomic,retain) CCLabelTTF* playerBlueLabel; |
24 |
@property (nonatomic,retain) CCLabelTTF* playerRedLabel; |
25 |
@property (nonatomic,retain) NSString* gameID; |
26 |
@property (nonatomic,retain) NSString* userID; |
27 |
@property (nonatomic,retain) NSString* colorPlayer; |
28 |
@property (nonatomic,retain) CCSprite* positionTouchedSprite; |
29 |
@property (nonatomic,retain) NSString* turn; |
30 |
@property (nonatomic, retain) NSMutableString* gameBoardInfoS; |
31 |
|
32 |
+(CCScene *) scene; |
33 |
-(void)getMinesAtPosition:(int)pos; |
34 |
-(void)drawGameBoard; |
35 |
-(void)setInitialAppValues; |
36 |
-(void)setArrowonTheRightPlayer; |
37 |
|
38 |
+(id) initializeWithPlayerID:(NSString*) playerid andWithGameID:(NSString*) gameid andWithTurnID:(NSString*) turnid; |
39 |
-(id) initializeInternalDataWithPlayerID:(NSString*) playerid andWithGameID:(NSString*) gameid andWithTurnID:(NSString*) turnid; |
40 |
|
41 |
@end
|
Cambie su archivo de implementación de GameScene a lo siguiente:
1 |
|
2 |
#import "GameScene.h"
|
3 |
#import "cocos2d.h"
|
4 |
#import "LoadingScene.h"
|
5 |
|
6 |
@implementation GameScene |
7 |
|
8 |
@synthesize spritesMutableArray = _spritesMutableArray; |
9 |
@synthesize touchedLocations = _touchedLocations; |
10 |
@synthesize size = _size; |
11 |
@synthesize initialSpritePositon = _initialSpritePositon; |
12 |
@synthesize sprites = _sprites; |
13 |
@synthesize spritePositionInGrid = _spritePositionInGrid; |
14 |
@synthesize webServerResult = _webServerResult; |
15 |
@synthesize gameGridSize = _gameGridSize; |
16 |
@synthesize initialSpriteXXPosition = _initialSpriteXXPosition; |
17 |
@synthesize initialSpriteYYPosition = _initialSpriteYYPosition; |
18 |
@synthesize deslocamentoNoEixoXX = _deslocamentoNoEixoXX; |
19 |
@synthesize deslocamentoNoEixoYY = _deslocamentoNoEixoYY; |
20 |
@synthesize tamanhoSprite = _tamanhoSprite; |
21 |
@synthesize responseData = _responseData; |
22 |
@synthesize resourceImage = _resourceImage; |
23 |
@synthesize playerTurnSprite = _playerTurnSprite; |
24 |
@synthesize youArrow = _youArrow; |
25 |
@synthesize playerBlueSprite = _playerBlueSprite; |
26 |
@synthesize playerRedSprite = _playerRedSprite; |
27 |
@synthesize playerBlueLabel = _playerBlueLabel; |
28 |
@synthesize playerRedLabel = _playerRedLabel; |
29 |
@synthesize playerBlueScore = _playerBlueScore; |
30 |
@synthesize playerRedScore = _playerRedScore; |
31 |
@synthesize gameID = _gameID; |
32 |
@synthesize userID = _userID; |
33 |
@synthesize colorPlayer = _colorPlayer; |
34 |
@synthesize turn = _turn; |
35 |
@synthesize gameBoardInfoS; |
36 |
@synthesize lastSpriteTouch; |
37 |
|
38 |
@synthesize positionTouchedSprite = _positionTouchedSprite; |
39 |
|
40 |
+(CCScene *) scene{ |
41 |
//+(id)scene{
|
42 |
CCScene *scene = [CCScene node]; |
43 |
|
44 |
GameScene *layer = [GameScene node]; |
45 |
|
46 |
[scene addChild: layer]; |
47 |
|
48 |
return scene; |
49 |
}
|
50 |
|
51 |
-(id) init{ |
52 |
if( (self=[super init] )) { |
53 |
}
|
54 |
return self; |
55 |
}
|
56 |
|
57 |
//new initialize method to recive parameters such as playerID, turnID, and gameID
|
58 |
+(id) initializeWithPlayerID:(NSString*) playerid andWithGameID:(NSString*) gameid andWithTurnID:(NSString*) turnid{ |
59 |
return [[[self alloc] initializeInternalDataWithPlayerID:playerid andWithGameID:gameid andWithTurnID:turnid] autorelease]; |
60 |
}
|
61 |
|
62 |
//initialize several values and call methods to generate and draw all scene
|
63 |
-(id) initializeInternalDataWithPlayerID:(NSString*) playerid andWithGameID:(NSString*) gameid andWithTurnID:(NSString*) turnid{ |
64 |
self = [super init]; |
65 |
|
66 |
if (self) { |
67 |
// Activate touch events
|
68 |
[super setIsTouchEnabled:YES]; |
69 |
//set the parameters
|
70 |
[self setGameID:gameid]; |
71 |
[self setUserID:playerid]; |
72 |
[self setTurn:turnid]; |
73 |
[self setInitialAppValues]; |
74 |
|
75 |
//generate and draw gameBoard
|
76 |
[self drawGameBoard]; |
77 |
|
78 |
//call the updater one time for second
|
79 |
[self schedule:@selector(update:) interval:1]; |
80 |
}
|
81 |
return self; |
82 |
}
|
83 |
|
84 |
|
85 |
// detect touchs and which sprite was touched
|
86 |
-(void)ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{ |
87 |
|
88 |
//verify if the user have the turn
|
89 |
if(![_turn isEqualToString:_userID]){ |
90 |
//in case of the user not have the turn
|
91 |
return; |
92 |
}
|
93 |
|
94 |
UITouch* touch = [touches anyObject]; |
95 |
CGPoint location = [touch locationInView: [touch view]]; |
96 |
CGPoint converted = [[CCDirector sharedDirector] convertToGL:location]; |
97 |
|
98 |
_spritePositionInGrid=0; |
99 |
for (CCSprite* spr in _spritesMutableArray){ |
100 |
CCSprite* currentSprite = [_spritesMutableArray objectAtIndex:_spritePositionInGrid]; |
101 |
|
102 |
if (CGRectContainsPoint([currentSprite boundingBox], converted)){ |
103 |
|
104 |
// Verificar se a posição já foi touched
|
105 |
BOOL jaExiste =FALSE; |
106 |
for (int i = 0 ; i < [_touchedLocations count]; i++){ |
107 |
if (_spritePositionInGrid == [[_touchedLocations objectAtIndex:i] integerValue]) |
108 |
jaExiste=TRUE; |
109 |
}
|
110 |
|
111 |
//was touched?
|
112 |
if([gameBoardInfoS characterAtIndex:_spritePositionInGrid] != '9'){ |
113 |
return; |
114 |
}
|
115 |
|
116 |
if (!jaExiste){ |
117 |
[_touchedLocations addObject:[NSNumber numberWithInt:_spritePositionInGrid]]; |
118 |
lastSpriteTouch = _spritePositionInGrid; |
119 |
|
120 |
self.responseData = [NSMutableData data]; |
121 |
|
122 |
_positionTouchedSprite = [_spritesMutableArray objectAtIndex:_spritePositionInGrid]; |
123 |
|
124 |
NSMutableString* enderecoReturnValue = [NSMutableString stringWithFormat:@"http://10.0.6.178:8080/Minesweeper_Flag/resources/generic/returnValue/%@/%@/%d",_userID,_gameID,_spritePositionInGrid]; |
125 |
NSLog(@"%@",enderecoReturnValue); |
126 |
|
127 |
NSURLRequest *request = [NSURLRequest requestWithURL: [NSURL URLWithString:enderecoReturnValue]]; |
128 |
[[NSURLConnection alloc] initWithRequest:request delegate:self]; |
129 |
}
|
130 |
}
|
131 |
_spritePositionInGrid++; |
132 |
}
|
133 |
}
|
134 |
|
135 |
-(void) update: (ccTime) dt |
136 |
{
|
137 |
if(![_turn isEqualToString:_userID]){ |
138 |
|
139 |
NSMutableString* returnUpdateURL = [NSMutableString stringWithFormat:@"http://10.0.6.178:8080/Minesweeper_Flag/resources/generic/returnUpdates/%@",_gameID]; |
140 |
|
141 |
NSURL *url = [NSURL URLWithString:returnUpdateURL]; |
142 |
NSData *jsonData = [NSData dataWithContentsOfURL:url]; |
143 |
|
144 |
NSString *value = [[NSString alloc] init]; |
145 |
NSRange a; |
146 |
|
147 |
if(jsonData != nil) { |
148 |
NSError *error = nil; |
149 |
id result = [NSJSONSerialization JSONObjectWithData:jsonData options:NSJSONReadingMutableContainers error:&error]; |
150 |
if(error == nil) { |
151 |
NSString* resultString = [result objectForKey:@"Uncover_Matrix"]; |
152 |
int positionAt, positionDiff=-1; |
153 |
|
154 |
for(positionAt= 0; positionAt< resultString.length; positionAt++){ |
155 |
a = NSMakeRange(positionAt, 1); |
156 |
if(![[gameBoardInfoS substringWithRange:a] isEqualToString:[resultString substringWithRange:a]]){ |
157 |
positionDiff = positionAt; |
158 |
value = [[NSString alloc] initWithString:[resultString substringWithRange:a]]; |
159 |
break; |
160 |
}
|
161 |
}
|
162 |
if(positionDiff != -1){ |
163 |
[self setTurn:[result objectForKey:@"Turn"]]; |
164 |
|
165 |
[gameBoardInfoS replaceCharactersInRange:a withString:value]; |
166 |
|
167 |
if([value isEqualToString:@"*"]){ |
168 |
_playerBlueScore++; |
169 |
NSString *stringBlueScore = [NSString stringWithFormat:@"%d",_playerBlueScore]; |
170 |
[_playerBlueLabel setString:stringBlueScore]; |
171 |
value = @"9"; |
172 |
}
|
173 |
|
174 |
if([value isEqualToString:@"#"]){ |
175 |
_playerRedScore++; |
176 |
NSString *stringRedScore = [NSString stringWithFormat:@"%d",_playerRedScore]; |
177 |
[_playerRedLabel setString:stringRedScore]; |
178 |
value = @"10"; |
179 |
}
|
180 |
|
181 |
[self setArrowonTheRightPlayer]; |
182 |
|
183 |
CGPoint coordenates = [self getReversePositionGameBoard:positionDiff]; |
184 |
[self getMinesAtPosition:[value integerValue] withXCoord:coordenates.x andYYpos:coordenates.y]; |
185 |
}
|
186 |
|
187 |
}
|
188 |
else
|
189 |
NSLog(@"error"); |
190 |
}
|
191 |
}
|
192 |
|
193 |
}
|
194 |
|
195 |
//recive the position and the value returned from Web Services and replace in the board
|
196 |
-(void)getMinesAtPosition:(int)pos withXCoord:(int)xxpos andYYpos:(int)yypos{ |
197 |
_webServerResult = pos; |
198 |
|
199 |
switch (_webServerResult) { |
200 |
case 0: |
201 |
[self setResourceImage:@"0.png"]; |
202 |
break; |
203 |
case 1: |
204 |
[self setResourceImage:@"1.png"]; |
205 |
break; |
206 |
case 2: |
207 |
[self setResourceImage:@"2.png"]; |
208 |
break; |
209 |
case 3: |
210 |
[self setResourceImage:@"3.png"]; |
211 |
break; |
212 |
case 4: |
213 |
[self setResourceImage:@"4.png"]; |
214 |
break; |
215 |
case 5: |
216 |
[self setResourceImage:@"5.png"]; |
217 |
break; |
218 |
case 6: |
219 |
[self setResourceImage:@"6.png"]; |
220 |
break; |
221 |
case 7: |
222 |
[self setResourceImage:@"7.png"]; |
223 |
break; |
224 |
case 8: |
225 |
[self setResourceImage:@"8.png"]; |
226 |
break; |
227 |
case 9: |
228 |
[self setResourceImage:@"flag_b.png"]; |
229 |
break; |
230 |
case 10: |
231 |
[self setResourceImage:@"flag_r.png"]; |
232 |
break; |
233 |
default:
|
234 |
_resourceImage = @"flag_r.png"; |
235 |
break; |
236 |
}
|
237 |
|
238 |
// New sprite substitution after the WS invocation
|
239 |
CCSprite* newSprite = [CCSprite spriteWithFile:_resourceImage]; |
240 |
newSprite.scale = .25; |
241 |
newSprite.position = ccp(xxpos,yypos); |
242 |
|
243 |
[self addChild:newSprite]; |
244 |
}
|
245 |
|
246 |
// communication
|
247 |
- (void)connection:(NSURLConnection *)connection didReceiveResponse:(NSURLResponse *)response { |
248 |
[[self responseData] setLength:0]; |
249 |
}
|
250 |
|
251 |
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data { |
252 |
[[self responseData] appendData:data]; |
253 |
}
|
254 |
|
255 |
- (void)connection:(NSURLConnection *)connection didFailWithError:(NSError *)error { |
256 |
NSLog(@"Connection failed: %@", [error description]); |
257 |
}
|
258 |
|
259 |
- (void)connectionDidFinishLoading:(NSURLConnection *)connection { |
260 |
|
261 |
NSString* responseString = [[NSString alloc] initWithData:[self responseData] encoding:NSUTF8StringEncoding]; |
262 |
|
263 |
NSRange range = NSMakeRange(lastSpriteTouch, 1); |
264 |
[gameBoardInfoS replaceCharactersInRange:range withString:responseString]; |
265 |
|
266 |
//assign the turn after the request
|
267 |
if([responseString isEqualToString:@"*"]){ |
268 |
_playerBlueScore++; |
269 |
NSString *stringBlueScore = [NSString stringWithFormat:@"%d",_playerBlueScore]; |
270 |
[_playerBlueLabel setString:stringBlueScore]; |
271 |
_turn = _userID; |
272 |
responseString = @"9"; |
273 |
}
|
274 |
else if([responseString isEqualToString:@"#"]){ |
275 |
_playerRedScore++; |
276 |
NSString *stringRedScore = [NSString stringWithFormat:@"%d",_playerRedScore]; |
277 |
[_playerRedLabel setString:stringRedScore]; |
278 |
_turn = _userID; |
279 |
responseString = @"10"; |
280 |
}
|
281 |
else{ |
282 |
_turn = @"is not you"; |
283 |
}
|
284 |
|
285 |
[self setArrowonTheRightPlayer]; |
286 |
|
287 |
[self getMinesAtPosition:[responseString integerValue] withXCoord:_positionTouchedSprite.position.x andYYpos:_positionTouchedSprite.position.y]; |
288 |
}
|
289 |
|
290 |
//put the arrow on the player that holds the turn
|
291 |
-(void)setArrowonTheRightPlayer{ |
292 |
|
293 |
if([_turn isEqualToString:_userID]) { |
294 |
if ([_colorPlayer isEqualToString:@"blue"]) |
295 |
_playerTurnSprite.position = ccp(_size.width/1.30, _size.height/1.10); |
296 |
else
|
297 |
_playerTurnSprite.position = ccp(_size.width/1.30, _size.height/1.48); |
298 |
}
|
299 |
|
300 |
if(![_turn isEqualToString:_userID]) { |
301 |
if ([_colorPlayer isEqualToString:@"blue"]) |
302 |
_playerTurnSprite.position = ccp(_size.width/1.30, _size.height/1.48); |
303 |
else
|
304 |
_playerTurnSprite.position = ccp(_size.width/1.30, _size.height/1.10); |
305 |
}
|
306 |
}
|
307 |
|
308 |
//calculate the reverse position to change the sprite on gameBoard
|
309 |
-(CGPoint)getReversePositionGameBoard:(int)positionTouched{ |
310 |
NSInteger linha = (int) positionTouched / 16.0; |
311 |
NSInteger y = 688 - _tamanhoSprite * linha; |
312 |
NSInteger x = ( (int) positionTouched % 16) * 38 + _initialSpriteXXPosition; |
313 |
CGPoint position = CGPointMake(x, y); |
314 |
|
315 |
return position; |
316 |
}
|
317 |
|
318 |
//draw the gameBoard (background, sprites blue (water), and the playerss avatar
|
319 |
-(void)drawGameBoard{ |
320 |
|
321 |
for (int i = 0 ; i < _gameGridSize; i++) { |
322 |
_sprites = [CCSprite spriteWithFile:@"water.png"]; |
323 |
_sprites.scale = .25; |
324 |
_initialSpritePositon = CGPointMake(_initialSpriteXXPosition+_deslocamentoNoEixoXX, _size.height-_initialSpriteYYPosition-(_deslocamentoNoEixoYY*_tamanhoSprite)); |
325 |
_sprites.position = ccp(_initialSpritePositon.x,_initialSpritePositon.y); |
326 |
_deslocamentoNoEixoXX+=_tamanhoSprite; |
327 |
|
328 |
if (((i+1) % 16) == 0){ |
329 |
_deslocamentoNoEixoXX=0; |
330 |
_deslocamentoNoEixoYY++; |
331 |
}
|
332 |
|
333 |
[_spritesMutableArray addObject:_sprites]; |
334 |
[self addChild:_sprites]; |
335 |
}
|
336 |
|
337 |
|
338 |
[self addChild:_playerTurnSprite z:4]; |
339 |
|
340 |
[self addChild:_playerBlueSprite]; |
341 |
[self addChild:_playerRedSprite]; |
342 |
[self addChild:_youArrow]; |
343 |
|
344 |
[self addChild:_playerBlueLabel]; |
345 |
[self addChild:_playerRedLabel]; |
346 |
}
|
347 |
|
348 |
//set initial values
|
349 |
-(void)setInitialAppValues{ |
350 |
_size = [[CCDirector sharedDirector] winSize]; |
351 |
_gameGridSize = 256, _tamanhoSprite = 38; |
352 |
_initialSpriteXXPosition = 80, _initialSpriteYYPosition = 80; |
353 |
_deslocamentoNoEixoXX=0, _deslocamentoNoEixoYY=0; |
354 |
_spritesMutableArray = [[NSMutableArray alloc] initWithCapacity:256]; |
355 |
_touchedLocations = [[NSMutableArray alloc] initWithCapacity:256]; |
356 |
[self setResourceImage:@""]; |
357 |
|
358 |
CCSprite * background = [CCSprite spriteWithFile:@"background.jpg"]; |
359 |
background.position = ccp(_size.width/2,_size.height/2); |
360 |
[self addChild:background]; |
361 |
|
362 |
//init the gameBoard array
|
363 |
gameBoardInfoS = [[NSMutableString alloc] init]; |
364 |
for(int i=0;i<256;i++){ |
365 |
[gameBoardInfoS appendString:@"9"]; |
366 |
}
|
367 |
|
368 |
_playerBlueSprite = [CCSprite spriteWithFile:@"player_bue.png"]; |
369 |
_playerBlueSprite.scale = .5; |
370 |
_playerBlueSprite.position = ccp(_size.width/1.30, _size.height/1.25); |
371 |
_playerRedSprite = [CCSprite spriteWithFile:@"player_red.png"]; |
372 |
_playerRedSprite.scale = .5; |
373 |
_playerRedSprite.position = ccp(_size.width/1.30, _size.height/1.75); |
374 |
|
375 |
//identify the players on field
|
376 |
if([_turn isEqualToString:_userID]){ |
377 |
_youArrow = [CCSprite spriteWithFile:@"you.png"]; |
378 |
_youArrow.scale = .3; |
379 |
_youArrow.position = ccp(_size.width/1.30, _size.height/1.30); |
380 |
[self setColorPlayer:@"blue"]; |
381 |
}
|
382 |
else{ |
383 |
_youArrow = [CCSprite spriteWithFile:@"you.png"]; |
384 |
_youArrow.scale = .3; |
385 |
_youArrow.position = ccp(_size.width/1.30, _size.height/1.85); |
386 |
[self setColorPlayer:@"red"]; |
387 |
}
|
388 |
|
389 |
_playerTurnSprite = [CCSprite spriteWithFile:@"arrowTurn.png"]; |
390 |
_playerTurnSprite.scale = 1; |
391 |
_playerTurnSprite.position = ccp(_size.width/1.30, _size.height/1.10); |
392 |
|
393 |
|
394 |
_playerBlueLabel = [CCLabelTTF labelWithString:@"0" fontName:@"Marker Felt" fontSize:50.0]; |
395 |
_playerBlueLabel.position = ccp(_size.width/1.15, _size.height/1.25); |
396 |
_playerRedLabel = [CCLabelTTF labelWithString:@"0" fontName:@"Marker Felt" fontSize:50.0]; |
397 |
_playerRedLabel.position = ccp(_size.width/1.15, _size.height/1.75); |
398 |
|
399 |
[self setPlayerBlueScore:0]; |
400 |
[self setPlayerRedScore:0]; |
401 |
}
|
402 |
|
403 |
-(void) registerWithTouchDispatcher |
404 |
{
|
405 |
NSLog(@"registerWithTouchDispatcher"); |
406 |
[[[CCDirector sharedDirector] touchDispatcher] addStandardDelegate:self priority:0]; |
407 |
}
|
408 |
|
409 |
- (void) dealloc |
410 |
{
|
411 |
[turn release]; |
412 |
[super dealloc]; |
413 |
}
|
414 |
|
415 |
@end
|
Paso 10: Construye y ejecuta el juego
Después de incluir el código mencionado anteriormente, el siguiente paso es construir y probar la aplicación. Use el atajo cmd + b. Si todo está bien, deberías ver el simulador de iPad funcionando.


Paso 11: Disfruta del juego
En este paso final, el objetivo es disfrutar del juego. ¡Dado que el juego es para dos jugadores, la mejor manera de jugarlo es con un amigo!
En las siguientes pantallas podemos ver el tablero de juego final a través de la visión de los dos jugadores (azul y rojo). La flecha arriba del avatar del jugador indica el turno actual del jugador; el jugador que está jugando actualmente.
Tablero de juego del jugador azul:


Tablero de juego del jugador rojo:


Paso 12: Conclusiones
Esto concluye la Parte II de cómo crear un juego de Flag Minesweeper utilizando tanto el código del lado del servidor como el del lado del cliente. Por ahora, debes tener suficiente conocimiento para crear un juego Cocos2D simple usando el mismo motor de juego. Si tiene alguna pregunta o comentario, no dude en dejarlos en la sección de comentarios aquí.



