Tutorial: Desenvolvendo um shoot em up game: Parte 3 – Movendo a nave

Fizemos os inimigos aparecerem e desaparecerem da tela para adicionar dinamismo ao jogo.

A nave do jogador ainda não faz nenhum tipo de movimentação… nem atira.

Hoje faremos com que ela se movimente para a esquerda ou direita.

 

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

Vimos no tutorial passado como fazer os helicópteros entrarem e saírem da tela. Para isso, dividimos essas animações em duas: uma que os inimigos saem da tela; outra que os inimigos entram. No início do jogo, nós executamos a animação de entrada dos inimigos. Ao passarem cinco segundos, eles saem da tela e entram novamente para as suas posições normais. Com isso, fizemos que este processo ocorra de cinco em cinco segundos.

NaveNo tutorial de hoje implementaremos a movimentação da nave do jogador. Para isso, precisaremos adicionar os botões de interação que fazem a nave se movimentar para a esquerda ou direita. Por fim, precisaremos criar as funcionalidades de ambos os botões.

 

Adicionando os botões na tela

Iniciaremos a programação adicionando os botões que fazem a nave do jogador andar para a esquerda e direita. Como de praxe, precisamos declarar na classe “HelloWorld” duas variáveis de sprites que representam esses botões. Assim, abra o arquivo “HelloWorldScene.h” e adicione as seguintes linhas de código:

cocos2d::CCSprite* botaoDireita;

cocos2d::CCSprite* botaoEsquerda;

logo abaixo dessa:

cocos2d::CCSprite* sombraInimigos[6];

Agora criaremos esses dois sprites e os mostraremos na tela. Para isso, abra o arquivo “HelloWorldScene.cpp” e adicione as seguintes linhas de código:

HelloWorld::botaoEsquerda = CCSprite::createWithSpriteFrameName(“BotaoEsq.png”);

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

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

addChild(HelloWorld::botaoEsquerda,3);

HelloWorld::botaoDireita = CCSprite::createWithSpriteFrameName(“BotaoDir.png”);

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

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

addChild(HelloWorld::botaoDireita,3);

logo ACIMA dessa:

HelloWorld::entraInimigos();

BotaoVocê pode notar no código que acabamos de adicionar que nós apenas criamos os sprites dos botões, os escalamos, os posicionamos na tela e os mostramos ao jogador.

Agora implementaremos as suas funcionalidades.

 

Programando as funcionalidades dos botões

Para podermos fazer os botões funcionarem, precisaremos implementar os métodos “ccTouchesBegan” e “ccTouchesEnded”. Além disso, para que o toque no botão de movimentação do avião não seja confundido com o toque no botão de tiro, precisaremos identificar quando o avião está se movimentando e quando ele está parado. Para isso, precisaremos adicionar na classe “HelloWorld” uma variável que armazena um valor inteiro, que identifica o toque na tela e que fez a nave se movimentar. Dessa foma, abra o arquivo “HelloWorldScene.h” e adicione as seguintes linhas de código:

int andando;

void ccTouchesBegan(cocos2d::CCSet *pTouches,cocos2d::CCEvent *pEvent);

void ccTouchesEnded(cocos2d::CCSet *pTouches,cocos2d::CCEvent *pEvent);

logo abaixo dessa:

cocos2d::CCSprite* botaoEsquerda;

Iniciaremos a programação das funcionalidades implementando os métodos “ccTouchesBegan” e “ccTouchesEnded”. Esses métodos são chamados, respectivamente, quando o jogador inicia um toque na tela e quando ele tira o dedo dela. Dessa forma, adicione a implementação do método “ccTouchesBegan” no final do arquivo “HelloWorldScene.cpp”.

void HelloWorld::ccTouchesBegan(CCSet *pTouches,CCEvent *pEvent) {

    float tempo;

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

    CCPoint ponto = (static_cast<CCTouch*>(pTouches->anyObject()))->getLocationInView();

    ponto.y = size.height – ponto.y;

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

        HelloWorld::jogador->stopAllActions();

        HelloWorld::sombraJogador->stopAllActions();

        tempo = (HelloWorld::jogador->getPositionX() – HelloWorld::jogador->boundingBox().size.width/2)/300.0;

        CCMoveTo *andarJogador = CCMoveTo::create(tempo,ccp(HelloWorld::jogador->boundingBox().size.width/2,HelloWorld::jogador->getPositionY()));

        CCArray* framesJogador = CCArray::create();

        framesJogador->addObject(CCSpriteFrameCache::sharedSpriteFrameCache()->spriteFrameByName(“Jogador4.png”));

        framesJogador->addObject(CCSpriteFrameCache::sharedSpriteFrameCache()->spriteFrameByName(“Jogador5.png”));

        framesJogador->addObject(CCSpriteFrameCache::sharedSpriteFrameCache()->spriteFrameByName(“Jogador6.png”));

        framesJogador->addObject(CCSpriteFrameCache::sharedSpriteFrameCache()->spriteFrameByName(“Jogador5.png”));

        CCRepeatForever* frames = CCRepeatForever::create(CCAnimate::create(

          CCAnimation::createWithSpriteFrames(framesJogador,0.1)));

        CCMoveTo *andarSombraJogador = CCMoveTo::create(tempo,ccp(HelloWorld::jogador->boundingBox().size.width/2 + 0.15*size.width,HelloWorld::sombraJogador->getPositionY()));

        HelloWorld::jogador->setFlipX(false);

        HelloWorld::jogador->runAction(andarJogador);

        HelloWorld::jogador->runAction(frames);

        HelloWorld::sombraJogador->setDisplayFrame(CCSpriteFrameCache::sharedSpriteFrameCache()->spriteFrameByName(“JogadorSombra2.png”));

        HelloWorld::sombraJogador->setFlipX(false);

        HelloWorld::sombraJogador->runAction(andarSombraJogador);

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

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

        HelloWorld::jogador->stopAllActions();

        HelloWorld::sombraJogador->stopAllActions();

        tempo = (size.width – HelloWorld::jogador->boundingBox().size.width/2 – HelloWorld::jogador->getPositionX())/300.0;

        CCMoveTo *andarJogador = CCMoveTo::create(tempo,ccp(size.width – HelloWorld::jogador->boundingBox().size.width/2,HelloWorld::jogador->getPositionY()));

        CCArray* framesJogador = CCArray::create();

        framesJogador->addObject(CCSpriteFrameCache::sharedSpriteFrameCache()->spriteFrameByName(“Jogador4.png”));

        framesJogador->addObject(CCSpriteFrameCache::sharedSpriteFrameCache()->spriteFrameByName(“Jogador5.png”));

        framesJogador->addObject(CCSpriteFrameCache::sharedSpriteFrameCache()->spriteFrameByName(“Jogador6.png”));

        framesJogador->addObject(CCSpriteFrameCache::sharedSpriteFrameCache()->spriteFrameByName(“Jogador5.png”));

        CCRepeatForever* frames = CCRepeatForever::create(CCAnimate::create(

          CCAnimation::createWithSpriteFrames(framesJogador,0.1)));

        CCMoveTo *andarSombraJogador = CCMoveTo::create(tempo,ccp(size.width – HelloWorld::jogador->boundingBox().size.width/2 + 0.15*size.width,HelloWorld::sombraJogador->getPositionY()));

        HelloWorld::jogador->setFlipX(true);

        HelloWorld::jogador->runAction(andarJogador);

        HelloWorld::jogador->runAction(frames);

        HelloWorld::sombraJogador->setDisplayFrame(CCSpriteFrameCache::sharedSpriteFrameCache()->spriteFrameByName(“JogadorSombra2.png”));

        HelloWorld::sombraJogador->setFlipX(true);

        HelloWorld::sombraJogador->runAction(andarSombraJogador);

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

    }

}

soco-no-vidroForam adicionados bastantes códigos, porém todos são simples de entender. Primeiramente, localizamos a posição que houve um toque na tela. Logo após, identificamos se esse toque foi no botão que faz o avião andar para a esquerda ou se foi no outro botão. Caso o toque tenha sido no primeiro botão citado, então realizamos esse tratamento. Na verdade, o método é similar, apenas espelhamos a movimentação do avião.

Note que, para realizar a movimentação de forma correta, primeiramente nós paramos todas as animações que estão em andamento dos sprites da nave e de sua sombra. Logo após, nós calculamos o tempo necessário para ela atravessar a tela e criamos as animações que farão a nave e a sua sombra se movimentarem até o final da tela. Note também que mudamos os frames da nave, para que haja a animação dela realizando uma curva. Por último, nós identificamos que a nave está em movimentação armazenando o número identificador do toque na tela na variável “andando”.

Ainda não terminamos de implementar a funcionalidade dos botões. Implementamos apenas o método que faz o tratamento necessário quando o jogador inicia um toque na tela. Agora precisamos realizar o tratamento de quando o jogador tira o dedo na tela. Para isso, adicione no final do mesmo aquivo a implementação do método “ccTouchesEnded”.

void HelloWorld::ccTouchesEnded(CCSet *pTouches,CCEvent *pEvent) {

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

        HelloWorld::jogador->stopAllActions();

        HelloWorld::sombraJogador->stopAllActions();

        CCArray* framesJogador = CCArray::create();

        framesJogador->addObject(CCSpriteFrameCache::sharedSpriteFrameCache()->spriteFrameByName(“Jogador1.png”));

        framesJogador->addObject(CCSpriteFrameCache::sharedSpriteFrameCache()->spriteFrameByName(“Jogador2.png”));

        framesJogador->addObject(CCSpriteFrameCache::sharedSpriteFrameCache()->spriteFrameByName(“Jogador3.png”));

        framesJogador->addObject(CCSpriteFrameCache::sharedSpriteFrameCache()->spriteFrameByName(“Jogador2.png”));

        HelloWorld::jogador->setFlipX(false);

        HelloWorld::jogador->runAction(CCRepeatForever::create(CCAnimate::create(

          CCAnimation::createWithSpriteFrames(framesJogador,0.1))));

        HelloWorld::sombraJogador->setFlipX(false);

        HelloWorld::sombraJogador->setDisplayFrame(CCSpriteFrameCache::sharedSpriteFrameCache()->spriteFrameByName(“JogadorSombra1.png”));

        HelloWorld::andando = –1;

    }

}

A implementação desse método é mais simples do que a implementação do método “ccTouchesBegan”. Primeiramente é verificado se o jogador parou de realizar um toque em um dos botões de movimentação da nave. Caso isso aconteça, então são paradas todas as animações existentes nos sprites da nave e da sua sombra. É criada uma nova animação no sprite da nave que faz com que ela fique soltando fogo pelas turbinas. Também deixamos a sombra dela pertinente com a sua forma. Por último, fizemos a variável identificadora de movimentação da nave armazenar o valor -1, para que a nave não pare a animação corrente caso o jogador venha a apertar em outro lugar na tela de toque.

Por último, e não menos importante, precisamos acionar a tela de toque e inicializar a variável “andando” com o valor -1. Para isso, no mesmo arquivo, adicione as seguintes linhas de código:

HelloWorld::andando = -1;

setTouchEnabled(true);

logo ACIMA dessa:

HelloWorld::entraInimigos();

Compile o código e execute o jogo. Você verá os dois botões de interação e perceberá que a funcionalidade deles está funcionando devidamente. O resultado é algo parecido com o que é mostrado na animação a seguir.

JogoExecutando

 

Vimos nesse tutorial como incluir os botões que movimentam a nave do jogador e como implementar as suas funcionalidades. Precisamos de uma variável identificadora para verificar se um toque na tela recém-finalizado corresponde a um que foi iniciado em um dos botões de movimentação. Essa variável é importante para que o jogo não confunda quando o jogador parar de apertar outro botão que não seja um de movimentação.

Veremos no próximo tutorial como incluir o botão de tiro e como adicionar esta funcionalidade na nave. Também faremos com que os inimigos atirem em tempos aleatórios.

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

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.