gergonzalez
Nuestro primer videojuego para iPhone – III
Escrito por gergonzalez | Noviembre 24, 2009 | Casos de Estudio | Comentarios 0
Llevamos ya dos posts y todavía no hemos conseguido que nuestro videojuego sea medianamente jugable. Hoy nos proponemos acabar con esa situación mediante la implementación de la detección de colisiones y la inteligencia artificial de nuestro oponente.
Detección de colisiones
Llamamos detección de colisiones a la lógica que nos permite detectar cuando dos o más sprites, en nuestro caso vistas, chocan entre ellos y en función a esas colisiones actuar en consecuencia.
Para nuestro juego tendremos que detectar cuando la pelota choca con cualquiera de las paletas o en los límites de pantalla, y en cuyo caso producir un cambio en su velocidad con el fin de que se cumpla la tercera Ley de Newton o de acción y reacción. No profundizaremos más en el tema, se deja a gusto del lector hacer el juego un poco más divertido con cambios en relación al punto de impacto de la pelota con la raqueta.
Pues vamos a ello, primeramente tendremos que volver a modificar nuestra cabecera iPingPongViewController.h declarando nuevas variables instanciadas y un método:
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 | #import <UIKit/UIKit.h> @interface iPingPongViewController : UIViewController { UIView *player1; UIView *player2; UIView *ball; UILabel *player1Score; UILabel *player2Score; UILabel *startLabel; CGPoint ballVelocity; BOOL gameRunning; NSInteger player1ScoreValue;//New NSInteger player2ScoreValue;//New } @property(nonatomic, retain) UIView *ball; @property(nonatomic, retain) UIView *player1; @property(nonatomic, retain) UIView *player2; @property(nonatomic, retain) UILabel *player1Score; @property(nonatomic, retain) UILabel *player2Score; @property(nonatomic, retain) UILabel *startLabel; @property (nonatomic) CGPoint ballVelocity; @property (nonatomic) NSInteger player1ScoreValue;//New @property (nonatomic) NSInteger player2ScoreValue;//New -(void)playAgain: (BOOL) newGame;//New @end |
No creo que nadie a estas alturas albergue dudas ante el código anterior, simplemente declaramos dos variables instanciadas de tipo NSInteger que almacenarán el tanteo que luego será mostrado por las etiquetas player1Score. Además nos hemos declarado un método que usaremos para controlar quien es el jugador ganador.
Sin más dilación, abrimos iPingPongViewController.m e introducimos:
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 | #define kBallSpeedX 3 #define kBallSpeedY 2 #define kFPS 0.01 //New #define kScoreToWin 5 #import "iPingPongViewController.h" @implementation iPingPongViewController @synthesize ball, player1, player2, player1Score, player2Score; @synthesize startLabel, ballVelocity; @synthesize player1ScoreValue, player2ScoreValue;//New ... [NSTimer scheduledTimerWithTimeInterval:kFPS target:self selector:@selector(gameLoop) userInfo:nil repeats:YES]; } -(void)gameLoop{ if (gameRunning) { ball.center = CGPointMake(ball.center.x + ballVelocity.x, ball.center.y + ballVelocity.y); //Collision Detection if(ball.center.x>self.view.bounds.size.width || ball.center.x < 0){ ballVelocity.x = -ballVelocity.x; } if(ball.center.y>self.view.bounds.size.height || ball.center.y < 0){ ballVelocity.y = -ballVelocity.y; } if (CGRectIntersectsRect (ball.frame, player1.frame)){ if (ball.center.y < player1.center.y){ ballVelocity.y = -ballVelocity.y-0.1; } } if (CGRectIntersectsRect (ball.frame, player2.frame)){ if (ball.center.y < player2.center.y){ ballVelocity.y = -ballVelocity.y+0.1; } } if(ball.center.y <= 0) { player1ScoreValue++; ballVelocity = CGPointMake(kBallSpeedX, kBallSpeedY); [self playAgain:(player1ScoreValue >= kScoreToWin)]; } if(ball.center.y > self.view.bounds.size.height) { player2ScoreValue++; ballVelocity = CGPointMake(kBallSpeedX, kBallSpeedY); [self playAgain:(player2ScoreValue >= kScoreToWin)]; } } else { if (startLabel.hidden) { startLabel.hidden = NO; } } } -(void)playAgain:(BOOL) newGame { gameRunning = NO; ball.center = self.view.center; if(newGame) { if(player2ScoreValue > player1ScoreValue) { startLabel.text = @"PLAYER 2 WINS!"; } else { startLabel.text = @"PLAYER 1 WINS!"; } player2ScoreValue = 0; player1ScoreValue = 0; } else { startLabel.text = @"TAP TO START GAME"; } player1Score.text = [NSString stringWithFormat:@"%d",player1ScoreValue]; player2Score.text = [NSString stringWithFormat:@"%d",player2ScoreValue]; } ... |
Al igual que en posts anteriores hacemos uso de constantes, en este caso para representar el número de puntos necesarios para ganar. Ya en nuestro método gameloop vemos como para la implementación de la detección de colisiones simplemente utilizamos cláusulas condicionales if para testear si la pelota se encuentra o tocando los bordes de la pantalla en cuyo caso cambia su velocidad con respecto al eje x, o interseccionando con la paleta, modificando entonces su velocidad en el eje y, y añadiéndole un factor para que la velocidad vaya aumentando en función a los golpes devueltos. Para evitar el molesto efecto que se produce a veces de que la pelota queda atrapada en nuestra paleta hemos introducido un if más que chequea esta problemática.
Lo siguiente que hacemos es comprobar cuando la pelota toca los bordes inferior y superior de nuestra pantalla y que suponen que un jugador ha logrado un punto, por lo tanto se aumenta la variable habilitada para tal fin y se llama al método que acabamos de declarar playAgain, el cual comprueba si hemos alcanzado el número de puntos necesarios para ganar, mostrando el mensaje correspondiente. También desde este método se actualizan las etiquetas que utilizamos como marcador.
Si simulamos ahora veremos como la pelota ya no desaparece de nuestra pantalla, ya que al llegar a una pared esta choca y cambia su dirección manteniéndose en la pantalla, lo mismo ocurre al colisionar con cualquiera de las dos paletas. Nuestro problema reside ahora en que nuestro oponente nos observa impertérrito y no hace ni ademán de devolvernos la pelota…
IA
Una vez implementada la detección de colisiones tenemos que hacer que la vista player2 interactúe con nosotros y nos intente devolver la pelota o lo que es lo mismo, asignarle una ia. Debido al carácter de nuestro caso de estudio, no vamos a profundizar en complejos sistemas para simular inteligencias artificiales, algo completamente fuera del alcance de este caso de estudio.
Por lo tanto vamos a crear una sencilla ia, para ello nuestro competidor empezará su movimiento en el momento en que la pelota sobrepase la red, y su movimiento consistirá en que a partir de este momento seguirá la dirección de la pelota, una constante que llamaremos kPlayer2Speed será el que controlará la velocidad de nuestro competidor y determinará el nivel de dificultad de este.
Pues vamos a ello, esta vez no necesitamos reescribir nuestro header, por eso en iPingPongViewController.m declararemos la constante antes nombrada y la lógica de comportamiento de nuestro oponente:
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 | #define kBallSpeedX 3 #define kBallSpeedY 2 #define kFPS 0.01 #define kScoreToWin 5 //New #define kPlayer2Speed 3 ... -(void)gameLoop{ if (gameRunning) { ... //AI if(ball.center.y <= self.view.center.y) { if(ball.center.x < player2.center.x) { player2.center = CGPointMake(player2.center.x - kPlayer2Speed, player2.center.y); } if(ball.center.x > player2.center.x) { player2.center = CGPointMake(player2.center.x + kPlayer2Speed, player2.center.y); } } ... |
Como vemos nuestra lógica consiste en lo anteriormente explicado, comprobamos que la pelota sobrepasa la red y entonces nuestra vista player2 cambiará sus coordenadas con respecto a x en la misma dirección en la que se mueve la pelota con una velocidad determinada por la constante kPlayer2Speed. Tan simple como eso.
Pues ya está, ahora ya podemos simular y ver nuestro juego en acción, muy limitado y poco trabajado, pero ahora es cosa de cada uno mejorarlo a partir de las premisas dadas. Buena Suerte. En próximos post ya terminaremos este caso de estudio, mediante la introducción de un menú y sonidos. Saludos.
Puede que te interese también:
-

- Hola, me llamo Germán González Rodríguez, soy ingeniero de teleco. Toda mi experiencia laboral ha estado ligada al diseño web, aunque en la actualidad dedico mis esfuerzos a la programación para iPhone. Más...
-
Más Leídos
-
Blogroll
- App cultura Iphone Iphone Developer Program iPod iPod Touch iTunes Keynote Mac Personal Programación SDK videojuegos









Comentarios