Casa do C Sum Sum 1 Introdu 1 1.1 O que voc 3 1.2 Que comece a divers 8 2 Prot 9 2.1 Iniciando o projeto . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11 2.2 Criando a base do jogo . . . . . . . . . . . . . . . . . . . . . . . . . . . 14 2.3 Desenhando o objeto principal . . . . . . . . . . . . . . . . . . . . . . 20 2.4 Captando os comandos do usu 2.5 Criando o inimigo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 29 2.6 Detectando colis 32 2.7 Adicionando um placar . . . . . . . . . . . . . . . . . . . . . . . . . . . 40 2.8 Criando bot 2.9 Adicionando mais vida: imagens da nave e do c 2.10 Conclus 3 Hist 51 3.1 14-bis . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 52 3.2 14-bis VS 100 Meteoros . . . . . . . . . . . . . . . . . . . . . . . . . . . 53 4 Tela inicial: Lidando com Background, logo e bot 57 4.1 Sobre o Cocos2D . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59 4.2 Iniciando o projeto . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 59 4.3 ajustando a orienta . . . . . . . . . . . . . . . . . . . . . . . . . . . 62 4.4 Background . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 64 iii Sum Casa do C 4.5 Assets da Tela de abertura . . . . . . . . . . . . . . . . . . . . . . . . . 67 4.6 Capturando configura 4.7 Logo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 71 4.8 Bot 72 4.9 Conclus 74 5 Tela do jogo e objetos inimigos 77 5.1 GameScene . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 78 5.2 Transi 5.3 Engines . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 81 5.4 Meteor . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 85 5.5 Tela do game . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 87 5.6 Conclus 6 Criando o Player 93 6.1 Desenhando o Player . . . . . . . . . . . . . . . . . . . . . . . . . . . . 94 6.2 Bot 6.3 Atirando . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 101 6.4 Movendo o player . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 107 6.5 Conclus 7 Detectando colis 111 7.1 Detectando colis 7.2 Efeitos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 115 7.3 Player morre . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 122 7.4 Placar . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 123 7.5 Conclus 8 Adicionando sons e m 127 8.1 Executando sons . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 128 8.2 Cache de sons . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 129 8.3 M 8.4 Conclus iv Casa do C Sum 9 Voando com a gravidade! 133 9.1 Usando o Aceler 9.2 Controlando a instabilidade . . . . . . . . . . . . . . . . . . . . . . . . 141 9.3 Calibrando a partir da posi 9.4 Desafios com o aceler 9.5 Conclus 10 Tela final e game over 147 10.1 Tela final . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 148 10.2 Tela Game Over . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 152 10.3 Conclus 11 Pausando o jogo 157 11.1 Montando a tela de pause . . . . . . . . . . . . . . . . . . . . . . . . . . 158 11.2 Controlando o Game Loop . . . . . . . . . . . . . . . . . . . . . . . . . 161 11.3 Adicionando o bot 11.4 A interface entre jogo e pause . . . . . . . . . . . . . . . . . . . . . . . 163 11.5 Pausando o jogo . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 163 11.6 Pausando os objetos . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 168 11.7 Conclus 12 Continuando nosso jogo 173 12.1 Utilizando ferramentas sociais . . . . . . . . . . . . . . . . . . . . . . . 173 12.2 Highscore . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 174 12.3 Achievements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 176 12.4 Desafios para voc 12.5 Como ganhar dinheiro? . . . . . . . . . . . . . . . . . . . . . . . . . . . 178 12.6 Conclus v Cap Introdu jogos no iOS River Raid, para Atari, foi provavelmente o primeiro jogo de videogame que muitos jogaram. Nesse cl inimigos, destruir helic Casa do C Figura 1.1: RIVER RAID no Atari Incr nave, fazer pontos e passar por obst Com o passar do tempo, novos jogos foram surgindo e se tornaram cada vez mais sofisticados. Apesar de todos os conceitos dos jogos antigos terem sido mantidos, um jogo de Playstation 3, por exemplo, pode envolver dezenas de desenvolvedores. Atualmente, com o crescimento dos casual gamers, os celulares e tablets se tor- naram plataformas de sucessos e disputadas. Com eles, o desenvolvimento de um jogo n interessante e bem implementada pode ser o suficiente para seu jogo obter sucesso. S 2 Casa do C Cap Figura 1.2: Capa do jogo RIVER RAID de 1982 1.1 O que voc Este livro mento de aplicativos para iOS. Ele • Um jogo desenvolvido com Cocos2D A ideia volvimento de games, seja profissionalmente, para evoluir seus conhecimentos ou mesmo por pura divers 3 1.1. O que voc Casa do C O que O Xcode pode baix vem junto do pr relho iPhone / iPad ou public ple registrado no “iOS Developer Program” (mais em https://developer.apple.com/ programs/ios/) . No cap Um prot No in 2 classes. O objetivo sejam eles simples ou avan Nesse cap mento, apenas os frameworks padr a um resultado bem interessante, como esse: 4 Casa do C Cap Figura 1.3: Imagem do nosso prot O c https://github.com/BivisSoft/jogos_ios_prototipo Um estudo do jogo que ser Programa das em desenvolvimento de jogos possuem roteiristas para criar a hist 5 1.1. O que voc Casa do C Teremos um cap minar as transi Figura 1.4: 14 bis VS 100 Meteoros Tamb dinheiro com itens, miss Um jogo desenvolvido com Cocos2D Quando os principais conceitos j mento do jogo finalizada, iniciaremos o desenvolvimento do nosso jogo principal. Para ele, utilizaremos um framework chamado Cocos2D, que facilita e otimiza di- 6 Casa do C Cap versas quest Figura 1.5: 14 bis VS 100 Meteoros O c https://github.com/BivisSoft/jogos_ios_14bis Grupo de Discuss Existe um grupo de discuss desenvolvidos aqui. Caso voc implementar modifica tilhe! https://groups.google.com/group/desenvolvimento-de-jogos-para-ios 7 1.2. Que comece a divers Casa do C Caso tenha uma conta de Desenvolvedor Apple, voc f https://developer.apple.com/ 1.2 Que comece a divers Este livro vai te dar a base para criar um jogo! Voc ao final desta leitura. A partir disso, 8 Cap Prot Vamos come todo o livro, focado em conceitos importantes, ilustrando com muita pr jogo. Com os conceitos desse cap tes, por Para percorrer esse caminho, iniciaremos criando um prot t senvolver um game. ter, permitindo focar apenas nos conceitos importantes. Casa do C game. Nosso prot conhecer os objetivos. Funcionalidades do prot Pense em um jogo 2D tradicional como Super Mario Bros ou mesmo Street Figh- ter. Eles possuem uma s mento, que podemos chamar de Player. O player recebe algum tipo de est (input) para executar movimentos na tela, como teclado, joystick ou mouse. Ap como superar um tempo, ultrapassar uma marca de pontos ou encostar em algum outro objeto do game. Essas s caracter em um aplicativo iOS. Nosso jogo ter mente, a nave. com o passar do tempo. capturado pelo inimigo. • Game Over quando o inimigo encostar no player Ao fim desse cap 10 Casa do C Cap Figura 2.1: Imagem do jogo. Temos muito para percorrer nesse prot por tr 2.1 Iniciando o projeto Vamos iniciar criando um projeto comum de iOS. Como voc cativo iOS, perceber nada espec http://itunes.apple.com/br/app/xcode/id497799835 11 2.1. Iniciando o projeto Casa do C Esse aplicativos iOS, mas mesmo assim passaremos passo a passo em alguns pontos e revisaremos conceitos chave, para facilitar seu acompanhamento. Abra o Xcode e v para criar um novo projeto. A tela para escolher um template de projeto ser No menu selecione Empty Application e clique em Next. Figura 2.2: Novo projeto de aplicativo iOS. Criaremos um projeto chamado Impossible. Por que esse nome? Se voc na lista de funcionalidades, nosso prot Coloque o nome do projeto como impossible, no campo Product Name. Como nosso prot Device escolha iPhone. Nos demais campos, deixe as op 12 Casa do C Cap Figura 2.3: Criando o projeto. Clique em Next e escolha o local onde o projeto ser Nesse momento temos o projeto iniciado. Voc iPhone x.x Simulator em scheme e clique em Run. Ao execut 13 2.2. Criando a base do jogo Casa do C Figura 2.4: Simulador com tela em branco. Como desenvolveremos nosso prot legadas, no simulador clique em Hardware, Device e escolha a op (Retina 4-inch). O simulador ir de 4 polegadas. Caso o simulador fique muito grande em sua tela, clique em Window, Scale e escolha um percentual de zoom que seja melhor para que voc simulador inteiro. 2.2 Criando a base do jogo Hora de come Teremos apenas duas classes: uma UIViewController, na qual trataremos 14 Casa do C Cap as a s AppDelegate, criada por padr A l enta l pelos conceitos importantes, definiremos a arquitetura do nosso jogo, assim como dividiremos as responsabilidades em diversas classes. Game ViewController A classe GameViewController come entrada do nosso jogo, ent Vamos criar esta classe como qualquer UIViewController File, New e selecionando File. Na tela para escolher o tipo de arquivo a ser criado, vamos escolher Cocoa Touch na se Figura 2.5: Criando nova Classe. Vamos dar o nome da nossa classe como GameViewController, subclasse de uma UIViewController: 15 2.2. Criando a base do jogo Casa do C Figura 2.6: Criando a GameViewController. No nosso arquivo GameViewController.m vamos remover o que o Xcode gerou de c assim: # import "GameViewController.h" @implementation GameViewController - (void)viewDidLoad { [super viewDidLoad]; } @end Agora, vamos alterar a classe AppDelegate para que use nossa GameViewController como rootViewController. Para isto, primeira- mente devemos importar a GameViewController e depois alterar o m application:didFinishLaunchingWithOptions:. Altere o arquivo AppDelegate.m. # import "GameViewController.h" 16 Casa do C Cap // ... - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { self.window = [[UIWindow alloc] initWithFrame: [[UIScreen mainScreen] bounds]]; // Override point for customization after application launch. self.window.backgroundColor = [UIColor whiteColor]; GameViewController *gameVC = [[GameViewController alloc] init]; self.window.rootViewController = gameVC; [self.window makeKeyAndVisible]; return YES; } A l A classe que conter de UIView. Deixe o header Impossible.h com o c Xcode. # import @interface Impossible : UIView @end E limpe todo o c # import "Impossible.h" @implementation Impossible @end No iOS um objeto do tipo UIView permite que desenhos sejam executados sobre a superf Agora que j classe Impossible, que representa a l Para isso, na classe GameViewController.h criaremos uma propriedade que ar- mazenar 17 2.2. Criando a base do jogo Casa do C # import # import "Impossible.h" @interface GameViewController : UIViewController @property (nonatomic, strong) Impossible *impossibleView; @end Na GameViewController.m inicializaremos nossa impossibleView e adi- cionaremos ela - (void)viewDidLoad { [super viewDidLoad]; // Instancia um objeto do tipo Impossible self.impossibleView = [[Impossible alloc] init]; // Define o tamanho dele, com base no tamanho da tela self.impossibleView.frame = CGRectMake(0.0f, 0.0f, self.view.frame.size.width, self.view.frame.size.height); [self.view addSubview:self.impossibleView]; } Pode rodar seu projeto novamente, mas ainda n que um jogo ainda est Game Loop Normalmente os jogos t loop infinito, conhecido como game loop ou main loop. O jogo rapidamente temos a impress 18 Casa do C Cap Figura 2.7: Desenho animado em bloco de papel. Esse conceito lidar no desenvolvimento de um jogo. Com v lidades na tela a chance de perder o controle estar errado fica muito mais f Vamos colocar o nosso loop infinito dentro da classe Impossible. Para isto, criaremos um timer que chamar Tamb ou n @property (nonatomic, strong) NSTimer *gameRunTimer; @property (nonatomic) BOOL running; Ent - (id)init { self = [super init]; 19 2.3. Desenhando o objeto principal Casa do C if (self) { // Timer que executa o Game Loop ("run") 60 vezes por segundo self.gameRunTimer = [NSTimer scheduledTimerWithTimeInterval:1.0f/60.0f target:self selector:@selector(run) userInfo:nil repeats:YES]; // Vari self.running = YES; } return self; } // Game Loop - (void)run { if (self.running == YES) { NSLog(@"Impossible Running...!"); } } Como criamos nosso timer dentro do init, no momento em que a Impossible for criada ela j Rode o projeto novamente. Temos nossa primeira sa polgante. O console exibe Impossible Running...! infinitamente. 2.3 Desenhando o objeto principal O motor do nosso jogo j tece na tela. Nosso pr c muitas vezes por segundo. No nosso caso temos um c por qualquer recurso ou imagem melhor trabalhado para definir um personagem interessante. Com a populariza simples de jogos 2D reapareceu com muita for 20 Casa do C Cap quadradas, com fundos transparentes. Voc no Google por como esse jogo funciona com o que falamos at Figura 2.8: Sprites do famoso Mario Bros. Utilizando iOS UIView e CGContext Para desenhar elementos na tela do jogo no iPhone, temos algumas op der todo o desenho atrav Quando desenhamos na vida real, precisamos de ferramentas como pinc lugar para utiliz essa tela, na qual podemos desenhar diversas formas ou mesmo Sprites. Para ter acesso a esse elemento, podemos declarar nossa classe como sendo uma tela, por meio da UIView. 21 2.3. Desenhando o objeto principal Casa do C Ao utilizar uma UIView, temos um tipo de View especializado em exibir dese- nhos na tela. O principal prop que renderizar as atualiza objetos em seu m identificar que a nossa UIView deve ser redesenhada. Para desenhar vamos usar as fun CoreGraphics da Apple. Com ela, conseguiremos definir elementos como textos, linhas, figuras geom objetos. Vamos come drawRect: em nossa classe Impossible.m, e declarando uma vari t - (void)drawRect:(CGRect)rect { CGContextRef context = UIGraphicsGetCurrentContext(); // desenha o player // o que fazer aqui??? j } No momento em que sobrescrevemos o m total controle do que iremos desenhar ou n Finalmente vamos desenhar o player! Utilizaremos nossos pinc fun criar um outro m - (void)drawPlayerInContext:(CGContextRef)context { UIGraphicsPushContext(context); CGContextBeginPath(context); CGContextAddArc(context, 160, 275, 25, 0, (2 * M_PI), YES); // Circulo de 360° (0 ~ 2pi) 22 Casa do C Cap CGContextSetRGBFillColor(context, 0.0f, 0.9f, 0.0f, 1.0f); CGContextFillPath(context); UIGraphicsPopContext(); } Para desenhar nosso player, criamos um c com uma cor. Agora basta invocar o m Repare que s - (void)drawRect:(CGRect)rect { CGContextRef context = UIGraphicsGetCurrentContext(); [self drawPlayerInContext:context]; } Rode novamente o seu projeto. Obtivemos nosso primeiro resultado! 23 2.4. Captando os comandos do usu Casa do C Figura 2.9: Player na tela. 2.4 Captando os comandos do usu objetos Existem diversas maneiras de interagir com um jogo e com o player principal. Mover o mouse e clicar, utilizar o teclado, tocar a tela ou mesmo capturar o movimento de um aparelho, usando por exemplo, o aceler toque na tela para mover o player. Vamos capturar cada toque como sendo um input do usu Nesse momento vamos explorar esse conceito de inputs do usu e novamente reparar na import UIViewController principal como 24 Casa do C Cap porta de entrada do jogo. Utilizaremos um objeto do pr UITapGestureRecognizer para identificar os toques na tela. Toda vez que um toque for detectado, o UITapGestureRecognizer chamar as coordenadas tocadas na superf Nesse momento, ao detectar um toque, moveremos para baixo nosso player. Re- pare que aqui poder interessantes no jogo, como mover para um lado ou para ou outro, mover mais r pido etc. Para fim de entendimento de conceito e prototipa nessa implementa Antes de mais nada, precisamos saber em que posi o atributo playerY no Impossible.h: @property (nonatomic) int playerY; Defina tamb todo init da Impossible.m: - (id)init { self = [super init]; if (self) { // Timer que executa o Game Loop (m self.gameRunTimer = [NSTimer scheduledTimerWithTimeInterval:1.0f/60.0f target:self selector:@selector(run) userInfo:nil repeats:YES]; // Posi self.playerY = 275; // Vari self.running = YES; } return self; } E, toda vez que invocarem o drawPlayerInContext:, vamos desenh nessa altura, em vez daquele n 25 2.4. Captando os comandos do usu Casa do C // Desenha o Player - (void)drawPlayerInContext:(CGContextRef)context { UIGraphicsPushContext(context); CGContextBeginPath(context); CGContextAddArc(context, 160.0f, self.playerY, 25.0f, 0, (2 * M_PI), YES); // C CGContextSetRGBFillColor(context, 0.0f, 0.9f, 0.0f, 1.0f); CGContextFillPath(context); UIGraphicsPopContext(); } Ainda na Impossible.m teremos um m ver o player para baixo (a tela do iPhone possui a posi esquerdo): - (void)moveDown:(int)pixels { if (self.running == YES) { self.playerY += pixels; } } Como este ser GameViewController, temos tamb - (void)moveDown:(int)pixels; Para podermos receber a informa GameViewController.m criaremos nosso objeto para reconhecer toques na tela: - (void)viewDidLoad { //... // Instancia um objeto para reconhecimento de gestos do tipo "Tap" // e a a 26 Casa do C Cap UITapGestureRecognizer *tapGesture = [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTapGesture:)]; // Adiciona o reconhecimento de gestos // usu [self.impossibleView addGestureRecognizer:tapGesture]; } A classe ainda n - (void)handleTapGesture:(UITapGestureRecognizer *)gesture { if (gesture.state == UIGestureRecognizerStateEnded) { [self.impossibleView moveDown:10]; } } Rode o jogo. Mas temos um problema: ao tocar na tela, nosso player ainda n Isto acontece porque n Impossible redesenhar a tela. Vamos corrigir este problema! No run da Impossible.m vamos informar que a tela precisa ser redesenhada: - (void)run { if (self.running == YES) { NSLog(@"Impossible Running...!"); // Informa ao iOS que a tela deve ser redesenhada [self setNeedsDisplay]; } } Rode o jogo. Algo j e o que acontece com a tela: 27 2.4. Captando os comandos do usu Casa do C Figura 2.10: Player movendo na tela, mas temos um problema. Importante: Limpando a tela Lembra da rela impress com uma pequena varia dividualmente, da a impress O que faremos para que o player se mova na tela formos renderizar um novo frame. Como nosso prot fundo com imagens se movendo, podemos apenas iniciar o frame com um fundo preto. Para jogos com backgrounds mais complexos a estrat Casa do C Cap mais complexa. Na classe Impossible.m, altere o m ground da tela de preto, chamando o novo m que iremos criar: - (void)drawRect:(CGRect)rect { CGContextRef context = UIGraphicsGetCurrentContext(); [self drawBackgroundInContext:context]; [self drawPlayerInContext:context]; } // Desenha o Plano de Fundo - (void)drawBackgroundInContext:(CGContextRef)context { UIGraphicsPushContext(context); CGContextSetRGBFillColor(context, 0.0, 0.0, 0.0, 1.0); CGContextFillRect(context, CGRectMake(0, 0, self.frame.size.width, self.frame.size.height)); UIGraphicsPopContext(); } Rode agora e toque na tela. O player se mover 2.5 Criando o inimigo Chegamos diversas maneiras. Pode ser o tempo, pode ser uma l A partir dos inimigos podemos conhecer diversos conceitos importantes para um jogo funcionar. Assim como o player principal, os inimigos possuem seus pr prios movimentos, por tumam ser definidos por l mais inteligente ele for de acordo com a movimenta Para o prot lamos anteriormente, esse c 2.5. Criando o inimigo Casa do C ou seja, de acordo com o passar do jogo, seu raio ir mente, ocupando cada vez mais a regi Vamos criar, na classe Impossible.h, uma propriedade que representa o raio do inimigo: @property (nonatomic) int enemyRadius; Assim como temos um m Impossible.m teremos um que desenha o inimigo: - (void)drawRect:(CGRect)rect { CGContextRef context = UIGraphicsGetCurrentContext(); [self drawBackgroundInContext:context]; [self drawPlayerInContext:context]; [self drawEnemyInContext:context]; } //... // Desenha o Inimigo - (void)drawEnemyInContext:(CGContextRef)context { UIGraphicsPushContext(context); CGContextBeginPath(context); CGContextAddArc(context, 100, 100, self.enemyRadius, 0, (2 * M_PI), YES); // C CGContextSetRGBFillColor(context, 0.4f, 0.4f, 0.4f, 1.0f); CGContextFillPath(context); UIGraphicsPopContext(); } Para este game, queremos que a cada novo frame o raio do inimigo cres Para isto, vamos alterar o run: 30 Casa do C Cap - (void)run { if (self.running == YES) { // Aumenta raio do inimigo self.enemyRadius++; // Informa ao iOS que a tela deve ser redesenhada [self setNeedsDisplay]; } } Ao rodar o jogo, nosso inimigo cresce sozinho e o player se afasta com o touch na tela! 31 2.6. Detectando colis Casa do C Figura 2.11: Inimigo aparece na tela. Nesse momento conseguimos mover o player principal e tentar se afastar do ini- migo que cresce cada vez mais com o passar do tempo. Agora precisamos detectar a colis 2.6 Detectando colis J A gra acontece, situa No nosso caso, o player n 32 Casa do C Cap Repare que aqui ainda n Poder Como um vulc Detectando colis Precisamos ent seguiu encostar no circulo menor, movido pelo usu colis Uma maneira bem tradicional ou ret mesmo. Na imagem abaixo, uma nave de jogos de tiro. Para detectar que algo colide com ela, a 33 2.6. Detectando colis Casa do C Figura 2.12: Regi Pode-se questionar se esse m quadrado, que n essa aproxima Simplificar a detec tica bem comum. Al que analisar cada item real de uma imagem de um player. Colis Chegamos a um dos conceitos mais importantes no desenvolvimento de um game! Precisamos identificar a colis 34 Casa do C Cap chave do nosso prot formas de pontuar, e muitas delas utilizam a colis com inimigos. Existem diversas formas de detectar colis mais simples. Para o nosso prot A colis conceitos matem Figura 2.13: Teorema de Pit Na figura anterior, existe uma maneira matematicamente simples de determinar 35 2.6. Detectando colis Casa do C se as circunfer De posse das duas informa raios Vamos ao c Primeiro criaremos algumas propriedades para esse c ferem inimigo. Altere sua classe Impossible.h para ter todos esses atributos. Repare que j possu @property (nonatomic) int playerX; @property (nonatomic) int playerY; @property (nonatomic) int playerRadius; @property (nonatomic) int enemyX; @property (nonatomic) int enemyY; @property (nonatomic) int enemyRadius; No init da Impossible.m, vamos definir os valores padr dades: - (id)init { //... // Posi self.playerRadius = 25; self.playerX = 160; self.playerY = 275; // Posi self.enemyX = 0; self.enemyY = 0; self.enemyRadius = 0; //... } 36 Casa do C Cap Altere os m que foram criadas: // Desenha o Player - (void)drawPlayerInContext:(CGContextRef)context { UIGraphicsPushContext(context); CGContextBeginPath(context); CGContextAddArc(context, self.playerX, self.playerY, self.playerRadius, 0, (2 * M_PI), YES); // C CGContextSetRGBFillColor(context, 0.0f, 0.9f, 0.0f, 1.0f); CGContextFillPath(context); UIGraphicsPopContext(); } // Desenha o Inimigo - (void)drawEnemyInContext:(CGContextRef)context { UIGraphicsPushContext(context); CGContextBeginPath(context); CGContextAddArc(context, self.enemyX, self.enemyY, self.enemyRadius, 0, (2 * M_PI), YES); // C CGContextSetRGBFillColor(context, 0.4f, 0.4f, 0.4f, 1.0f); CGContextFillPath(context); UIGraphicsPopContext(); } O m fun drada. // Verifica Colis 37 2.6. Detectando colis Casa do C - (void)checkCollision { double distance = 0.0f; // Teorema de Pit distance = pow(self.playerY - self.enemyY, 2) + pow(self.playerX - self.enemyX, 2); distance = sqrt(distance); if (distance <= (self.playerRadius + self.enemyRadius)) { self.running = NO; } } Adicione a chamada ao m // Game Loop - (void)run { if (self.running == YES) { // Aumenta raio do inimigo self.enemyRadius++; // Checa colis [self checkCollision]; // Informa ao iOS que a tela deve ser redesenhada [self setNeedsDisplay]; } } Rode o jogo, quando houver colis parar! Isto acontece porque quando ocorre a colis running para falso, que Seria mais bonito um Game Over mais convincente, n uma mensagem de Game Over. Para os textos de nosso game utilizaremos a classe UILabel pr Crie uma propriedade que exibir @property (nonatomic, strong) UILabel *gameOverLabel; 38 Casa do C Cap No init da Impossible.m vamos instanciar o label gameOverLabel, s sem nenhum texto para que ele n - (id)init { //... // Cria self.gameOverLabel = [[UILabel alloc] initWithFrame:CGRectMake(20.0f, 40.0f, 300.0f, 50.0f)]; self.gameOverLabel.font = [UIFont systemFontOfSize:40.0f]; self.gameOverLabel.textColor = [UIColor lightGrayColor]; self.gameOverLabel.backgroundColor = [UIColor clearColor]; self.gameOverLabel.text = @""; [self addSubview:self.gameOverLabel]; //... } Ainda na Impossible.m, altere o m a mensagem de Game Over quando ocorrer uma colis // Verifica Colis - (void)checkCollision { double distance = 0.0f; // Teorema de Pit distance = pow(self.playerY - self.enemyY, 2) + pow(self.playerX - self.enemyX, 2); distance = sqrt(distance); if (distance <= (self.playerRadius + self.enemyRadius)) { self.gameOverLabel.text = @"GAME OVER!"; self.running = NO; } } A partir daqui, o jogo j alcance o player. Essa muito tempo. Atualmente, com o avan cessamento cada vez maiores, detec criadas. 39 2.7. Adicionando um placar Casa do C Figura 2.14: Player aparece na tela. 2.7 Adicionando um placar Vamos ver agora um pr detec Crie a propriedade score na Impossible.h, al bir @property (nonatomic) int score; 40 Casa do C Cap @property (nonatomic, strong) UILabel *gameScoreLabel; - (void)increaseScore:(int)points; Na Impossible.m vamos instanciar o label do score e implementar o m que aumenta o score: - (id)init { //... // Cria self.gameScoreLabel = [[UILabel alloc] initWithFrame:CGRectMake(20.0f, 85.0f, 300.0f, 30.0f)]; self.gameScoreLabel.font = [UIFont systemFontOfSize:25.0f]; self.gameScoreLabel.textColor = [UIColor whiteColor]; self.gameScoreLabel.backgroundColor = [UIColor clearColor]; self.gameScoreLabel.text = @"0"; [self addSubview:self.gameScoreLabel]; //... } // Soma Pontos - (void)increaseScore:(int)points { if (self.running == YES) { self.score += points; self.gameScoreLabel.text = [NSString stringWithFormat:@"%d", self.score]; } } No m o m - (void)handleTapGesture:(UITapGestureRecognizer *)gesture { if (gesture.state == UIGestureRecognizerStateEnded) { [self.impossibleView moveDown:10]; [self.impossibleView increaseScore:100]; } } 41 2.8. Criando bot Casa do C O jogo deve estar assim: Figura 2.15: Score aparece na tela. 2.8 Criando bot Outro conceito importante em jogos de configura exemplo, a dificuldade ou mesmo fechar e sair do jogo s desenvolvimento. No prot (reiniciar) e Stop (parar). Simplificaremos para entender o conceito, e depois, poderemos converter para bot 42 Casa do C Cap No arquivo GameViewController.m crie os bot e Stop: - (void)viewDidLoad { //... // Bot UIButton *buttonRestart = [UIButton buttonWithType:UIButtonTypeCustom]; [buttonRestart setTitle:@"Restart" forState:UIControlStateNormal]; buttonRestart.contentHorizontalAlignment = UIControlContentHorizontalAlignmentLeft; buttonRestart.frame = CGRectMake(20.0f, 170.0f, 80.0f, 35.0f); [buttonRestart addTarget:self action:@selector(restart:) forControlEvents:UIControlEventTouchUpInside]; [self.view addSubview:buttonRestart]; // Bot UIButton *buttonStop = [UIButton buttonWithType:UIButtonTypeCustom]; [buttonStop setTitle:@"Stop" forState:UIControlStateNormal]; buttonStop.contentHorizontalAlignment = UIControlContentHorizontalAlignmentLeft; buttonStop.frame = CGRectMake(20.0f, 250.0f, 80.0f, 35.0f); [buttonStop addTarget:self action:@selector(stop:) forControlEvents:UIControlEventTouchUpInside]; [self.view addSubview:buttonStop]; } - (void)restart:(id)sender { // Reinicia o jogo // J } - (void)stop:(id)sender { // Para o jogo 43 2.8. Criando bot Casa do C // J } Rode o jogo e veja que os bot Figura 2.16: Bot Agora, precisaremos de uma forma de parar e reinicializar as propriedades do game. No arquivo Impossible.h defina os m - (void)restart; - (void)stopGame; No arquivo Impossible.m implemente estes m restart, utilizaremos uma abordagem simples de reinicializa 44 Casa do C Cap // Para o Jogo - (void)stopGame { self.running = NO; } // Reinicia o jogo, redefinindo as vari - (void)restart { self.enemyX = 0; self.enemyY = 0; self.enemyRadius = 0; self.playerRadius = 25; self.playerX = 160; self.playerY = 275; self.score = 0; self.gameScoreLabel.text = @"0"; self.gameOverLabel.text = @""; self.running = YES; } Precisamos agora alterar para que o toque dos bot classe Impossible. Na GameViewController.m, altere os m e stop: : - (void)restart:(id)sender { [self.impossibleView restart]; } - (void)stop:(id)sender { [self.impossibleView stopGame]; } A tela final do prot 45 2.9. Adicionando mais vida: imagens da nave e do c Casa do C Figura 2.17: Prot 2.9 Adicionando mais vida: imagens da nave e do c Temos toda a l Vamos desenhar um background que simule um c isso, utilizaremos a imagem sky.png. Um ponto importante mais sprites forem adicionados ao jogo, mais esfor tornar o jogo mais lento. Mais a frente, utilizaremos frameworks que otimizam essa quest Adicione os arquivos sky.png e nave.png no diret 46 Casa do C Cap fazer isto pelo Xcode clicando na pasta Impossible do projeto, e acessando o menu File, Add Files to "Impossible".... Vamos alterar algumas linhas para utilizar as imagens. No m drawBackgroundInContext: da classe Impossible adicione as linhas que de- senhar // Desenha o Plano de Fundo - (void)drawBackgroundInContext:(CGContextRef)context { // UIGraphicsPushContext(context); // CGContextSetRGBFillColor(context, 0.0, 0.0, 0.0, 1.0); // CGContextFillRect(context, CGRectMake(0, 0, // self.frame.size.width, // self.frame.size.height)); // UIGraphicsPopContext(); // Utiliza uma imagem do projeto e a desenha em um // determinado ponto da tela UIImage *image = [UIImage imageNamed:@"sky.png"]; [image drawAtPoint:CGPointMake(0.0f, 0.0f)]; } Altere o m // Desenha o Player - (void)drawPlayerInContext:(CGContextRef)context { // UIGraphicsPushContext(context); // CGContextBeginPath(context); // CGContextAddArc(context, // self.playerX, // self.playerY, // self.playerRadius, // 0, (2 * M_PI), // YES); // C // CGContextSetRGBFillColor(context, 0.0f, 0.9f, 0.0f, 1.0f); // CGContextFillPath(context); // UIGraphicsPopContext(); // Utiliza uma imagem do projeto e a desenha em um // determinado ponto da tela 47 2.9. Adicionando mais vida: imagens da nave e do c Casa do C UIImage *image = [UIImage imageNamed:@"nave.png"]; [image drawAtPoint:CGPointMake( (self.playerX - (image.size.width / 2)), (self.playerY - (image.size.height / 2)))]; } Altere a cor do inimigo para vermelho. // Desenha o Inimigo - (void)drawEnemyInContext:(CGContextRef)context { UIGraphicsPushContext(context); CGContextBeginPath(context); CGContextAddArc(context, self.enemyX, self.enemyY, self.enemyRadius, 0, (2 * M_PI), YES); // C //CGContextSetRGBFillColor(context, 0.4f, 0.4f, 0.4f, 1.0f); CGContextSetRGBFillColor(context, 1.0f, 0.0f, 0.0f, 1.0f); CGContextFillPath(context); UIGraphicsPopContext(); } Pode rodar o jogo, o prot jogo! 48 Casa do C Cap Figura 2.18: Imagem do jogo. 2.10 Conclus Um jogo possui diversos conceitos bem espec tipos de projetos de software como web ou desktop. Para desenvolver um jogo Um jogo ocorre normalmente em um loop infinito, no qual inputs, ou seja, entradas de comandos, s game. O movimento do player, normalmente uma imagem chamada sprite cos- tuma ser dado a partir desses inputs, assim como o movimento dos inimigos, que 49 2.10. Conclus Casa do C indiretamente, tamb Conceitos perif bot pensados na execu Com isso em mente, podemos planejar nosso jogo e iniciar seu desenvolvi- mento. 50 Cap Hist Jogos s comando das a No come al Mas se criarmos um jogo de nave, qual apelo ele ter mil outros jogos que podemos encontrar na App Store? O que far O enredo, os personagens e a carisma s jogo. que j naves? Como trazer isso para um contexto com o qual os nossos jogadores estejam familiarizados? Criaremos um jogo tamb 3.1. 14-bis Casa do C a um importante brasileiro que participou do inicio dessa revolu Em 1873 nascia Alberto Santos Dumont, um piloto, atleta e inventor brasileiro. Figura 3.1: Alberto Santos Dumont Santos Dumont projetou, construiu e voou os primeiros bal motor a gasolina, conquistando o Pr Torre Eiffel. Se tornou ent s 3.1 14-bis Em 1906, Santos Dumont criou um avi primeiro objeto mais pesado que o ar a superar a gravidade terrestre. 52 Casa do C Cap Figura 3.2: 14-bis do brasileiro Santos Dumont O pesadelo de Santos Dumont Em agosto de 1914 come a ser utilizados em combates a metralhadoras e bombas. Santos Dumont viu seu sonho se transformar em pesadelo. Em 1932, um conflito entre o estado de S foi iniciado e avi 3.2 14-bis VS 100 Meteoros Santos Dumont inventou o 14-bis n gearemos Dumont e sua inven Depois do meteoro Shoemaker-Levy-9 que caiu em J que caiu na R brasileiro detectou a presen sejam detidos urgentemente. 53 3.2. 14-bis VS 100 Meteoros Casa do C Figura 3.3: 14-bis VS 100 Meteoros O planeta est buscam formas de evitar o fim. Eis que surge um brasileiro, com seu invento 14-bis, para enfrentar o perigo e tentar salvar a todos n para destruir os meteoros que caem sobre a Terra, voc 54 Casa do C Cap Figura 3.4: Fluxo do game 14-bis VS 100 Meteoros 55 Cap Tela inicial: Lidando com Background, logo e bot Hora de come de desenvolvimento de jogos como game loop, sprites, colis Vamos criar um jogo baseado no game do cap utilizando um framework de desenvolvimento de jogos chamado Cocos2D. O motivo de utilizar um framework daqui pra frente bot mas com posicionamento das imagens sabilidade do framework Casa do C • Trabalhar mais facilmente com sons e efeitos Nesse cap ground, e quatro bot na tela e como detectar os inputs dos bot tulo, devemos ter a tela inicial como a seguir: Figura 4.1: Tela de abertura. Voc rias, no GitHub: https://github.com/BivisSoft/jogos_ios_14bis 58 Casa do C Cap Mas prefira voc e certamente ajudar 4.1 Sobre o Cocos2D O Cocos2D foi criada em Python e desde ent Para utilizar a vers http://www.cocos2d-iphone.org/download/ Ap um projeto j e navegue at ./install-templates.sh -f. $ cd cocos2d-iphone $ ./install-templates.sh -f 4.2 Iniciando o projeto Vamos iniciar criando um novo projeto no Xcode, por template do Cocos2D como base. Abra o Xcode e v New e selecione Project... para criar um novo projeto. A tela para escolher um template de projeto ser se e clique em Next. 59 4.2. Iniciando o projeto Casa do C Figura 4.2: Novo projeto de Cocos2D. Criaremos um projeto chamado Bis. O nome do bundle ficar voc voc no GitHub em https://github.com/BivisSoft/jogos_ios_14bis. Como nosso jogo ser apenas para iPhone, no campo Device escolha iPhone. 60 Casa do C Cap Figura 4.3: Criando o projeto Cocos2D. Clique em Next e escolha o local onde o projeto ser Nesse momento temos o projeto iniciado. Execute-o e o simulador dever uma tela escrito Hello World: Figura 4.4: Simulador com Hello World do Cocos2D. 61 4.3. ajustando a orienta Casa do C Voc dor? Estes n outro, e a quantidade de frames por segundo. Estas informa Para nosso game, vamos retirar estes n da tela. Para isto, no arquivo AppDelegate., no m application:didFinishLaunchingWithOptions: vamos alterar a op- //... // Display FSP and SPF [director_ setDisplayStats:NO]; //... 4.3 ajustando a orienta Por padr modo paisagem. Para nosso game, alteraremos o projeto para rodar o jogo em modo retrato. Abra as configura Orientations e marque apenas a op Figura 4.5: Configurando projeto em modo retrato. No AppDelegate.m, dentro do @implementation da MyNavigationController, altere os seguintes m tela n - (NSUInteger)supportedInterfaceOrientations { return UIInterfaceOrientationMaskPortrait; } 62 Casa do C Cap - (BOOL)shouldAutorotateToInterfaceOrientation: (UIInterfaceOrientation)interfaceOrientation { return interfaceOrientation == UIInterfaceOrientationPortrait; } Rode o game e agora o Hello World dever Figura 4.6: Simulador com Hello World em modo retrato. 63 4.4. Background Casa do C 4.4 Background A primeira tela do game framework, ganhamos alguns reconhecimentos do Cocos2D, como conseguir fazer a transi Uma classe que herda de CCLayer n podemos criar nossa tela inicial como bem entendermos, apenas utilizando esse comportamento para informar ao framework que tipo de objeto estamos represen- tando. Layers Criar telas com o CCLayer do Cocos2D se sobrep forma, e quando colocadas uma sobre as outras definem a tela final. Na tela de abertura, podemos pensar em camadas para a imagem de background, para o logo e para o menu. Criaremos uma classe chamada TitleScreen. Voc ou grupo do projeto que desejar. No nosso projeto, utilizamos o diret Lembre-se de organizar suas classes em grupos significativos. Nesta classe, utilizaremos um segundo componente do Cocos2D. Para instanciar uma tela no framework, utilizamos a classe CCScene, que Scenes Outro objeto importante do Cocos2D s inicializar telas do jogo. Um jogo pode ter quantas Scenes forem necess r Por exemplo, no nosso jogo teremos a tela de abertura, a tela do jogo, a tela de ajuda, a tela de pause etc. Cada uma delas Vamos ao c trabalhar com camadas e de uma tela. Criaremos a classe TitleScreen que re- ceber base da tela inicial. Criaremos o m dentro de uma Scene. Voc 64 Casa do C Cap criarmos ter No header TitleScreen.h iremos declarar o m # import "cocos2d.h" @interface TitleScreen : CCLayer + (CCScene *)scene; @end E no implementation TitleScreen.m vamos implementar o m pode copi + (CCScene *)scene { // 'scene' is an autorelease object. CCScene *scene = [CCScene node]; // 'layer' is an autorelease object. TitleScreen *layer = [TitleScreen node]; // add layer as a child to scene [scene addChild:layer]; // return the scene return scene; } O c no nosso caso, esses elementos ser Vamos iniciar configurando o background do game. Assim como bot o background tamb para manipular imagens temos o conceito de Sprites, que basicamente jeto associado Sprites Um Sprite no Cocos2D 2D que pode ser movida, rotacionada, animada, ter sua escala alterada etc. Umas das 65 4.4. Background Casa do C vantagens de utilizar Sprites como objetos do Cocos2D possibilidades de anima Criaremos ent lo mando qual a imagem desejada e configuramos sua posi o tamanho da tela, tanto largura quanto altura, para posicionar o background de forma centralizada. Faremos isso com um elemento muito importante do Cocos2D, o CCDirector, que ser na tela de abertura, instanciando-o no init da TitleScreen.m: - (id)init { self = [super init]; if (self) { // Imagem de Background CCSprite *background = [CCSprite spriteWithFile:@"background.png"]; background.position = ccp([CCDirector sharedDirector].winSize.width / 2.0f, [CCDirector sharedDirector].winSize.height / 2.0f); [self addChild:background]; } return self; } Da mesma forma que acontece nos demais aplicativos de iOS, podemos ter uma imagem para aparelhos de tela n aparelhos com tela retina. Entretanto, ao inv retina. O Cocos2D j arquivos com final “-hd”. Estas configura Appdelegate.m. Basta apenas incluir as imagens em ambas resolu e o Cocos2D vai utilizar uma ou outra automaticamente. Precisamos ent jogo. Criamos algumas imagens e utilizamos outras de fontes gratuitas para nosso jogo. Voc https://github.com/bivissoft/jogos_ios_14bis 66 Casa do C Cap Coloque os arquivos background.png e background-hd.png dentro do di- ret outras imagens, sons e arquivos dos quais precisaremos no decorrer do desenvolvi- mento de nosso jogo. 4.5 Assets da Tela de abertura A tela de abertura do game ter Para come utilizaremos no game. Crie a classe Assets, subclasse de um NSObject, e em seu header declare as imagens que utilizaremos: @interface Assets : NSObject # define kBACKGROUND @"background.png" # define kLOGO @"logo.png" # define kPLAY @"play.png" # define kHIGHSCORE @"highscore.png" # define kHELP @"help.png" # define kSOUND @"sound.png" @end Para facilitar, importaremos a classe Assets em nosso arquivo Prefix.pch. Desta forma, todas as classes de nosso projeto automaticamente importar Assets. Tamb classe que ser //... # ifdef __OBJC__ # import # import # import "cocos2d.h" # import "Assets.h" # endif Sempre ap jeto para que as classes identifiquem as altera Ap 67 4.6. Capturando configura Casa do C Agora, altere a linha da TitleScreen.m que chamava o background.png para utilizar a constante da classe Assets: //... CCSprite *background = [CCSprite spriteWithFile:kBACKGROUND]; //... Utilizaremos essa classe para adicionar outros assets posteriormente, quando os objetos inimigos e o player forem desenhados. essa para n queira alterar a imagem da nave principal. 4.6 Capturando configura tivo Existem diversos dispositivos iOS atualmente, com diferentes tamanhos de tela. Existem algumas t mento do jogo. Utilizaremos aqui uma t aos diversos dispositivos, capturando as medidas e utilizando-os sempre que for necess Para iniciar as configura • SCREEN_WIDTH(): Retorna a largura da tela • SCREEN_HEIGHT(): Retorna a altura da tela • WIN_SIZE(): Retorna o tamanho (largura e altura) da tela O Cocos2D nos ajuda nesse momento, pois j executar essa fun par A vantagem de utilizar uma macro, alter 68 Casa do C Cap Director O CCDirector controla o FPS, tamanho da tela, resolu scenes, ou seja, transi tela est Vamos criar a classe DeviceSettings, respons CCDirector e retornar as medidas e configura Crie a classe DeviceSettings, subclasse de um NSObject, e no header dela declare as seguintes macros: # define SCREEN_WIDTH() \ [CCDirector sharedDirector].winSize.width # define SCREEN_HEIGHT() \ [CCDirector sharedDirector].winSize.height # define WIN_SIZE() \ [CCDirector sharedDirector].winSize Como importaremos a DeviceSettings em diversas outras classes do projeto, vamos aproveitar e inclu //... # ifdef __OBJC__ # import # import # import "cocos2d.h" # import "Assets.h" # import "DeviceSettings.h" # endif Com isso, podemos refatorar o posicionamento do background no init da TitleScreen.m para ficar como a seguir. //... background.position = ccp(SCREEN_WIDTH() / 2.0f, SCREEN_HEIGHT() / 2.0f); //... O CCDirector 69 4.6. Capturando configura Casa do C Iniciando a tela de abertura Tela inicial preparada! Agora precisamos fazer a transi informar ao Cocos2D para iniciar a tela de abertura. Sempre ap IntroLayer. Esta scene gada ao abrir o aplicativo, evitando que a tela Primeiramente, vamos ajustar a orienta IntroLayer. Altere o m imagem de abertura: -(id) init { if( (self=[super init])) { // ask director for the window size CGSize size = [[CCDirector sharedDirector] winSize]; CCSprite *background; if( UI_USER_INTERFACE_IDIOM() == UIUserInterfaceIdiomPhone ) { background = [CCSprite spriteWithFile:@"Default-568h@2x.png"]; //background = [CCSprite spriteWithFile:@"Default.png"]; //background.rotation = 90; } else { background = [CCSprite spriteWithFile:@"Default-Landscape~ipad.png"]; } background.position = ccp(size.width/2, size.height/2); // add the label as a child to this Layer [self addChild: background]; } return self; } Agora vamos alterar a IntroLayer.m para que chame nossa TitleScreen ap 70 Casa do C Cap esse controle. Utilizaremos o CCDirector novamente, dessa vez para apresentar uma nova tela, utilizando o m a TitleScreen, que # import "TitleScreen.h" //... @implementation IntroLayer //... -(void) onEnter { [super onEnter]; [[CCDirector sharedDirector] replaceScene: [CCTransitionFade transitionWithDuration:1.0 scene:[TitleScreen scene] ]]; } @end J rado! Fa 4.7 Logo Vamos utilizar a mesma ideia e colocar um logo do jogo no topo da tela. O logo mamos de Sprites. Criaremos um Sprite de forma simples para posicionar o logo e utilizaremos o m colocar o elemento. Pra finalizar, basta adicionar o logo Mude o init de sua TitleScreen.m: - (id)init { self = [super init]; if (self) { //... // Imagem de Logo CCSprite *title = [CCSprite spriteWithFile:kLOGO]; title.position = ccp(SCREEN_WIDTH() / 2.0f, SCREEN_HEIGHT() - 130.0f); 71 4.8. Bot Casa do C [self addChild:title]; } return self; } @end Ao rodar o projeto j nados. 4.8 Bot Os bot mover o player, atirar etc. Utilizando o Cocos2D o trabalho com inputs de bot n mento dos Sprites. Esse trabalho ser ocupar com a l No Cocos2D, a classe CCMenuItem e suas subclasses representam os bot classe CCMenu representa o menu onde estes bot O que precisamos agora No init da TitleScreen.m, criaremos nosso menu com os bot - (id)init { self = [super init]; if (self) { //... 72 Casa do C Cap // Cria os bot CCMenuItemSprite *playButton = [CCMenuItemSprite itemWithNormalSprite:[CCSprite spriteWithFile:kPLAY] selectedSprite:[CCSprite spriteWithFile:kPLAY] target:self selector:@selector(playGame:)]; CCMenuItemSprite *highscoreButton = [CCMenuItemSprite itemWithNormalSprite:[CCSprite spriteWithFile:kHIGHSCORE] selectedSprite:[CCSprite spriteWithFile:kHIGHSCORE] target:self selector:@selector(viewHighscore:)]; CCMenuItemSprite *helpButton = [CCMenuItemSprite itemWithNormalSprite:[CCSprite spriteWithFile:kHELP] selectedSprite:[CCSprite spriteWithFile:kHELP] target:self selector:@selector(viewHelp:)]; CCMenuItemSprite *soundButton = [CCMenuItemSprite itemWithNormalSprite:[CCSprite spriteWithFile:kSOUND] selectedSprite:[CCSprite spriteWithFile:kSOUND] target:self selector:@selector(toggleSound:)]; // Define as posi playButton.position = ccp(0.0f, 0.0f); highscoreButton.position = ccp(0.0f, -50.0f); helpButton.position = ccp(0.0f, -100.0f); soundButton.position = ccp((SCREEN_WIDTH() / -2.0f) + 70.0f, (SCREEN_HEIGHT() / -2.0f) + 70.0f); // Cria o menu que ter CCMenu *menu = [CCMenu menuWithItems:playButton, highscoreButton, helpButton, soundButton, nil]; [self addChild:menu]; } return self; } @end 73 4.9. Conclus Casa do C Perceba que nossos bot foram criados utilizando a classe CCMenuItemSprite, que representa um bot Existem diversos tipos de bot CCMenuItemImage para UIImages, entre outros. Por padr no menu ser posi Quando criamos os bot que indicam os m apenas criar estes m - (void)playGame:(id)sender { NSLog(@"Bot } - (void)viewHighscore:(id)sender { NSLog(@"Bot } - (void)viewHelp:(id)sender { NSLog(@"Bot } - (void)toggleSound:(id)sender { NSLog(@"Bot } Rode o projeto e confira os bot 4.9 Conclus O jogo deve estar como mostrado na tela abaixo, com background e logo configura- dos. Al de Som. 74 Casa do C Cap Figura 4.7: Tela de abertura. O core do desenvolvimento de um jogo n t Nesse cap CCScene), camadas ( CCLayer), menus ( CCMenu) e bot lizamos tamb A seguir, faremos a transi elementos do game. 75 Cap Tela do jogo e objetos inimigos Hora de adicionar perigo ao nosso game! Nesse cap jogo de fato, onde toda a a Para iniciar, passaremos pela transi Al cos2D ser Utilizaremos muito do que j tar camadas de uma tela, CCSprites para controlar objetos e CCScene para criar a tela do jogo. Al No fim desse cap al 5.1. GameScene Casa do C Figura 5.1: Meteoros inimigos. 5.1 GameScene Precisamos de uma tela para o jogo, para conter os elementos principais de itera uma tela herdando da classe CCLayer do Cocos2D, para que possamos ter diversas camadas atuantes, como bot Tamb CCScene, que saber 78 Casa do C Cap O Maestro Idealmente, essa classe n onar como um orquestrador de todos os elementos, ou seja, um maestro em uma orquestra, que dirige e comanda o que todos os outros elementos fazem e como eles interagem entre si. Ela ser correspondente. Algumas das responsabilidades da GameScene, a classe maestro do jogo, devem ser: • Iniciar a tela do game e organizar as camadas • Inicializar cada um desses objetos A classe GameScene tem muita responsabilidade, por l conceitos vistos anteriormente, do game loop. Vamos ent zemos anteriormente na tela de abertura. O header GameScene.h ficar @interface GameScene : CCLayer + (CCScene *)scene; @end E o implementation GameScene.m # import "GameScene.h" @implementation GameScene + (CCScene *)scene { // 'scene' is an autorelease object. 79 5.2. Transi Casa do C CCScene *scene = [CCScene node]; // 'layer' is an autorelease object. GameScene *layer = [GameScene node]; // add layer as a child to scene [scene addChild:layer]; // return the scene return scene; } - (id)init { self = [super init]; if (self) { // Imagem de Background CCSprite *background = [CCSprite spriteWithFile:kBACKGROUND]; background.position = ccp(SCREEN_WIDTH() / 2.0f, SCREEN_HEIGHT() / 2.0f); [self addChild:background]; } return self; } @end 5.2 Transi Para que o jogo comece, precisamos fazer o link entre a tela de abertura e a tela do game! Aqui utilizaremos o CCDirector que sabe manter uma CCScene ativa por vez. Al Utilizaremos o m de pausa entre uma tela e outro, gerando um efeito suave. Para isso, na classe TitleScreen, mudamos o playGame: para que bot play comece o jogo. Importe a GameScene na TitleScreen.h # import "GameScene.h" 80 Casa do C Cap E mude o m //... - (void)playGame:(id)sender { NSLog(@"Bot [[CCDirector sharedDirector] replaceScene:[CCTransitionFade transitionWithDuration:1.0 scene:[GameScene scene]]]; } Rode o jogo e clique no bot tela de background! 5.3 Engines Temos a classe que orquestrar pons os inimigos. Nossos inimigos ser Criaremos uma nova camada, um novo layer para representar esses inimi- gos. Como utilizado anteriormente, camadas s CCLayer do Cocos2D. Engine de objetos inimigos Nossa camada de objetos inimigos, os meteoros, ser migos e enviar precisaremos manter o link entre a tela principal do game e a engine de inimigos. Essa objetos com ciclos de vida diferentes que rodam pela aplica Delegates s visto este conceito em diversas outras classes do iOS, como a UITableView e UIScrollView por exemplo. A engine de inimigos sabe como e quando criar os inimigos. Mas apenas isto. 81 5.3. Engines Casa do C Nossa tela principal Em nosso game, a tela principal do jogo ser Ou seja, a tela principal do jogo ser elemento no jogo. Muitas vezes, principalmente para objetos inimigos, utilizamos n Essa ideia foi muito utilizada por jogos em que o n um if. No c Vale citar que a engine com o Cocos2D, utilizamos m um schedule para que a engine analise se deve ou n objeto inimigo na tela. Abaixo, o c header MeteorsEngine.h iremos definir um protocolo, que legate (a GameScene) ter @protocol MeteorsEngineDelegate; @interface MeteorsEngine : CCLayer @property (nonatomic, assign) iddelegate; + (MeteorsEngine *)meteorEngine; @end @protocol MeteorsEngineDelegate - (void)meteorsEngineDidCreateMeteor:(CCNode *)meteor; @end No implementation MeteorsEngine.m, vamos escrever a l meteoros: 82 Casa do C Cap # import "MeteorsEngine.h" @implementation MeteorsEngine + (MeteorsEngine *)meteorEngine { return [[[MeteorsEngine alloc] init] autorelease]; } - (id)init { self = [super init]; if (self) { [self schedule:@selector(meteorsEngine:) interval:(1.0f/10.0f)]; } return self; } - (void)meteorsEngine:(float)dt { // sorte: 1 em 30 gera um novo meteoro! if(arc4random_uniform(30) == 0) { if ([self.delegate respondsToSelector: @selector(meteorsEngineDidCreateMeteor:)]){ // Pede para o delegate criar o meteoro // (por enquanto, n [self.delegate meteorsEngineDidCreateMeteor:nil]; } } } @end Para fechar o link entre ambas as camadas, implementaremos o protocolo MeteorsEngineDelegate na tela do jogo, o que obrigar para receber os objetos criados por essa Engine e coloc Primeiramente, vamos informar na GameScene.h que ela dever o protocolo: # import "MeteorsEngine.h" 83 5.3. Engines Casa do C @interface GameScene : CCLayer + (CCScene *)scene; @end E na GameScene.m crie o m criaremos em seguida. - (void)meteorsEngineDidCreateMeteor:(CCNode *)meteor { // Aqui incluiremos o meteoro na tela } Mantendo as refer Um outro ponto importante para o controle do jogo e toda a orquestra ter todos os objetos criados de uma forma f para detectar colis Vamos guardar a refer&&NSMutableArray na classe GameScene , e tamb refer na GameScene.h # import "MeteorsEngine.h" @interface GameScene : CCLayer + (CCScene *)scene; // Engines @property (nonatomic, retain) MeteorsEngine *meteorsEngine; // Arrays @property (nonatomic, retain) NSMutableArray *meteorsArray; @end O Cocos2D n as propriedades que criarmos como retain dever m 84 Casa do C Cap Crie o m - (void)dealloc { [_meteorsEngine release]; [_meteorsArray release]; [super dealloc]; } Lembre-se de incluir o dealloc em todas as demais classes que criarmos com propriedades declaradas com retain. 5.4 Meteor Chegamos ao objeto inimigo propriamente dito. As principais responsabilidades desse objeto s • Carregar imagem ( CCSprite) • Posicionar elemento na tela o tempo Esse amos telas preenchendo-as com elementos, bot dar a base a esses elementos principais do jogo. A primeira coisa a se fazer @interface Assets : NSObject # define kMETEOR @"meteor.png" @end Vamos criar a classe Meteor. Inicialmente, cada meteoro nasce no topo da tela ( Meteor.h duas propriedades para guardar a posi construtor meteorWithImage: e o m teriormente: 85 5.4. Meteor Casa do C # import "CCSprite.h" @interface Meteor : CCSprite @property (nonatomic, assign) float positionX; @property (nonatomic, assign) float positionY; + (Meteor *)meteorWithImage:(NSString *)image; - (void)start; @end No implementation Meteor.h, nosso construtor ir do meteoro: # import "Meteor.h" @implementation Meteor + (Meteor *)meteorWithImage:(NSString *)image { Meteor *meteor = [Meteor spriteWithFile:image]; meteor.positionX = arc4random_uniform(SCREEN_WIDTH()); meteor.positionY = SCREEN_HEIGHT(); meteor.position = ccp(meteor.positionX, meteor.positionY); return meteor; } @end Repare que o objeto meteoro permanece vivo na mem Ele Aqui mais uma vez o framework nos ajuda. Para que cada frame seja renderizado durante o jogo, e a posi nos permite escolher um m definido no start, fazendo scheduleUpdate, que invocar 86 Casa do C Cap //... - (void)start { [self scheduleUpdate]; } - (void)update:(float)dt { self.positionY -= 1; self.position = ccp(self.positionX, self.positionY); } Vari O Cocos2D vai tentar invocar o seu m segundos, isso cessador pode estar ocupado com outras coisas, fazendo com que essa chamada agendada n mais. Nesse caso, vai dar uma impress a tela ser mas na verdade pode ter passado alguns segundos. Num jogo profissional, voc dir corretamente quantos pixels cada objeto deve mudar. No nosso caso, se o dt for maior que o de 1 frame, dever que 1 pixel, fazendo a regra de 3. 5.5 Tela do game Agora que temos nossa classe Meteor, vamos alterar o protocolo da MeteorsEngine.h para que passe um objeto meteoro como par # import "Meteor.h" //... @protocol MeteorsEngineDelegate - (void)meteorsEngineDidCreateMeteor:(Meteor *)meteor; @end Na MeteorsEngine.m passaremos um novo meteoro como par delegate: 87 5.5. Tela do game Casa do C - (void)meteorsEngine:(float)dt { // sorte: 1 em 30 gera um novo meteoro! if(arc4random_uniform(30) == 0) { if ([self.delegate respondsToSelector: @selector(meteorsEngineDidCreateMeteor:)]){ // Pede para o delegate criar o meteoro [self.delegate meteorsEngineDidCreateMeteor: [Meteor meteorWithImage:kMETEOR]]; } } } Para fechar e criar o link entre a classe da tela do jogo, com a engine de meteoros e com os objetos meteoros criados, modificaremos a classe GameScene. Primeiramente, vamos criar um m de jogo. Crie o m - (void)addGameObjects { // Inicializa os Arrays self.meteorsArray = [NSMutableArray array]; // Inicializa a Engine de Meteoros self.meteorsEngine = [MeteorsEngine meteorEngine]; } No construtor, criaremos um layer especialmente para os meteoros e adiciona- remos a tela do jogo via addChild. Declare a propriedade meteorsLayer na GameScene.h: //... // Layers @property (nonatomic, retain) CCLayer *meteorsLayer; No init da GameScene.m criaremos a layer de meteoros e invocaremos o addGameObjetos. Ficar - (id)init { self = [super init]; 88 Casa do C Cap if (self) { // Imagem de Background CCSprite *background = [CCSprite spriteWithFile:kBACKGROUND]; background.position = ccp(SCREEN_WIDTH() / 2.0f, SCREEN_HEIGHT() / 2.0f); [self addChild:background]; // CCLayer para os Meteoros self.meteorsLayer = [CCLayer node]; [self addChild:self.meteorsLayer]; [self addGameObjects]; } return self; } E agora podemos alterar o m incluir o meteoro na tela e inici - (void)meteorsEngineDidCreateMeteor:(Meteor *)meteor { // A Engine de Meteoros indicou que um novo Meteoro foi criado [self.meteorsLayer addChild:meteor]; [meteor start]; [self.meteorsArray addObject:meteor]; } Um m Cocos2D assim que a tela do game est Ele ser meteorsEngine e indicaremos o delegate como self, para sermos avisados quando novos meteoros forem criados: //... - (void)onEnter { [super onEnter]; [self startGame]; } - (void)startGame 89 5.6. Conclus Casa do C { [self startEngines]; } - (void)startEngines { [self addChild:self.meteorsEngine]; self.meteorsEngine.delegate = self; } 5.6 Conclus Temos agora a tela de abertura e a tela de jogo (onde o game loop para jogar contra eles, 90 Casa do C Cap Figura 5.2: Meteoros inimigos. 91 Cap Criando o Player Tela do jogo preparada e inimigos aparecendo! Cen desenvolvimento do player! Essa de jogos, pois programaremos o objeto que ser Para isso, utilizaremos a maioria dos elementos do framework Cocos2D que vi- mos at Nossa tela de jogo precisar o Player e detectaremos inputs do usu Resumidamente, nesse cap • Colocar o player na tela • Movimentar o player • E atirar! 6.1. Desenhando o Player Casa do C Daremos um passo importante na constru final Figura 6.1: 14 bis atirando contra os meteoros. 6.1 Desenhando o Player Iniciaremos pela imagem, adicionando a figura do player no Assets.h. @interface Assets : NSObject # define kNAVE @"nave.png" @end 94 Casa do C Cap Criaremos o objeto principal e, como anteriormente, controlamos figuras e ima- gens herdando do CCSprite do Cocos2D. Utilizaremos o m Precisamos de vari mais Como j e player utilizando um delegate. Na Player.h iremos declarar nossas propriedades de posi construtor. @protocol PlayerDelegate; @interface Player : CCSprite @property (nonatomic, assign) iddelegate; @property (nonatomic, assign) float positionX; @property (nonatomic, assign) float positionY; + (Player *)player; @end @protocol PlayerDelegate // Vamos criar os m @end A Player.m ser # import "Player.h" @implementation Player + (Player *)player { Player *player = [Player spriteWithFile:kNAVE]; // Posiciona o Player rec player.positionX = SCREEN_WIDTH() / 2.0f; player.positionY = 120.0f; player.position = ccp(player.positionX, player.positionY); 95 6.1. Desenhando o Player Casa do C return player; } O objeto Player j camada na tela do jogo respons tela do jogo, temos que adicionar mais uma camada. Essa camada ter playerLayer. Na GameScene.h vamos declarar as propriedades da layer e do player, al informar que ela implementar # import "Player.h" //... @interface GameScene : CCLayer //... @property (nonatomic, retain) CCLayer *playerLayer; @property (nonatomic, retain) Player *player; Na GameScene.m trutor. Ap - (id)init { self = [super init]; if (self) { //... // CCLayer para o Jogador self.playerLayer = [CCLayer node]; [self addChild:self.playerLayer]; [self addGameObjects]; } return self; } - (void)addGameObjects { // Inicializa os Arrays self.meteorsArray = [NSMutableArray array]; 96 Casa do C Cap // Inicializa a Engine de Meteoros self.meteorsEngine = [MeteorsEngine meteorEngine]; // Cria o Player self.player = [Player player]; self.player.delegate = self; [self.playerLayer addChild:self.player]; } Figura 6.2: 14 bis pronto para a 97 6.2. Bot Casa do C 6.2 Bot J Vamos agora adicionar outros elementos mandado pelos inputs do usu esses controles. Iniciaremos adicionando 3 imagens, duas para movimentar o player entre di- reita e esquerda e outra que ser @interface Assets : NSObject # define kLEFTCONTROL @"left.png" # define kRIGHTCONTROL @"right.png" # define kSHOOTBUTTON @"shootButton.png" @end Para os bot Entretanto, os objetos do tipo CCMenuItemSprite s o usu menu, por usu Para isto, criaremos a classe CCMenuItemGameButton que ir alguns m sejam executadas logo quando o bot Crie a classe CCMenuItemGameButton, subclasse da CCMenuItemSprite. No arquivo header CCMenuItemGameButton.h n tera Vamos apenas alterar os m CCMenuItemGameButton.m: @implementation CCMenuItemGameButton - (void)selected { // Quando est // antes de o jogador tirar o dedo da tela [super activate]; } 98 Casa do C Cap - (void)activate { // N // acionado no m // [super activate]; } @end Pronto, temos nosso bot na tela alterando a classe GameScene. Vamos importar a classe de bot # import "CCMenuItemGameButton.h" //... @property (nonatomic, retain) CCLayer *gameButtonsLayer; No arquivo de implementa dos bot Perceba que os bot forma que fizemos na TitleScreen, mas desta vez utilizaremos nossa classe CCMenuItemGameButton: - (id)init { self = [super init]; if (self) { //... // CCLayer para os Bot self.gameButtonsLayer = [CCLayer node]; [self addChild:self.gameButtonsLayer]; // Cria os bot CCMenuItemGameButton *leftControl = [CCMenuItemGameButton itemWithNormalSprite:[CCSprite spriteWithFile:kLEFTCONTROL] selectedSprite:[CCSprite spriteWithFile:kLEFTCONTROL] target:self selector:@selector(moveLeft:)]; CCMenuItemGameButton *rightControl = [CCMenuItemGameButton itemWithNormalSprite:[CCSprite spriteWithFile:kRIGHTCONTROL] selectedSprite:[CCSprite spriteWithFile:kRIGHTCONTROL] target:self 99 6.2. Bot Casa do C selector:@selector(moveRight:)]; CCMenuItemGameButton *shootButton = [CCMenuItemGameButton itemWithNormalSprite:[CCSprite spriteWithFile:kSHOOTBUTTON] selectedSprite:[CCSprite spriteWithFile:kSHOOTBUTTON] target:self selector:@selector(shoot:)]; // Define as posi leftControl.position = ccp(-110.0f, (SCREEN_HEIGHT() / -2.0f) + 50.0f); rightControl.position = ccp(-50.0f, (SCREEN_HEIGHT() / -2.0f) + 50.0f); shootButton.position = ccp((SCREEN_WIDTH() / 2.0f) - 50.0f, (SCREEN_HEIGHT() / -2.0f) + 50.0f); // Cria o menu que ter CCMenu *menu = [CCMenu menuWithItems:leftControl, rightControl, shootButton, nil]; [self.gameButtonsLayer addChild:menu]; [self addGameObjects]; } return self; } Por fim, - (void)moveLeft:(id)sender { NSLog(@"Bot } - (void)moveRight:(id)sender { NSLog(@"Bot } - (void)shoot:(id)sender { 100 Casa do C Cap NSLog(@"Bot } Rode o jogo e aperte os bot toca, e n Figura 6.3: Controles de dire Imagens posicionadas e preparadas. Hora de come 6.3 Atirando Falaremos agora de uma parte do jogo que pode parecer simples a princ tem impacto em muitas outras partes para que funcione: o tiro! Para que a nave 101 6.3. Atirando Casa do C atire, precisaremos de uma s • Um novo asset, ou seja, imagem do tiro • Uma classe que represente o tiro como um CCSprite • Definir o posicionamento do tiro na tela • Uma camada na tela do jogo para os tiros • Associar o tiro e o player, para que o tiro saia do player H dessas depend nando a figura do tiro na classe Assets.h. @interface Assets : NSObject # define kSHOOTBUTTON @"shootButton.png" @end Podemos iniciar a programa ou algo assim, vamos tentar pensar em aparece na tela, precisamos moviment Para gerar essa sensa mento do tiro no eixo vertical, criaremos um m m par ser executado desde a A classe de tiro precisa manter o link com a tela de jogo, ent delegate. Criaremos tamb indicar que o tiro est Declare as propriedades e m 102 Casa do C Cap @interface Shoot : CCSprite @property (nonatomic, assign) float positionX; @property (nonatomic, assign) float positionY; + (Shoot *)shootWithPositionX:(float)positionX andPositionY:(float)positionY; - (void)start; @end Vamos implementar os m @implementation Shoot + (Shoot *)shootWithPositionX:(float)positionX andPositionY:(float)positionY { Shoot *shoot = [Shoot spriteWithFile:kSHOOT]; // Posiciona o Tiro rec shoot.positionX = positionX; shoot.positionY = positionY; shoot.position = ccp(shoot.positionX, shoot.positionY); return shoot; } - (void)start { // Inicia a Anima [self scheduleUpdate]; } - (void)update:(float)dt { // Move o Tiro para cima self.positionY += 2; self.position = ccp(self.positionX, self.positionY); } 103 6.3. Atirando Casa do C @end Tiro e tela de jogo At que guardar # import "Shoot.h" //... @property (nonatomic, retain) CCLayer *shootsLayer; @property (nonatomic, retain) NSMutableArray *shootsArray; Al mos a camada. - (id)init { self = [super init]; if (self) { //... // CCLayer para os Tiros self.shootsLayer = [CCLayer node]; [self addChild:self.shootsLayer]; } return self; } E no m - (void)addGameObjects { //... self.shootsArray = [NSMutableArray array]; //... } 104 Casa do C Cap Atirando! J Nesse momento, iremos definir o m que ele est # import "Shoot.h" //... @protocol PlayerDelegate - (void)playerDidCreateShoot:(Shoot *)shoot; @end Nesse momento, implementaremos a interface PlayerDelegate na GameScene. Dessa forma, a tela de jogo saber quando for requisitada para atirar. A interface obriga a cria playerDidCreateShoot:. Nele, um novo tiro, que classe Shoot, permitindo que ela controle o que for necess Primeiramente, implemente o m playerDidCreateShoot: na GameScene.m - (void)playerDidCreateShoot:(Shoot *)shoot { // O Player indicou que um novo Tiro foi criado [self.shootsLayer addChild:shoot]; [shoot start]; [self.shootsArray addObject:shoot]; } Criaremos na classe Player um m ros. Precisamos disso por um fato muito importante, que posicionamento da nave na hora do tiro. Declare o m - (void)shoot; Implemente o c 105 6.3. Atirando Casa do C - (void)shoot { // Aqui ser } Apertando o bot A GameScene.m seja, o momento em que o bot Vamos alterar o m m - (void)shoot:(id)sender { NSLog(@"Bot [self.player shoot]; } Player atirando Tudo pronto para atirar a partir do bot tiro definida, o bot que o pr para que capture o posicionamento da nave e chame seu delegate para cria Bang! - (void)shoot { // Atira if ([self.delegate respondsToSelector: @selector(playerDidCreateShoot:)]) { [self.delegate playerDidCreateShoot: [Shoot shootWithPositionX:self.positionX andPositionY:self.positionY]]; } } 106 Casa do C Cap Figura 6.4: Player atirando. 6.4 Movendo o player Vamos fechar esse cap atirar, moveremos o Player para esquerda e direita. Utilizaremos a mesma estrat Para iniciar, o Player deve saber se mover. Movimentar o player que sua posi Na classe Player adicionaremos dois novos m mudam a posi - (void)moveLeft; 107 6.4. Movendo o player Casa do C - (void)moveRight; Vamos implementar os m - (void)moveLeft { // Move o Player para a Esquerda if (self.positionX > 30.0f) { self.positionX -= 10.0f; } self.position = ccp(self.positionX, self.positionY); } - (void)moveRight { // Move o Player para a Direita if (self.positionX < SCREEN_WIDTH() - 30.0f) { self.positionX += 10.0f; } self.position = ccp(self.positionX, self.positionY); } Na GameScene.m alteraremos os m pelos bot - (void)moveLeft:(id)sender { NSLog(@"Bot [self.player moveLeft]; } - (void)moveRight:(id)sender { NSLog(@"Bot [self.player moveRight]; } Ao rodar o projeto, devemos ter a nave se movimentando a partir dos toques no comando de controle da nave. Al Player. 108 Casa do C Cap 6.5 Conclus Esse diversos elementos do framework Cocos2D, como camadas, sprites e agendamento (update), diversos conceitos de jogos foram usados, al O jogo deve estar como na figura abaixo. Figura 6.5: 14 bis atirando contra os meteoros. 109 Cap Detectando colis criando efeitos Colis o personagem principal capturando algum elemento, ou como no jogo que estamos programando, um tiro que atinge um meteoro. Detectar que um elemento encostou em outro complexo. Como mostrado no cap detectar colis No cap • Quando um tiro atinge um meteoro Veremos aqui mais uma vez que utilizar um framework de desenvolvimento de jogos como o Cocos2D ajuda muito nesse trabalho. 7.1. Detectando colis Casa do C Uma vez detectadas as colis uma anima tulo, falaremos sobre a atualiza Esse cap sendo um cap 7.1 Detectando colis A primeira coisa que precisamos para identificar a colis arrays, verificar se algum elemento de um array sobrep Na GameScene.h criaremos um NSMutableArray para o player que, inici- almente, ter @property (nonatomic, retain) NSMutableArray *playersArray; E adicionaremos esse player no array de objetos na GameScene.m. Mais pra frente voc - (void)addGameObjects { //... // Insere o Player no array de Players self.playersArray = [NSMutableArray array]; [self.playersArray addObject:self.player]; } Definindo as bordas Precisamos agora de uma forma de definir as bordas ou limites do tiro, da nave e dos meteoros, para que seja poss No prot Como estamos utilizando o Cocos2D e seus Sprites podemos mais facilmente conseguir essas informa Precisaremos de um m nha as bordas do elemento. Para isso, utilizaremos um m 112 Casa do C Cap prios Sprites chamado boundingBox. Esse m representa os contornos da figura mapeados em forma retangular. Com esse m analisado. Checando a colis Uma vez que j e posi Criaremos um m par ficados. Ou seja, se queremos checar se os tiros est tela de jogo, no caso a GameScene. Precisamos disso para poder executar algum m tectas, como nave com tiro ou tiro com meteoro, isso ser execu mais, que recebe o nome do m O que teremos ent cada elemento do segundo array. Caso a detec enquanto, e mostraremos quais elementos tiveram a colis Na GameScene.m teremos o c - (BOOL)checkRadiusHitsOfArray:(NSArray *)array1 againstArray:(NSArray *)array2 withSender:(id)sender andCallbackMethod:(SEL)callbackMethod { BOOL result = NO; for (int i = 0; i < [array1 count]; i++) { // Pega objeto do primeiro array CCSprite *obj1 = [array1 objectAtIndex:i]; CGRect rect1 = obj1.boundingBox; for (int j = 0; j < [array2 count]; j++) { // Pega objeto do segundo array CCSprite *obj2 = [array2 objectAtIndex:j]; 113 7.1. Detectando colis Casa do C CGRect rect2 = obj2.boundingBox; // Verifica colis if (CGRectIntersectsRect(rect1, rect2)) { NSLog(@"Colis NSStringFromSelector(callbackMethod)); result = YES; // Se encontrou uma colis i = [array1 count] + 1; j = [array2 count] + 1; } } } return result; } Tendo o m a verifica Ainda na GameScene.m faremos essas chamadas. - (void)checkHits:(float)dt { // Checa se houve colis [self checkRadiusHitsOfArray:self.meteorsArray againstArray:self.shootsArray withSender:self andCallbackMethod:@selector(meteorHit:withShoot:)]; // Checa se houve colis [self checkRadiusHitsOfArray:self.playersArray againstArray:self.meteorsArray withSender:self andCallbackMethod:@selector(playerHit:withMeteor:)]; } Como citado anteriormente, agendaremos o m 114 Casa do C Cap Para isso, o m - (void)startGame { // Ao entrar na GameScene, inicia a checagem de colis // e inicia as Engines do jogo [self schedule:@selector(checkHits:)]; [self startEngines]; } Rode o jogo e repare no console do Xcode. A colis portanto, podemos partir para a atualiza rios! 7.2 Efeitos No momento que detectamos que dois objetos do jogo colidiram, algumas coisas devem ser feitas. Vamos listar para facilitar o fluxo a seguir. • Remover os elementos dos arrays • Atualizar o placar ou mostrar tela de game over Vamos come utilizando o Cocos2D. O framework oferece alguns efeitos tradicionais como fade e scale. A ideia Para o primeiro exemplo, vamos animar o meteoro quando Nesse momento, animaremos o meteoro para que fique pequeno, dando a impress que sumiu por ser atingido. O que faremos abaixo • Reduzir a escala de tamanho da imagem • Retirar da tela com um efeito leve, chamado fade out 115 7.2. Efeitos Casa do C Ap e da mem desse objeto, que roda de tempos em tempos atualizando sua posi movimento. Teremos um novo m lis - (void)gotShot; Agora implemente o m - (void)gotShot { // Para o agendamento [self unscheduleUpdate]; // Cria efeitos float dt = 0.2f; CCScaleBy *a1 = [CCScaleBy actionWithDuration:dt scale:0.5f]; CCFadeOut *a2 = [CCFadeOut actionWithDuration:dt]; CCSpawn *s1 = [CCSpawn actionWithArray: [NSArray arrayWithObjects:a1, a2, nil]]; // M CCCallFunc *c1 = [CCCallFunc actionWithTarget:self selector:@selector(removeMe:)]; // Executa efeito [self runAction:[CCSequence actionWithArray: [NSArray arrayWithObjects:s1, c1, nil]]]; } No m mos as a O Cocos2D possui ainda um m seja liberado da mem ser queremos que o m - (void)removeMe:(id)sender { 116 Casa do C Cap // Quando o Meteoro [self removeFromParentAndCleanup:YES]; } Animando o tiro Utilizaremos o mesmo pensamento para animar o tiro quando colidir com um meteoro. Para isso, criaremos o m nas alguns par Esse m Vamos declarar o m - (void)explode; Agora implemente o m - (void)explode { // Para o agendamento [self unscheduleUpdate]; // Cria efeitos float dt = 0.2f; CCScaleBy *a1 = [CCScaleBy actionWithDuration:dt scale:2.0f]; CCFadeOut *a2 = [CCFadeOut actionWithDuration:dt]; CCSpawn *s1 = [CCSpawn actionWithArray: [NSArray arrayWithObjects:a1, a2, nil]]; // M CCCallFunc *c1 = [CCCallFunc actionWithTarget:self selector:@selector(removeMe:)]; // Executa efeito [self runAction:[CCSequence actionWithArray: [NSArray arrayWithObjects:s1, c1, nil]]]; } 117 7.2. Efeitos Casa do C Para limpar o objeto da mem cos2D chamado removeFromParentAndCleanup:. - (void)removeMe:(id)sender { // Quando o Tiro [self removeFromParentAndCleanup:YES]; } J lis Agora precisamos fazer a chamada a ele nos mo- mento de colis Voltando GameScene temos o m checkRadiusHitsOfArray:againstArray:withSender:andCallbackMethod:, que percorre dois arrays e verifica intersec Nesse m m lis No arquivo GameScene.m altere o m checkRadiusHitsOfArray:againstArray:withSender:andCallbackMethod:. //... // Verifica colis if (CGRectIntersectsRect(rect1, rect2)) { NSLog(@"Colis NSStringFromSelector(callbackMethod)); result = YES; // Se o sender possui o m // executa o mesmo com os objetos encontrados nos arrays if ([sender respondsToSelector:callbackMethod]) { [sender performSelector:callbackMethod withObject:[array1 objectAtIndex:i] withObject:[array2 objectAtIndex:j]]; } // Se encontrou uma colis 118 Casa do C Cap i = [array1 count] + 1; j = [array2 count] + 1; } A partir daqui, basta fazer as chamadas aos m GameScene.m adicione o m - (void)meteorHit:(id)meteor withShoot:(id)shoot { // Quando houve uma colis // o Meteoro foi atingido e que o Tiro deve explodir if ([meteor isKindOfClass:[Meteor class]]) { [(Meteor *)meteor gotShot]; } if ([shoot isKindOfClass:[Shoot class]]) { [(Shoot *)shoot explode]; } } Rode o projeto e teste o que fizemos at Removendo objetos Precisamos lembrar que embora os objetos n eles continuam alocados na mem remos agora o c Primeiramente, vamos criar um delegate para nossa classe Meteor para que ela avise quando dever @protocol MeteorDelegate; @interface Meteor : CCSprite @property (nonatomic, assign) float positionX; @property (nonatomic, assign) float positionY; @property (nonatomic, assign) iddelegate; + (Meteor *)meteorWithImage:(NSString *)image; 119 7.2. Efeitos Casa do C - (void)start; - (void)gotShot; @end @protocol MeteorDelegate - (void)meteorWillBeRemoved:(Meteor *)meteor; @end Altere o m ser - (void)gotShot { //... // Notifica delegate if ([self.delegate respondsToSelector: @selector(meteorWillBeRemoved:)]) { [self.delegate meteorWillBeRemoved:self]; } } Faremos a mesma coisa para a classe Shoot. Declare o protocolo e delegate na Shoot.h: @protocol ShootDelegate; @interface Shoot : CCSprite @property (nonatomic, assign) iddelegate; @property (nonatomic, assign) float positionX; @property (nonatomic, assign) float positionY; + (Shoot *)shootWithPositionX:(float)positionX andPositionY:(float)positionY; - (void)start; - (void)explode; @end 120 Casa do C Cap @protocol ShootDelegate - (void)shootWillBeRemoved:(Shoot *)shoot; @end Altere o m ser - (void)explode { //... // Notifica delegate if ([self.delegate respondsToSelector: @selector(shootWillBeRemoved:)]) { [self.delegate shootWillBeRemoved:self]; } } Agora, na nossa GameScene, toda vez que criarmos um novo Meteor ou Shoot, queremos possibilitar que eles avisem a pr r e do Shoot. Informe na GameScene.h de que ela implementar MeteorDelegate e ShootDelegate: @interface GameScene : CCLayer Agora altere os m playerDidCreateShoot: e meteorsEngineDidCreateMeteor: na GameScene.m para que ele defina os delegates: - (void)playerDidCreateShoot:(Shoot *)shoot { // O Player indicou que um novo Tiro foi criado [self.shootsLayer addChild:shoot]; shoot.delegate = self; [shoot start]; [self.shootsArray addObject:shoot]; } 121 7.3. Player morre Casa do C - (void)meteorsEngineDidCreateMeteor:(Meteor *)meteor { // A Engine de Meteoros indicou que um novo Meteoro foi criado [self.meteorsLayer addChild:meteor]; meteor.delegate = self; [meteor start]; [self.meteorsArray addObject:meteor]; } Feito isso, basta fazer a remo os m los: - (void)meteorWillBeRemoved:(Meteor *)meteor { // Ap // para que seja removido do Array meteor.delegate = nil; [self.meteorsArray removeObject:meteor]; } - (void)shootWillBeRemoved:(Shoot *)shoot { // Ap // para que seja removido do Array shoot.delegate = nil; [self.shootsArray removeObject:shoot]; } 7.3 Player morre Utilizando a mesma estrat com ele. Para isso, na classe Player teremos o m Declare-o na Player.h: - (void)explode; E o implemente na Player.m: - (void)explode { 122 Casa do C Cap // Para o agendamento [self unscheduleUpdate]; // Cria efeitos float dt = 0.2f; CCScaleBy *a1 = [CCScaleBy actionWithDuration:dt scale:2.0f]; CCFadeOut *a2 = [CCFadeOut actionWithDuration:dt]; CCSpawn *s1 = [CCSpawn actionWithArray: [NSArray arrayWithObjects:a1, a2, nil]]; // Executa efeito [self runAction:s1]; } Para que seja executado, crie o m playerHit:withMeteor: na GameScene.m. Repare que este camos as colis - (void)playerHit:(id)player withMeteor:(id)meteor { // Quando houve uma colis // indica que ambos foram atingidos if ([player isKindOfClass:[Player class]]) { [(Player *)player explode]; } if ([meteor isKindOfClass:[Meteor class]]) { [(Meteor *)meteor gotShot]; } } Rode o projeto e verifique a tela do jogo! 7.4 Placar Para fechar esse cap tru Primeiramente, trataremos o valor de pontos que aparece na tela como uma nova camada, e camadas para o Cocos2D s 123 7.4. Placar Casa do C Essa camada apresentar samos de duas vari A segunda Al zer isso. Existe no Cocos2D uma classe chamada CCLabelBMFont. Essa classe pos- sui um m uma fonte a ser usada. Depois disso, basta configurar o tamanho da letra e posicionamento. Alteraremos o valor do score com um m todo poder Crie a classe Score e defina as propriedades e m @interface Score : CCLayer @property (nonatomic, assign) int score; @property (nonatomic, retain) CCLabelBMFont *text; + (Score *)score; - (void)increase; @end No init da Score.m crie o label de texto: + (Score *)score { return [[[Score alloc] init] autorelease]; } - (id)init { self = [super init]; if (self) { // Inicializa a pontua self.score = 0; // Posiciona o Placar rec self.position = ccp(SCREEN_WIDTH() - 50.0f, SCREEN_HEIGHT() - 50.0f); 124 Casa do C Cap // Adiciona o Player na tela, como um texto para o jogador self.text = [CCLabelBMFont labelWithString: [NSString stringWithFormat:@"%d", self.score] fntFile:@"UniSansSemiBold_Numbers_240.fnt"]; self.text.scale = (float)(240.0f / 240.0f); [self addChild:self.text]; } return self; } Implemente tamb menta a vari - (void)increase { // Aumenta a pontua self.score++; self.text.string = [NSString stringWithFormat:@"%d", self.score]; } Agora que temos a camada do placar preparada, vamos adicion pal. Para isso, criaremos dois objetos: um do tipo CCLayer e outro do tipo Score. Na GameScene.h adicione: # import "Score.h" //... @property (nonatomic, retain) CCLayer *scoreLayer; @property (nonatomic, retain) Score *score; adicione: - (id)init { self = [super init]; if (self) { //... // CCLayer para o Placar self.scoreLayer = [CCLayer node]; [self addChild:self.scoreLayer]; } 125 7.5. Conclus Casa do C return self; } Para finalizar, basta criar o objeto do tipo Score e adicionar a camada corres- pondente. Ainda na classe GameScene adicione essa chamada ao m addGameObjects: - (void)addGameObjects { //... // Cria o Placar self.score = [Score score]; [self.scoreLayer addChild:self.score]; } Agora altere o m quando houver colis - (void)meteorHit:(id)meteor withShoot:(id)shoot { //... // Aumenta a pontua [self.score increase]; } 7.5 Conclus Detectar colis como o Cocos2D as coisas s Nesse cap detec O pr jogos, os sons e efeitos. 126 Cap Adicionando sons e m Os sons s existem profiss sons dos games. Muitos jogos utilizam orquestras para executar sua trilha sonora. A m as partes importantes. Existem duas formas principais de sons no mundo dos games: m Quando o jogo come Essa m longo do game. Al tantes, como quando uma colis Para o nosso jogo, utilizaremos sons encontrados gratuitamente no site http:// www.freesound.org/. Voc 8.1. Executando sons Casa do C 8.1 Executando sons Nessa primeira etapa utilizaremos o framework Cocos2D para adicionar som a 3 eventos do jogo. Utilizaremos 3 arquivos de sons diferentes: • Disparo de um tiro Podemos utilizar os formatos mais comuns para adicionar sons ao nosso jogo, e aqui o formato escolhido ser Resources/Sounds do nosso projeto. Voc SoundEngine Para lidar com sons, o Cocos2D disponibiliza uma classe chamada SimpleAudioEngine, que possui diversos m lhar com sons e m uma inst Para isso executamos [SimpleAudioEngine sharedEngine] tendo acesso op Com esse acesso, podemos executar m 3 sons utilizando o m nome do arquivo de Para facilitar, importaremos a classe SimpleAudioEngine no arquivo Prefix.pch: # ifdef __OBJC__ //... # import "SimpleAudioEngine.h" # endif O primeiro efeito de som, o tiro, ser c 128 Casa do C Cap - (void)start { //... // Som do Tiro [[SimpleAudioEngine sharedEngine] playEffect:@"shoot.wav"]; } O pr No arquivo Meteor.m adicione o c - (void)gotShot { // Som ao ser atingido [[SimpleAudioEngine sharedEngine] playEffect:@"bang.wav"]; //... } Para finalizar, adicionaremos um som quando um meteoro atingir o avi arquivo Player.m adicione o c - (void)explode { // Som ao explodir [[SimpleAudioEngine sharedEngine] playEffect:@"over.wav"]; //... } Agora o game j s 8.2 Cache de sons Vimos como executar sons utilizando o Cocos2D. Por um problema grave: a primeira vez que um dos sons precisa ser tocado, ele demora muito. Isso se deve ao fato do framework inicializar o som apenas no momento que foi necess Para colocar um som no cache devemos inici remos ent preloadCache. Esse m aqui. 129 8.3. M Casa do C - (void)preloadCache { // Cache de sons do Jogo [[SimpleAudioEngine sharedEngine] preloadEffect:@"shoot.wav"]; [[SimpleAudioEngine sharedEngine] preloadEffect:@"bang.wav"]; [[SimpleAudioEngine sharedEngine] preloadEffect:@"over.wav"]; } Adicione a chamada do m preloadCache no construtor da GameScene.m, na - (id)init { self = [super init]; if (self) { //... // Cache de sons do Jogo [self preloadCache]; } return self; } Rode novamente o projeto e verifique que agora os sons respondem perfeita- mente ao momento dos tiros e colis 8.3 M Agora que j m Para isso, usaremos a mesma classe SimpleAudioEngine por playBackgroundMusic:loop:. No construtor da GameScene.m adicione a chamada a m - (id)init { self = [super init]; if (self) { //... // M [[SimpleAudioEngine sharedEngine] playBackgroundMusic:@"music.wav" loop:YES]; 130 Casa do C Cap } return self; } Outro m pauseBackgroundMusic. Faremos isso logo ap teoro e avi - (void)explode { //... // Pausa a m [[SimpleAudioEngine sharedEngine] pauseBackgroundMusic]; } Rode o projeto e veja como o som adiciona vida ao jogo. Tiro, colis devem estar funcionando nesse momento! 8.4 Conclus Sons e m mesmo os que voc possu game! 131 Cap Voando com a gravidade! O mundo dos games foi totalmente revitalizado com os smartphones. Jogos que at remodelados com as novas possibilidades de experi podem proporcionar. A maioria dos jogos que fazem sucesso nos celulares hoje faz uso de algum re- curso do aparelho, como touch screen, arrastando objetos na tela ou utilizando a gravidade com o aceler diferente dos jogos para consoles. Nesse cap trole baseado na movimenta do aparelho que faz com que o game tenha uma melhor jogabilidade! Para este cap aceler cap 9.1. Usando o Aceler Casa do C Figura 9.1: Controle por aceler 9.1 Usando o Aceler Vamos iniciar planejando o uso do aceler Nosso objetivo Sem esses bot 134 Casa do C Cap Capturando as coordenadas A primeira coisa que precisamos saber ma mework CoreMotion, pr Sempre que iniciamos a CMMotionManager, ela come a cada movimenta no m bloco do handler possui dois objetos, sendo um deles um CMAccelerometerData, que em nosso c Essa deste bloco pelo CMMotionManager, o objeto accelerometerData com os novos valores de posi vari Figura 9.2: Eixos X, Y e Z. De posse dessas informa um determinado lado e atualizar a posi tante, ou seja, quando percebemos que o celular est 135 9.1. Usando o Aceler Casa do C A classe Accelerometer Vamos criar uma classe respons capturar seus valores. Mais Para come jeto. Para importar um framework, no menu depois clique no target bis. Na aba Build Phases, em Link Binary With Libraries clique no bot Pronto! temos o framework importado em nosso projeto. Agora criaremos a classe Accelerometer bem simples, como subclasse da NSObject. Nesta classe, vamos instanciar o CMMotionManager e criar m para iniciar e parar o mesmo. Tamb do aparelho. Para isso, criaremos 2 propriedades que v # import @interface Accelerometer : NSObject @property (nonatomic, retain) CMMotionManager *motionManager; - (void)startAccelerometerUpdates; - (void)stopAccelerometerUpdates; @property (nonatomic, assign) float currentAccelerationX; @property (nonatomic, assign) float currentAccelerationY; @end Na Accelerometer.m vamos instanciar a CMMotionManager no init e criar os m @implementation Accelerometer - (id)init { self = [super init]; if (self) { self.motionManager = 136 Casa do C Cap [[[CMMotionManager alloc] init] autorelease]; self.motionManager.accelerometerUpdateInterval = 0.01f; // 100Hz } return self; } - (void)startAccelerometerUpdates { // Verifica se o dispositivo possui acesso ao Aceler if ([self.motionManager isAccelerometerAvailable]) { // Zera as propriedades self.currentAccelerationX = 0.0f; self.currentAccelerationY = 0.0f; // Inicia atualiza [self.motionManager startAccelerometerUpdatesToQueue:[NSOperationQueue mainQueue] withHandler:^(CMAccelerometerData *accelerometerData, NSError *error) { self.currentAccelerationX = accelerometerData.acceleration.x; self.currentAccelerationY = accelerometerData.acceleration.y; }]; } } - (void)stopAccelerometerUpdates { // Quando os dados do Aceler // deve-se parar as atualiza if ([self.motionManager isAccelerometerActive]) { [self.motionManager stopAccelerometerUpdates]; } } @end O que temos at do aparelho. Ela n 137 9.1. Usando o Aceler Casa do C Para continuar, faremos que essa classe se comporte como um Singleton, tendo apenas um objeto do tipo Accelerometer no jogo. Para isso, no arquivo Accelerometer.h declare um m classe: + (Accelerometer *)sharedAccelerometer; No arquivo Accelerometer.m adicione o seguinte c static Accelerometer *sharedAccelerometer = nil; + (Accelerometer *)sharedAccelerometer { if (!sharedAccelerometer) { sharedAccelerometer = [[Accelerometer alloc] init]; } return sharedAccelerometer; } Dessa forma, n ado no jogo. Agora, j Configurando o player Toda vez que o aceler ocorrer de movimento. Como em outras classes, iremos agendar o m que o player busque as acelera posi Criaremos o m de aceler # import "Accelerometer.h" //... - (void)monitorAccelerometer; Na Player.m implemente este m - (void)monitorAccelerometer { // Inicia a Atualiza [[Accelerometer sharedAccelerometer] startAccelerometerUpdates]; 138 Casa do C Cap // Inicia a Anima [self scheduleUpdate]; } - (void)update:(float)dt { NSLog(@"X: %f", [[Accelerometer sharedAccelerometer] currentAccelerationX]); NSLog(@"Y: %f", [[Accelerometer sharedAccelerometer] currentAccelerationY]); } Agora vamos alterar o m ao player para monitorar o aceler - (void)startGame { //... // Ao entrar na GameScene, inicia o monitoramento do Aceler [self.player monitorAccelerometer]; } Voc sendo impressas. O que temos neste momento? A classe Accelerometer que recebe coordena- das do aparelho e a classe Player preparada para ler esses valores. Movendo o player Agora que j j ent A ideia aqui ser rizontal (direita ou esquerda) ou vertical (cima ou baixo). As coordenadas enviadas pelo aceler um lado sejam n ocorre para o eixo X, enviando n 139 9.1. Usando o Aceler Casa do C Com base nessa informa enviou e alteraremos a posi Na classe Player altere o m - (void)update:(float)dt { if ([[Accelerometer sharedAccelerometer] currentAccelerationX] < -0.0f) { self.positionX--; } if ([[Accelerometer sharedAccelerometer] currentAccelerationX] > 0.0f) { self.positionX++; } if ([[Accelerometer sharedAccelerometer] currentAccelerationY] < -0.0f) { self.positionY--; } if ([[Accelerometer sharedAccelerometer] currentAccelerationY] > 0.0f) { self.positionY++; } // Checa limites da tela if (self.positionX < 30.0f) { self.positionX = 30.0f; } if (self.positionX > SCREEN_WIDTH() - 30.0f) { self.positionX = SCREEN_WIDTH() - 30.0f; } if (self.positionY < 30.0f) { self.positionY = 30.0f; } if (self.positionY > SCREEN_HEIGHT() - 30.0f) { self.positionY = SCREEN_HEIGHT() - 30.0f; } // Configura posi self.position = ccp(self.positionX, self.positionY); } 140 Casa do C Cap J aparelho! Fa Experimente deixar a tela paralela a uma mesa. Algo estranho, n ele est 9.2 Controlando a instabilidade Voc ler gerando eventos! Para melhorar isso, vamos usar um limite. Em vez de comparar com zero, utili- zaremos uma constante de toler definir Crie a constante e altere o m # define kNOISE 0.15f //... - (void)update:(float)dt { if ([[Accelerometer sharedAccelerometer] currentAccelerationX] < -kNOISE) { self.positionX--; } if ([[Accelerometer sharedAccelerometer] currentAccelerationX] > kNOISE) { self.positionX++; } if ([[Accelerometer sharedAccelerometer] currentAccelerationY] < -kNOISE) { self.positionY--; } if ([[Accelerometer sharedAccelerometer] currentAccelerationY] > kNOISE) { self.positionY++; } //... } 141 9.3. Calibrando a partir da posi Casa do C A partir deste momento, devemos ter um bom controle do avi moviment bra 9.3 Calibrando a partir da posi relho Vamos utilizar uma estrat la como a posi Para isso faremos algumas altera novas propriedades que ser Na Accelerometer.h crie as seguintes propriedades: @property (nonatomic, assign) int calibrated; @property (nonatomic, assign) float calibratedAccelerationX; @property (nonatomic, assign) float calibratedAccelerationY; Alteraremos tamb startAccelerometerUpdates na Accelerometer.m. Nele, calibraremos o aceler meiras informa a posi A partir disso, faremos uma altera Ao inv # define kCALIBRATIONCOUNT 30 - (void)startAccelerometerUpdates { // Verifica se o dispositivo possui acesso ao Aceler if ([self.motionManager isAccelerometerAvailable]) { // Zera as propriedades self.currentAccelerationX = 0.0f; 142 Casa do C Cap self.currentAccelerationY = 0.0f; self.calibrated = 0; self.calibratedAccelerationX = 0.0f; self.calibratedAccelerationY = 0.0f; // Inicia atualiza [self.motionManager startAccelerometerUpdatesToQueue:[NSOperationQueue mainQueue] withHandler:^(CMAccelerometerData *accelerometerData, NSError *error) { if (self.calibrated < kCALIBRATIONCOUNT) { // Soma posi self.calibratedAccelerationX += accelerometerData.acceleration.x; self.calibratedAccelerationY += accelerometerData.acceleration.y; self.calibrated++; if (self.calibrated == kCALIBRATIONCOUNT) { // Calcula a m self.calibratedAccelerationX /= kCALIBRATIONCOUNT; self.calibratedAccelerationY /= kCALIBRATIONCOUNT; } } else { // Atualiza acelera self.currentAccelerationX = accelerometerData.acceleration.x - self.calibratedAccelerationX; self.currentAccelerationY = accelerometerData.acceleration.y - self.calibratedAccelerationY; } }]; } } Por que 30 vezes? O jogador acabou de apertar o bot melhor posi ler 143 9.4. Desafios com o aceler Casa do C Voc depois apag vimenta Retirando os bot Provavelmente voc querda e direita. Uma forma simples de remov bot // CCMenu *menu = [CCMenu menuWithItems:leftControl, // rightControl, // shootButton, // nil]; CCMenu *menu = [CCMenu menuWithItems:shootButton, nil]; 9.4 Desafios com o aceler Utilizar recursos como aceler gajador, sendo um daqueles detalhes que fazem a experi deslize mais. Se ele estiver pouco inclinado, em vez de incrementar a vari x em 1, voc pelo aceler Dependendo da calibra n 9.5 Conclus Esse muito mais interessante! 144 Casa do C Cap Figura 9.3: Controle por aceler 145 Cap Tela final e game over Todo o fluxo do jogo est pelas colis A primeira tela a ser criada ser quando o 14bis vencer os 100 meteoros. Essa tela ser de inicio do jogo. Ao final, deveremos ter a tela como abaixo. 10.1. Tela final Casa do C Figura 10.1: Tela do final do jogo. 10.1 Tela final Para montar a tela de final de jogo precisaremos de mais uma imagem, a finalend.png (e sua vers para o projeto e inclua a defini # define kFINALEND @"finalend.png" Vamos criar a classe que representar e seu nome ser Como toda scene, declare no FinalScreen.h o construtor: 148 Casa do C Cap @interface FinalScreen : CCLayer + (CCScene *)scene; @end Na FinalScreen.m implementaremos o m veitaremos o init para inicializar os objetos background, som e imagem de logo. Faremos isso como j - (id)init { self = [super init]; if (self) { // Imagem de Background CCSprite *background = [CCSprite spriteWithFile:kBACKGROUND]; background.position = ccp(SCREEN_WIDTH() / 2.0f, SCREEN_HEIGHT() / 2.0f); [self addChild:background]; // Som [[SimpleAudioEngine sharedEngine] playEffect:@"finalend.wav"]; // Imagem CCSprite *title = [CCSprite spriteWithFile:kFINALEND]; title.position = ccp(SCREEN_WIDTH() / 2.0f, SCREEN_HEIGHT() - 130.0f); [self addChild:title]; } return self; } Configuraremos, ainda no init, o bot //... // Cria o bot CCMenuItemSprite *beginButton = [CCMenuItemSprite itemWithNormalSprite:[CCSprite spriteWithFile:kPLAY] selectedSprite:[CCSprite spriteWithFile:kPLAY] target:self selector:@selector(restartGame:)]; 149 10.1. Tela final Casa do C // Define a posi beginButton.position = ccp(0.0f, 0.0f); // Cria o menu que ter CCMenu *menu = [CCMenu menuWithItems:beginButton, nil]; [self addChild:menu]; Quando o jogador selecionar o bot # import "TitleScreen.h" Voltando quando selecionado, para que retorne mentar este m - (void)restartGame:(id)sender { // Pausa a m [[SimpleAudioEngine sharedEngine] pauseBackgroundMusic]; // Transfere o Jogador para a TitleScreen [[CCDirector sharedDirector] replaceScene: [CCTransitionFade transitionWithDuration:1.0 scene:[TitleScreen scene]]]; } Para que essa classe possa ser vista no jogo, a classe GameScene deve estar ciente e inicializ # import "FinalScreen.h" Agora na GameScene.m teremos o m transi - (void)startFinalScreen { // Transfere o Jogador para a FinalScreen [[CCDirector sharedDirector] replaceScene: [CCTransitionFade transitionWithDuration:1.0 scene:[FinalScreen scene]]]; } 150 Casa do C Cap E quando teremos a tela de final do jogo? Faremos isso quando 100 meteoros forem destru meteoros forem destru Ainda na GameScene.m, no m a chamada da tela final ap - (void)meteorHit:(id)meteor withShoot:(id)shoot { //... // Verifica o m if (self.score.score >= 5) { [self startFinalScreen]; } } Ao destruir 5 meteoros j 151 10.2. Tela Game Over Casa do C Figura 10.2: Tela do final do jogo. 10.2 Tela Game Over A tela de game over seguir alizada em um outro momento. Quando? Simples, quando um meteoro atingir o avi Vamos ao c HD gameover-hd.png) e inclua a defini # define kGAMEOVER @"gameover.png" Crie a classe GameOverScreen que a mesma ideia da tela de final de jogo. A GameOverScreen.h ficar 152 Casa do C Cap # import "TitleScreen.h" @interface GameOverScreen : CCLayer + (CCScene *)scene; @end O init da GameOverScreen.m tamb j + (CCScene *)scene { // 'scene' is an autorelease object. CCScene *scene = [CCScene node]; // 'layer' is an autorelease object. GameOverScreen *layer = [GameOverScreen node]; // add layer as a child to scene [scene addChild:layer]; // return the scene return scene; } - (id)init { self = [super init]; if (self) { // Imagem de Background CCSprite *background = [CCSprite spriteWithFile:kBACKGROUND]; background.position = ccp(SCREEN_WIDTH() / 2.0f, SCREEN_HEIGHT() / 2.0f); [self addChild:background]; // Som [[SimpleAudioEngine sharedEngine] playEffect:@"finalend.wav"]; // Imagem CCSprite *title = [CCSprite spriteWithFile:kGAMEOVER]; title.position = 153 10.2. Tela Game Over Casa do C ccp(SCREEN_WIDTH() / 2.0f, SCREEN_HEIGHT() - 130.0f); [self addChild:title]; // Cria o bot CCMenuItemSprite *beginButton = [CCMenuItemSprite itemWithNormalSprite:[CCSprite spriteWithFile:kPLAY] selectedSprite:[CCSprite spriteWithFile:kPLAY] target:self selector:@selector(restartGame:)]; // Define a posi beginButton.position = ccp(0.0f, 0.0f); // Cria o menu que ter CCMenu *menu = [CCMenu menuWithItems:beginButton, nil]; [self addChild:menu]; } return self; } Para que o bot restartGame:. - (void)restartGame:(id)sender { // Pausa a m [[SimpleAudioEngine sharedEngine] pauseBackgroundMusic]; // Transfere o Jogador para a TitleScreen [[CCDirector sharedDirector] replaceScene: [CCTransitionFade transitionWithDuration:1.0 scene:[TitleScreen scene]]]; } A chamada a essa tela deve ser feita quando a colis detectada. Para isso, importe a GameOverScreen na GameScene.h: # import "GameOverScreen.h" Agora adicione a transi playerHit:withMeteor: da GameScene.m: 154 Casa do C Cap - (void)playerHit:(id)player withMeteor:(id)meteor { //... // Ao ser atingido, o Jogador [[CCDirector sharedDirector] replaceScene: [CCTransitionFade transitionWithDuration:1.0 scene:[GameOverScreen scene]]]; } A tela de game over deve estar aparecendo quando o meteoro colide com o avi como mostrado a seguir. Figura 10.3: Tela de game over. 155 10.3. Conclus Casa do C 10.3 Conclus Esse 156 Cap Pausando o jogo Nesse cap pause. Essa tela n Teremos muitos conceitos importantes nesse momento. Para n ter. • Construir uma nova camada para a tela de pause • Fazer o link entre a tela de pause e tela de jogo • Parar realmente os objetos na tela 11.1. Montando a tela de pause Casa do C Veja como a tela de pause pode enganar. S que tudo funcione bem. Repare que, sempre que poss na tela pode ajudar a ver com mais profundidade o trabalho que ser desenvolver. Ao final desse cap Figura 11.1: Tela de pause. 11.1 Montando a tela de pause Vamos come pause 158 Casa do C Cap de cena ( CCScene). Como anteriormente, toda nova camada no Cocos2D pode ser representada pela classe CCLayer. Uma parte importante dessa tela Criaremos os dois bot do game, a CCMenuItemSprite. Crie a classe PauseScreen, subclasse da CCLayer, com o header abaixo: @interface PauseScreen : CCLayer + (PauseScreen *)pauseScreen; @end A pr • Colocar o logo, como na tela de abertura Importe a imagem exit.png (e sua vers e inclua a defini # define kEXIT @"exit.png" Agora, na PauseScreen.m, implemente o m os bot + (PauseScreen *)pauseScreen { return [[[PauseScreen alloc] init] autorelease]; } - (id)init { self = [super init]; if (self) { 159 11.1. Montando a tela de pause Casa do C // Cor de Background CCLayerColor *background = [CCLayerColor layerWithColor:ccc4(0, 0, 0, 175) width:SCREEN_WIDTH() height:SCREEN_HEIGHT()]; [self addChild:background]; // Imagem de Logo CCSprite *title = [CCSprite spriteWithFile:kLOGO]; title.position = ccp(SCREEN_WIDTH() / 2.0f, SCREEN_HEIGHT() - 130.0f); [self addChild:title]; // Cria os bot CCMenuItemSprite *resumeButton = [CCMenuItemSprite itemWithNormalSprite:[CCSprite spriteWithFile:kPLAY] selectedSprite:[CCSprite spriteWithFile:kPLAY] target:self selector:@selector(resumeGame:)]; CCMenuItemSprite *quitButton = [CCMenuItemSprite itemWithNormalSprite:[CCSprite spriteWithFile:kEXIT] selectedSprite:[CCSprite spriteWithFile:kEXIT] target:self selector:@selector(quitGame:)]; // Define as posi resumeButton.position = ccp(0.0f, 0.0f); quitButton.position = ccp(0.0f, -100.0f); // Cria o menu que ter CCMenu *menu = [CCMenu menuWithItems:resumeButton, quitButton, nil]; [self addChild:menu]; } return self; } Nesse momento temos a tela de pause, por que criar uma tela de pause n 160 Casa do C Cap 11.2 Controlando o Game Loop Lembra da analogia de desenhos em blocos de papel do cap Falamos que um jogo pode ser comparado a uma sequ desenhadas a cada mudan press O que isso importa para a tela de pause? Precisamos ter um controle do que o jogo est momento. Com uma vari a tela de pause, se devemos paralisar os objetos na tela, retomar os movimentos dos objetos etc. Faremos esse controle com uma classe que ter Crie a classe Runner, subclasse da NSObject, e defina a vari seu header Runner.h: @interface Runner : NSObject @property (nonatomic, assign, getter = isGamePaused) BOOL gamePaused; + (Runner *)sharedRunner; @end Essa classe ter cia ativa no projeto, para que n Para isso, defina o m inst + (Runner *)sharedRunner; Na Runner.m implemente este m static Runner *sharedRunner = nil; + (Runner *)sharedRunner { if (!sharedRunner) { sharedRunner = [[Runner alloc] init]; 161 11.3. Adicionando o bot Casa do C } return sharedRunner; } Sempre que precisarmos fazer a verifica m Caso ainda n Com a classe Runner sendo um Singleton, podemos a qualquer momento acessar a propriedade gamePaused para saber o estado do game. Essa classe pode parecer simples. Um Singleton com uma Por v Como utilizaremos o Runner em diversas classes, importe-o na Prefix.pch: # import "Runner.h" 11.3 Adicionando o bot A pr # define kPAUSE @"pause.png" Adicionaremos esse novo bot criar uma nova vari bot Vamos criar o bot - (id)init { self = [super init]; if (self) { //... // Cria os bot //... CCMenuItemGameButton *pauseButton = [CCMenuItemGameButton itemWithNormalSprite:[CCSprite spriteWithFile:kPAUSE] selectedSprite:[CCSprite spriteWithFile:kPAUSE] 162 Casa do C Cap target:self selector:@selector(pauseGame:)]; // Define as posi //... pauseButton.position = ccp(-120.0f, (SCREEN_HEIGHT() / 2.0f) - 30.0f); // Cria o menu que ter //... CCMenu *menu = [CCMenu menuWithItems:shootButton, pauseButton, nil]; [self.gameButtonsLayer addChild:menu]; } return self; } Para fechar, vamos criar o m pause for tocado na GameScene.m: - (void)pauseGame:(id)sender { NSLog(@"Bot } 11.4 A interface entre jogo e pause De fato uma tela de pause n lando, nesse cap essas e envolvem diversas classes. Tendo a classe da tela de pause e os bot quando ele deve mostr 11.5 Pausando o jogo Come 163 11.5. Pausando o jogo Casa do C receber Para que a tela de pause possa ser vista, precisaremos adicionar uma nova ca- mada na GameScene. Criaremos uma camada do tipo CCLayer e um objeto do tipo PauseScreen. Na GameScene.h crie as propriedades: # import "PauseScreen.h" //... @property (nonatomic, retain) CCLayer *topLayer; @property (nonatomic, retain) PauseScreen *pauseScreen; No init da GameScene.m iniciaremos a camada e vamos adicionar a mesma - (id)init { self = [super init]; if (self) { // CCLayer para o exibi self.topLayer = [CCLayer node]; [self addChild:self.topLayer]; } } Feito isso, uma nova camada existe na tela de jogo, mas ainda sem rela queremos com a tela de pause. M Vamos seguir fazendo esse link. Faremos a tela de jogo saber que alguns m de pause s Vamos implementar cada um dos 3 m pauseGame:, j Na GameScene.m altere o m - (void)pauseGame:(id)sender { NSLog(@"Bot 164 Casa do C Cap if ([Runner sharedRunner].isGamePaused == NO) { [Runner sharedRunner].gamePaused = YES; } } Agora vamos fazer a tela de pause avisar a tela de jogo quando o bot ou quit foi selecionado. Faremos isto atrav Na PauseScreen.h declare o protocolo PauseScreenDelegate e a propri- edade delegate: @protocol PauseScreenDelegate; @interface PauseScreen : CCLayer @property (nonatomic, assign) iddelegate; + (PauseScreen *)pauseScreen; @end @protocol PauseScreenDelegate - (void)pauseScreenWillResumeGame:(PauseScreen *)pauseScreen; - (void)pauseScreenWillQuitGame:(PauseScreen *)pauseScreen; @end Vamos informar que nossa GameScene implementar da tela de pause, alterando o arquivo GameScene.h: @interface GameScene : CCLayer O pr meiro, o m guramos o pause para NO. Adicione o m na GameScreen.m: 165 11.5. Pausando o jogo Casa do C - (void)pauseScreenWillResumeGame:(PauseScreen *)pauseScreen { if ([Runner sharedRunner].isGamePaused == YES) { // Continua o jogo self.pauseScreen.delegate = nil; self.pauseScreen = nil; [Runner sharedRunner].gamePaused = NO; } } Para fechar, implementaremos o m mais simples entre os 3. Nele, vamos parar os sons que estamos tocando. Al - (void)pauseScreenWillQuitGame:(PauseScreen *)pauseScreen { [SimpleAudioEngine sharedEngine].effectsVolume = 0.0f; [SimpleAudioEngine sharedEngine].backgroundMusicVolume = 0.0f; // Transfere o Jogador para a TitleScreen [[CCDirector sharedDirector] replaceScene: [CCTransitionFade transitionWithDuration:1.0 scene:[TitleScreen scene]]]; } Iniciando tudo Precisamos configurar a vari No m pause abaixo: - (void)startGame { // Configura o status do jogo [Runner sharedRunner].gamePaused = NO; //... } Ajustaremos agora o m acionado pelo bot GameScreen.m: 166 Casa do C Cap - (void)pauseGame:(id)sender { NSLog(@"Bot if ([Runner sharedRunner].isGamePaused == NO) { [Runner sharedRunner].gamePaused = YES; } if ([Runner sharedRunner].isGamePaused == YES && self.pauseScreen == nil) { self.pauseScreen = [PauseScreen pauseScreen]; self.pauseScreen.delegate = self; [self.topLayer addChild:self.pauseScreen]; } } Agora que j delegate: - (void)resumeGame:(id)sender { if ([self.delegate respondsToSelector: @selector(pauseScreenWillResumeGame:)]) { [self.delegate pauseScreenWillResumeGame:self]; [self removeFromParentAndCleanup:YES]; } } - (void)quitGame:(id)sender { if ([self.delegate respondsToSelector: @selector(pauseScreenWillQuitGame:)]) { [self.delegate pauseScreenWillQuitGame:self]; } } Ao rodar o projeto, a tela de pause deve aparecer mas ainda temos trabalho a fazer. 167 11.6. Pausando os objetos Casa do C 11.6 Pausando os objetos Temos toda a arquitetura preparada para o pause, mas algo muito importante ainda n Precisamos usar a classe Runner que controla o estado do game para isso. Ser atrav A l tela de jogo se o jogo n elementos se essa condi Na MeteorsEngine.m adicione a verifica - (void)meteorsEngine:(float)dt { // Checa se o jogo est if ([Runner sharedRunner].isGamePaused == NO) { // Sorte: 1 em 30 gera um novo meteoro! if(arc4random_uniform(30) == 0) { if ([self.delegate respondsToSelector: @selector(meteorsEngineDidCreateMeteor:)]) { [self.delegate meteorsEngineDidCreateMeteor: [Meteor meteorWithImage:kMETEOR]]; } } } } Na Meteor.m adicione a verifica - (void)update:(float)dt { // Checa se o jogo est if ([Runner sharedRunner].isGamePaused == NO) { // Move o Meteoro para baixo self.positionY -= 1.0f; self.position = ccp(self.positionX, self.positionY); } } Na Shoot.m adicione a verifica 168 Casa do C Cap - (void)update:(float)dt { // Checa se o jogo est if ([Runner sharedRunner].isGamePaused == NO) { // Move o Tiro para cima self.positionY += 2; self.position = ccp(self.positionX, self.positionY); } } Na classe Player adicione as verifica - (void)update:(float)dt { // Checa se o jogo est if ([Runner sharedRunner].isGamePaused == NO) { //... Demais c } } - (void)moveLeft { // Checa se o jogo est if ([Runner sharedRunner].isGamePaused == NO) { //... Demais c } } - (void)moveRight { // Checa se o jogo est if ([Runner sharedRunner].isGamePaused == NO) { //... Demais c } } - (void)shoot { // Checa se o jogo est if ([Runner sharedRunner].isGamePaused == NO) { //... Demais c 169 11.7. Conclus Casa do C } } Muito trabalho, n ponto chave no desenvolvimento dos games. Rode o projeto e verifique o compor- tamento dessa nova tela no jogo. 11.7 Conclus Fazer uma tela de pause de lado na constru menta A not para desenvolver um jogo! Imagens, loop, camadas, sons, colis 170 Casa do C Cap Figura 11.2: Tela de pause. 171 Cap Continuando nosso jogo Depois de saber como criar a base de um jogo e sua estrutura, montar os cen para milhares de novos jogos. Esse cap e, quem sabe, rent 12.1 Utilizando ferramentas sociais Para tornar o jogo mais engajador podemos adicionar diversas funcionalidades como rankings, onde os usu vem como medalhas para provar conquistas durante o jogo e at cativo com itens especiais. 12.2. Highscore Casa do C A Apple fornece estas ferramentas de forma nativa, simplificando muito todo esse desenvolvimento, com os frameworks GameKit (para o Game Center) e StoreKit (para os In-App Purchases, que s 12.2 Highscore Os rankings ou highscores no Game Center s melhores. A implementa seguir. Primeiro, deve-se autenticar o usu GKLocalPlayer *localPlayer = [GKLocalPlayer localPlayer]; localPlayer.authenticateHandler = ^(UIViewController *viewController, NSError *error) { if (viewController != nil) { [self showAuthenticationDialogWhenReasonable:viewController]; } else if (localPlayer.isAuthenticated) { [self authenticatedPlayer:localPlayer]; } else { [self disableGameCenter]; } }]; Ap cando em qual Leaderboard a mesma ser GKScore *scoreReporter = [[GKScore alloc] initWithCategory:br.com.casadocodigo.bis.meteoros]; scoreReporter.value = meteorosDestruidos; [scoreReporter reportScoreWithCompletionHandler:^(NSError *error) { // C }]; Para exibir o Game Center dentro do seu jogo, pode-se implementar o c abaixo: 174 Casa do C Cap GKGameCenterViewController *gameCenterVC = [[GKGameCenterViewController alloc] init]; if (gameCenterVC != nil) { gameCenterVC.gameCenterDelegate = self; gameCenterVC.viewState = GKGameCenterViewControllerStateLeaderboards; gameCenterVC.leaderboardTimeScope = GKLeaderboardTimeScopeToday; gameCenterVC.leaderboardCategory = br.com.casadocodigo.bis.meteoros; [self presentViewController:gameCenterVC animated:YES completion:nil]; } Figura 12.1: Criando Leaderboards. Mais detalhes sobre o Game Center e Leaderboards podem ser encontrados na documenta https://developer.apple.com/library/ios/#documentation/NetworkingInternet/ Conceptual/GameKit_Guide/Introduction/Introduction.html 175 12.3. Achievements Casa do C 12.3 Achievements Imagine se ap e ap entregar o Achievement Santos Dumont ao jogador? Essas cria mais atrativo, com mais objetivos, e inclusive podem ser compartilhadas em redes sociais. A cria nas define os Achievements do jogo e informa a porcentagem completada atrav Game Center. if (meteorosDestruidos == 50) { GKAchievement *achievement = [[GKAchievement alloc] initWithIdentifier:br.com.casadocodigo.bis.pilotomaster]; if (achievement) { achievement.percentComplete = 100; [achievement reportAchievementWithCompletionHandler: ^(NSError *error) { if (error != nil) { NSLog(@"Erro: %@", error); } }]; } } Crie ideias interessantes de Achievements e implemente-as com o Game Center. 176 Casa do C Cap Figura 12.2: Criando Achievements. Mais detalhes sobre o Game Center e Achievements podem ser encontrados na documenta https://developer.apple.com/library/ios/#documentation/NetworkingInternet/ Conceptual/GameKit_Guide/Introduction/Introduction.html 12.4 Desafios para voc Aqui v Novos tiros Todo jogador adora incrementar sua muni bis! Esses tiros devem sair n de 45 graus para cada lado. Interessante fazer esse tiro ser dado ao jogador ap um elemento especial que d 177 12.5. Como ganhar dinheiro? Casa do C Diferentes meteoros Quem disse que todos os meteoros s diferentes de meteoros, que pontuam de forma diferente, de acordo com seu tama- nho? Comece simples, meteoros maiores valem 2 pontos e meteoros menores con- tinuam valendo 1. Com isso implementado, mude a imagem dos meteoros e at loque esporadicamente outros elementos que podem valer 5 ou 10 pontos, mas que apare nham velocidades diferentes entre si! Armaduras Morrer com apenas um meteoro que fortifique o avi mita a colis Efeitos nos sprites Atualmente, os objetos s como fazer o meteoro descer girando ou luzes no avi fazem o jogo parecer muito mais atraente. 12.5 Como ganhar dinheiro? O mundo dos games move um valor enorme e ta Para monetizar o game voc lecer um valor de venda quando o usu Uma outra forma bem interessante de ganhar dinheiro com o jogo gratuito ou cobrando um valor bem baixo, com o qual o usu O framework StoreKit da Apple oferece uma forma bem interessante e f aplicar para monetizar o game. Voc de c 178 Casa do C Cap Inicie a loja interna de seu aplicativo j quaisquer transa [[SKPaymentQueue defaultQueue] addTransactionObserver:self]; Busque no iTunes Connect todos os seus produtos, obtendo ent dispon SKProductsRequest *request = [[SKProductsRequest alloc] initWithProductIdentifiers:(NSSet *)productsID]; request.delegate = self; [request start]; Quando o jogador escolher um item para a compra na loja, como uma “Super Bomba”, o jogo deve comunicar-se com o iTunes Connect, solicitando a compra do item. SKPayment *payment = [SKPayment paymentWithProduct:br.com.casadocodigo.bis.SUPER_BOMBA]; [[SKPaymentQueue defaultQueue] addPayment:payment]; O iTunes solicita o Apple ID e a senha do usu s do exemplo. Ent que n [[SKPaymentQueue defaultQueue] finishTransaction:transaction]; Figura 12.3: Exemplo de itens pagos. Detalhes sobre In-App Purchase podem ser encontrados na documenta da Apple: https://developer.apple.com/library/ios/#documentation/NetworkingInternet/ Conceptual/StoreKitGuide/Introduction/Introduction.html 179 12.6. Conclus Casa do C 12.6 Conclus Desenvolver jogos uma lares modernos trouxeram uma oportunidade sam ter essa experi Al nossa imagina Fica novamente o convite para voc https://groups.google.com/group/desenvolvimento-de-jogos-para-ios Boa divers 180 See more books in http://www.e-reading-lib.com