Tutorial: Desenvolvendo um shoot em up game: Parte 4 – Tiros

Nosso jogo já tem inimigos que se movimentam por conta e interação com a nave.

Ainda falta fazer a nave atirar e os tiros acertarem os inimigos e o jogador.

Hoje implementaremos as funcionalidades de tiro da nave do jogador e dos helicópteros inimigos.

 

Todos os tutoriais da sequência
Parte 1Parte 2Parte 3 – Parte 4 – Parte 5Parte 6

Vimos no último tutorial como fazer a nave do jogador ir para a esquerda ou para a direita. Como o jogo não tinha nenhum tipo de interação, então foi necessário, primeiramente, incluirmos os botões na tela. Somente depois disso é que implementamos a funcionalidade de fazer a nave ir para a direita ou para a esquerda.

No tutorial de hoje nós implementaremos as funcionalidades de tiro do jogador e dos inimigos. Dessa forma, primeiro incluiremos o botão de tiro na tela, depois implementaremos a funcionalidade do tiro da nave do jogador e, por último, implementaremos a funcionalidade de tiro dos inimigos. Partiu.

 

Adicionando o botão de tiro na tela

BotaoDesligarSe você perceber, no sprite sheet que eu disponibilizei no primeiro tutorial dessa série tem um sprite frame de um botão que ainda não foi utilizado. Esse botão tem um ícone de fogo e, obviamente, serve para atirar nos inimigos. Adicionaremos esse botão no canto inferior direito da tela. Porém, se você notar, perceberá que nesse lugar já tem um botão para fechar o aplicativo. Dessa forma, primeiro precisaremos mover esse botão para o canto superior direito da tela. Assim, abra o arquivo “HelloWorldScene.cpp” e modifique a seguinte linha de código:

pCloseItem->setPosition( ccp(CCDirector::sharedDirector()->getWinSize().width – 20, 20) );

de forma que ela fique assim:

pCloseItem->setPosition( ccp(CCDirector::sharedDirector()->getWinSize().width – 20, CCDirector::sharedDirector()->getWinSize().height – 20) );

Com essa mudança, poderemos incluir o botão de tiro onde nos interessa. Para podermos identificar quando o jogador aperta esse botão, precisaremos incluir na classe “HelloWorld” uma variável que armazena o sprite referente a ele. Assim, abra o arquivo “HelloWorldScene.h” e adicione a seguinte linha de código:

cocos2d::CCSprite* botaoTiro;

logo abaixo dessa:

int andando;

Agora que declaramos a variável que armazena o sprite referente ao botão de tiro, podemos criar esse sprite e o adicionar na tela. Para isso, abra o arquivo “HelloWorldScene.cpp” e adicione as seguintes linhas de código:

HelloWorld::botaoTiro = CCSprite::createWithSpriteFrameName(“BotaoFogo.png”);

HelloWorld::botaoTiro->setScale((0.2*size.width)/HelloWorld::botaoTiro->boundingBox().size.width);

HelloWorld::botaoTiro->setPosition(ccp(0.9*size.width,HelloWorld::botaoTiro->boundingBox().size.height/2));

addChild(HelloWorld::botaoTiro,3);

logo abaixo dessa:

addChild(HelloWorld::botaoDireita,3);

Agora que o botão já está na tela, vamos implementar a sua funcionalidade.

 

Fazendo a nave do jogador atirar

F-15E-SparrowNo último tutorial, tivemos que adicionar uma variável que identifica quando um dos botões de movimentação está sendo pressionado. Precisaremos declarar na classe “HelloWorld” outra variável parecida, para que também não haja problemas no botão de tiro. Além disso, precisaremos criar um método que cria um sprite de tiro abaixo da nave do jogador e o faz movimentar na tela, dando o efeito de que a nave realmente atirou. Não obstante, o sprite que foi criado deve ser destruído quando ele sai da tela.

Como visto, precisaremos declarar uma variável que identifica quando o botão de tiro está sendo pressionado. Além disso, também é necessário declarar outros dois métodos: um que cria o sprite de um tiro e outro que destrói o mesmo. Para realizar essas declarações, abra o arquivo “HelloWorldScene.h” e adicione as seguintes linhas de código:

int atirando;

void criaTiroJogador();

void destroiTiro(cocos2d::CCNode* no);

logo abaixo dessa:

cocos2d::CCSprite* botaoTiro;

Relembrando, a variável identificadora é inicializada com o valor -1 e, quando o jogador aperta no botão de tiro, ela armazena o número identificador do toque na tela. Com o uso dessa variável nós evitamos problemas que acontecem no momento que o jogador tira o dedo da tela. Vamos inicializar a variável “atirando” com o valor -1. Abra o arquivo “HelloWorldScene.cpp” e adicione a seguinte linha de código:

HelloWorld::atirando = -1;

logo abaixo dessa:

addChild(HelloWorld::botaoTiro,3);

Agora vamos programar os métodos “criaTiroJogador” e “destroiTiro”. Adicione as seguintes linhas de código no final do mesmo arquivo:

void HelloWorld::criaTiroJogador() {

    float tempo;

    CCSize size = CCDirector::sharedDirector()->getWinSize();

    CCSprite* tiro = CCSprite::createWithSpriteFrameName(“JogadorTiro.png”);

    tiro->setScale(HelloWorld::jogador->getScale());

    tiro->setPosition(HelloWorld::jogador->getPosition());

    addChild(tiro,1);

    tempo = (size.height – tiro->getPositionY())/800.0;

    CCMoveTo* andar = CCMoveTo::create(tempo,ccp(tiro->getPositionX(),size.height));

    CCCallFuncN* destruir = CCCallFuncN::create(this,callfuncN_selector(HelloWorld::destroiTiro));

    tiro->runAction(CCSequence::create(andar,destruir,NULL));

}

void HelloWorld::destroiTiro(CCNode* no) {

    removeChild(no);

}

destruirNo método “criaTiroJogador” nós primeiramente criamos um sprite que representa o tiro que o jogador está dando no momento. Adicionamos o sprite na tela e calculamos o tempo necessário para ele atravessar a tela de baixo para cima. Logo após, nós fizemos o tiro percorrer a tela e executar o método “destroiTiro” quando ele terminar o percurso. Obviamente, quando o tiro percorrer toda a tela, o método “destroiTiro” entra em ação. Nele, nós apenas retiramos o sprite da tela, o que é suficiente para ser excluído completamente da memória.

Leitores com mais atenção perceberão que ainda não fizemos com que esses métodos sejam executados no momento certo. Por enquanto, não haverá nenhum efeito de tiro se o jogador apertar no botão de fogo. Ainda precisamos fazer com que o método “criaTiroJogador” seja executado constantemente enquanto o jogador estiver com o dedo encostado sobre o botão de tiro. Além disso, precisamos fazer com que o mesmo método pare de funcionar quando o dedo é desencostado da tela.

Para fazermos o método “criaTiroJogador” ser executado constantemente, adicione no final do método “ccTouchesBegan” as seguintes linhas de código:

else if(HelloWorld::botaoTiro->boundingBox().containsPoint(ponto)) {

    HelloWorld::criaTiroJogador();

    schedule(schedule_selector(HelloWorld::criaTiroJogador),0.1);

    HelloWorld::atirando = (static_cast<CCTouch*>(pTouches->anyObject()))->getID();

}

Para que o método “criaTiroJogador” seja parado, adicione no final do método “ccTouchesEnded” as seguintes linhas de código:

else if(HelloWorld::atirando==(static_cast<CCTouch*>(pTouches->anyObject()))->getID()) {

    unschedule(schedule_selector(HelloWorld::criaTiroJogador));

    HelloWorld::atirando = -1;

}

MetalhadoraNotem que, quando o jogador inicia um toque sobre o botão de fogo, o método “criaTiroJogador” é executado a cada 10 milissegundos. Isso faz com que a nave do jogador atire um novo projétil cada vez que ocorre esse intervalo de tempo. Além disso, a variável indicadora “atirando” começa a armazenar o número identificador do toque na tela. Quanto o jogador termina um toque na tela, que seja referente ao botão de fogo, o método “criaTiroJogador” é parado de executar a cada 10 milissegundos e a variável “atirando” novamente armazena o valor -1. Isso que fizemos dá o efeito de metralhadora enquanto o jogador estiver pressionando o botão de fogo. Legal, né?

Vale lembrar que todos os sprites de tiro criados são automaticamente destruídos pelo método “destroiTiro” após percorrerem a tela por completo.

 

Fazendo os inimigos atirarem

AtiradorPara concluirmos completamente o tutorial de hoje, basta fazermos os inimigos atirarem de forma aleatória. Para isso, é suficiente criarmos um método que é executado em intervalos de tempo aleatórios e que faz um dos seis inimigos atirar. Declararemos, assim, o método “atiraInimigo” na classe “HelloWorld”. Para isso, abra o arquivo “HelloWorldScene.h” e adicione a seguinte linha de código:

void atiraInimigo();

logo abaixo dessa:

void destroiTiro(cocos2d::CCNode* no);

O método “atiraInimigo” criará um sprite de tiro sob o de um inimigo que é escolhido aleatoriamente pelo computador. Após criar o sprite do tiro, o método fará o sprite recém-criado percorrer a tela de cima para baixo. Logo após, será sorteado um tempo aleatório entre zero e 1,5 segundos para esse método ser executado novamente e um novo tiro ser disparado pelos oponentes. Isso fará com que os inimigos atirem em tempos aleatórios, que variam de zero a 1,5 segundos. Adicione a implementação do método “atiraInimigo” a seguir, no final do arquivo “HelloWorldScene.cpp”.

void HelloWorld::atiraInimigo() {

    static int r = time(NULL);

    float tempo;

    int i;

    CCSize size = CCDirector::sharedDirector()->getWinSize();

    srand(r);

    r = rand();

    i = ((float)r)/(((float)RAND_MAX)/6.0);

    CCSprite* tiro = CCSprite::createWithSpriteFrameName(“InimigoTiro.png”);

    tiro->setScale(HelloWorld::inimigos[i]->getScale());

    tiro->setPosition(HelloWorld::inimigos[i]->getPosition());

    addChild(tiro,1);

    tempo = tiro->getPositionY()/600.0;

    CCMoveTo* andar = CCMoveTo::create(tempo,ccp(tiro->getPositionX(),0));

    CCCallFuncN* destruir = CCCallFuncN::create(this,callfuncN_selector(HelloWorld::destroiTiro));

    tiro->runAction(CCSequence::create(andar,destruir,NULL));

    srand(r);

    r = rand();

    tempo = 1.5*(((float)r)/RAND_MAX);

    schedule(schedule_selector(HelloWorld::atiraInimigo),tempo);

}

Agora basta fazermos o método “atiraInimigo” ser executado pela primeira vez, para que os inimigos atirem em intervalos de tempo aleatórios. Executaremos esse método após decorrer cinco segundos do início do jogo, para que o jogador possa se preparar. Para isso, adicione a seguinte linha de código:

schedule(schedule_selector(HelloWorld::atiraInimigo),5.0);

logo abaixo dessa:

HelloWorld::entraInimigos();

Agora siiim … os inimigos atirarão de forma aleatória e em intervalos de tempo também aleatórios. O resultado do tutorial de hoje é algo como o que é mostrado na Figura 1. Perceba que o botão de fogo está funcionando como uma metralhadora e os inimigos estão atirando como o previsto. Os tiros do jogador são os vermelhos e os dos inimigos são os azuis. Tentei fazer um vídeo, mas os tiros não apareciam direito. =/

Figura 1 - Tiros do jogador e dos inimigos
Figura 1 – Tiros do jogador e dos inimigos

Vimos nesse tutorial como adicionar as funcionalidades de tiro da nave do jogador e dos helicópteros dos inimigos. Primeiro incluímos o botão que faz a nave do jogador atirar. Logo após, nós implementamos um método que cria um tiro e outro que destrói o mesmo. Depois, nós fizemos com que o método que cria um tiro seja executado constantemente, enquanto o jogador apertar o botão de fogo. Por último, nós fizemos com que os inimigos atirassem contra o jogador. Ufa …

Veremos no próximo tutorial como fazer com que os inimigos explodam, caso um tiro do jogador entre em contato com eles. Também faremos com que o jogador exploda, caso um dos tiros dos inimigos acerte a nave dele.

Um grande abraço e até mais. []

Santiago Viertel

Santiago Viertel

Formado em Bacharelado em Ciência da Computação (UDESC), mestre e doutorando em Análise de Algoritmos (UFPR). Foi programador da Céu Games por 8 anos. Possui a preferência por jogos de estratégia e de tiro em primeira pessoa. Jogando bastante DotA 2, Left 4 Dead 2 e Age of Empires II HD.

Send this to a friend