full screen background image

Desenvolvendo um Jogo Digital do Zero: Parte 25 – Implementação do Menu Principal e Finalizações do Shark Pong

Desenvolvendo um Jogo Digital do Zero: Parte 25 – Implementação do Menu Principal e Finalizações do Shark Pong

Voltamos aos artigos sobre o desenvolvimento de um jogo digital do zero.

Desta vez, estamos no meio da inclusão do menu no jogo Shark Pong.

Hoje finalizaremos a sua implementação. Bora.

Retomando o Shark Pong

As pessoas que estão acompanhando de perto o desenvolvimento do Shark Pong perceberão que eu fiquei ausente da redação do blog por um tempo. Não pude escrever nesses últimos dois meses por motivos pessoais, mas agora eu estou de volta.

TelasAntigasVimos no último artigo como incluir a tela do menu principal e como ligar o botão “Novo Jogo” com o início do gameplay. Para isso, nós precisamos organizar duas sub-rotinas: uma própria para apresentar o menu ao jogador; outra para dar início ao gameplay.

O Shark Pong já ficou com uma cara mais profissional, porém ainda faltam concluir algumas funções no menu e melhorar a tela de término de jogo (vamos tirar aquelas etiquetas simplistas, né?!).

Vamos, primeiramente, mudar as telas de término do jogo. Depois implementaremos a funcionalidade do botão “Sobre”. Por último, Vamos incluir a funcionalidade do botão “Sair”.

Telas de término do jogo

Relembrando, as telas de término de jogo são duas: uma que mostra uma mensagem de parabéns pelo jogador ter vencido e outra que mostra uma mensagem informando que ele perdeu. Ambas possuem a mensagem textual e duas opções padrões: um botão para o usuário jogar novamente e outro para voltar ao menu principal.

Se você reabrir o sprite sheet disponibilizado no último artigo, perceberá que essas duas telas inteiras estão lá em imagens únicas. Assim, precisaremos criar sprites apenas para as telas em si e para os botões.

Vamos começar a edição do código adicionando variáveis na classe “HelloWorld” que armazenam esses sprites. Abra o arquivo “HelloWorldScene.h” e adicione as seguintes linhas de código:

cocos2d::CCSprite* fundoGanhouTerminoJogo;

cocos2d::CCSprite* fundoPerdeuTerminoJogo;

cocos2d::CCSprite* botaoGanhouTentarDeNovo;

cocos2d::CCSprite* botaoPerdeuTentarDeNovo;

cocos2d::CCSprite* botaoGanhouNaoTentarDeNovo;

cocos2d::CCSprite* botaoPerdeuNaoTentarDeNovo;

logo abaixo dessa:

cocos2d::CCSprite* link;

BotoesNote que criamos seis sprites: dois para as telas e quatro para os botões. Isso porque cada tela possui dois botões e tanto as telas como os botões são todos distintos entre si. Para podermos utilizar esses sprites no Shark Pong, precisaremos inicializá-los. Para isso, abra o arquivo “HelloWorldScene.cpp” e adicione as seguintes linhas de código:

HelloWorld::fundoGanhouTerminoJogo = CCSprite::createWithSpriteFrameName(“FundoGanhou.png”);

if(size.width/size.height>HelloWorld::fundoGanhouTerminoJogo->boundingBox().size.width/HelloWorld::fundoGanhouTerminoJogo->boundingBox().size.height)

    HelloWorld::fundoGanhouTerminoJogo->setScale(size.width/HelloWorld::fundoGanhouTerminoJogo->boundingBox().size.width);

else

    HelloWorld::fundoGanhouTerminoJogo->setScale(size.height/HelloWorld::fundoGanhouTerminoJogo->boundingBox().size.height);

HelloWorld::fundoGanhouTerminoJogo->setPosition(ccp(size.width/2,size.height/2));

addChild(HelloWorld::fundoGanhouTerminoJogo);

HelloWorld::botaoGanhouTentarDeNovo = CCSprite::createWithSpriteFrameName(“BotaoGanhouSim.png”);

HelloWorld::botaoGanhouTentarDeNovo->setScale((0.08*size.height)/HelloWorld::botaoGanhouTentarDeNovo->boundingBox().size.height);

HelloWorld::botaoGanhouTentarDeNovo->setPosition(ccp(0.43*size.width,0.46*size.height));

addChild(HelloWorld::botaoGanhouTentarDeNovo);

HelloWorld::botaoGanhouNaoTentarDeNovo = CCSprite::createWithSpriteFrameName(“BotaoGanhouNao.png”);

HelloWorld::botaoGanhouNaoTentarDeNovo->setScale((0.08*size.height)/HelloWorld::botaoGanhouNaoTentarDeNovo->boundingBox().size.height);

HelloWorld::botaoGanhouNaoTentarDeNovo->setPosition(ccp(0.57*size.width,0.46*size.height));

addChild(HelloWorld::botaoGanhouNaoTentarDeNovo);

HelloWorld::fundoPerdeuTerminoJogo = CCSprite::createWithSpriteFrameName(“FundoPerdeu.png”);

if(size.width/size.height>HelloWorld::fundoPerdeuTerminoJogo->boundingBox().size.width/HelloWorld::fundoPerdeuTerminoJogo->boundingBox().size.height)

    HelloWorld::fundoPerdeuTerminoJogo->setScale(size.width/HelloWorld::fundoPerdeuTerminoJogo->boundingBox().size.width);

else

    HelloWorld::fundoPerdeuTerminoJogo->setScale(size.height/HelloWorld::fundoPerdeuTerminoJogo->boundingBox().size.height);

addChild(HelloWorld::fundoPerdeuTerminoJogo);

HelloWorld::fundoPerdeuTerminoJogo->setPosition(ccp(size.width/2,size.height/2));

HelloWorld::botaoPerdeuTentarDeNovo = CCSprite::createWithSpriteFrameName(“BotaoPerdeuSim.png”);

HelloWorld::botaoPerdeuTentarDeNovo->setScale((0.08*size.height)/HelloWorld::botaoPerdeuTentarDeNovo->boundingBox().size.height);

HelloWorld::botaoPerdeuTentarDeNovo->setPosition(ccp(0.43*size.width,0.41*size.height));

addChild(HelloWorld::botaoPerdeuTentarDeNovo);

HelloWorld::botaoPerdeuNaoTentarDeNovo = CCSprite::createWithSpriteFrameName(“BotaoPerdeuNao.png”);

HelloWorld::botaoPerdeuNaoTentarDeNovo->setScale((0.08*size.height)/HelloWorld::botaoPerdeuNaoTentarDeNovo->boundingBox().size.height);

HelloWorld::botaoPerdeuNaoTentarDeNovo->setPosition(ccp(0.57*size.width,0.41*size.height));

addChild(HelloWorld::botaoPerdeuNaoTentarDeNovo);

HelloWorld::fundoGanhouTerminoJogo->setVisible(false);

HelloWorld::fundoPerdeuTerminoJogo->setVisible(false);

HelloWorld::botaoGanhouTentarDeNovo->setVisible(false);

HelloWorld::botaoPerdeuTentarDeNovo->setVisible(false);

HelloWorld::botaoGanhouNaoTentarDeNovo->setVisible(false);

HelloWorld::botaoPerdeuNaoTentarDeNovo->setVisible(false);

logo abaixo dessa:

addChild(HelloWorld::link);

RemocaoAntes de começarmos a usar os sprites inicializados para criar as telas de término jogo, vamos tirar as etiquetas que informavam se o jogador ganhou ou perdeu. Como agora temos uma tela só para isso, as etiquetas se tornaram obsoletas. Para removê-las, apague, do mesmo arquivo, a seguinte linha de código:

CCLabelTTF* fimDeJogo;

e as seguintes linhas de código:

if(jogador) {

    fimDeJogo = CCLabelTTF::create(“Voce Perdeu! =/”,””,40);

    fimDeJogo->setColor(ccc3(53,41,27));

} else {

    fimDeJogo = CCLabelTTF::create(“Voce Ganhou! =]”,””,40);

    fimDeJogo->setColor(ccc3(198,180,240));

}

fimDeJogo->setPosition(ccp(size.width/2,size.height/2));

addChild(fimDeJogo);

Agora implementaremos a apresentação da tela após a animação do tubarão comendo o surfista perdedor. Para isso, substitua as seguintes linhas de código:

if(HelloWorld::tubarao->getPositionX()>HelloWorld::paIA->getPositionX())

    HelloWorld::paIA->setVisible(false);

else

    HelloWorld::paJogador->setVisible(false);

por essas:

if(HelloWorld::tubarao->getPositionX()>HelloWorld::paIA->getPositionX()) {

    HelloWorld::paIA->setVisible(false);

    HelloWorld::fundoGanhouTerminoJogo->setVisible(true);

    HelloWorld::botaoGanhouTentarDeNovo->setVisible(true);

    HelloWorld::botaoGanhouNaoTentarDeNovo->setVisible(true);

} else {

    HelloWorld::paJogador->setVisible(false);

    HelloWorld::fundoPerdeuTerminoJogo->setVisible(true);

    HelloWorld::botaoPerdeuTentarDeNovo->setVisible(true);

    HelloWorld::botaoPerdeuNaoTentarDeNovo->setVisible(true);

}

setTouchEnabled(true);

Note que as modificações foram simples. Apenas deixamos visível a tela de término de jogo respectiva à qual surfista foi engolido pelo tubarão. Se o tubarão engoliu o surfista controlado pelo NPC, então o jogador ganhou e é mostrada a tela de término de jogo referente ao jogador ganhar. Caso o tubarão engoliu o surfista controlado pelo jogador, então é mostrada a tela de término de jogo referente ao jogador perder. As duas telas de término do jogo são ilustradas pelas Figuras 1 e 2. Note também que a tela de toque foi acionada. Isso porque o jogador precisa escolher uma das duas opções clicando em um dos botões.

Figura 1 - Tela Você Ganhou

Figura 1 – Tela Você Ganhou

Se você compilar e executar o jogo nesse momento, perceberá que essa mecânica funciona certinho. Porém, se você clicar em uma das opções, nada acontecerá. Isso porque nós ainda não implementamos a funcionalidade dos botões “Sim” e “Não” de ambas as telas. Faremos com que, caso o jogador clique em “Sim”, o gameplay seja reiniciado e, caso o jogador clique em “Não”, o jogo apresente o menu principal. Para isso, adicione as seguintes linhas de código:

else if((HelloWorld::botaoGanhouTentarDeNovo->isVisible()&&HelloWorld::botaoGanhouTentarDeNovo->boundingBox().containsPoint(ponto))||

  (HelloWorld::botaoPerdeuTentarDeNovo->isVisible()&&HelloWorld::botaoPerdeuTentarDeNovo->boundingBox().containsPoint(ponto))) {

    HelloWorld::animacaoInicialTubarao();

} else if((HelloWorld::botaoGanhouNaoTentarDeNovo->isVisible()&&HelloWorld::botaoGanhouNaoTentarDeNovo->boundingBox().containsPoint(ponto))||

  (HelloWorld::botaoPerdeuNaoTentarDeNovo->isVisible()&&HelloWorld::botaoPerdeuNaoTentarDeNovo->boundingBox().containsPoint(ponto))) {

    HelloWorld::abreMenu();

}

logo abaixo dessas:

    HelloWorld::animacaoInicialTubarao();

}

Perceba que essas linhas foram adicionadas dentro do método “ccTouchesBegan”. Caso uma das telas de término de jogo esteja sendo mostrada e um toque seja realizado sobre um dos botões “Sim” de uma das telas, então o método “animacaoInicialTubarao” é chamado e o gameplay é iniciado. Caso o mesmo aconteça, mas com um clique sobre o botão “Não”, então o método “abreMenu” é chamado e o menu principal é mostrado.

Como de praxe, sempre tem algo a mais a se preocupar. Se você compilar o código e executar o jogo, perceberá que as telas são mostradas e os botões funcionarão, porém as telas de término de jogo não fechadas. Elas permanecem, mesmo após o jogador ter escolhido uma das opções. Precisamos deixá-las invisíveis após o jogador escolher qualquer uma das opções. Para fazermos isso, adicione as seguintes linhas de código:

HelloWorld::fundoGanhouTerminoJogo->setVisible(false);

HelloWorld::fundoPerdeuTerminoJogo->setVisible(false);

HelloWorld::botaoGanhouTentarDeNovo->setVisible(false);

HelloWorld::botaoPerdeuTentarDeNovo->setVisible(false);

HelloWorld::botaoGanhouNaoTentarDeNovo->setVisible(false);

HelloWorld::botaoPerdeuNaoTentarDeNovo->setVisible(false);

logo abaixo dessa:

HelloWorld::link->setVisible(false);

e também logo abaixo dessa:

HelloWorld::link->setVisible(true);

Note que nós deixamos invisíveis os sprites referentes às duas telas de término de jogo no momento que o método “abreMenu” é executado e no momento que o método “animacaoInicialTubarao” é executado. Isso resolve o nosso problema.

Figura 2 - Tela Você Perdeu

Figura 2 – Tela Você Perdeu

Com isso, nós finalizamos a implementação da funcionalidade de término do jogo e volta ao menu principal.

Menu Sobre

Vamos continuar a implementação do menu com a inclusão da tela “Sobre”. Precisaremos criar um sprite que mostra uma imagem com as informações sobre a equipe desenvolvedora do Shark Pong. Além disso, precisaremos implementar um método específico que realiza todos os procedimentos para mostrar corretamente a tela “Sobre” o jogador.

Vamos declarar na classe “HelloWorld” uma variável que armazena um sprite responsável por apresentar as informações dos desenvolvedores e um novo método que apresentará a tela. Para isso, abra o arquivo “HelloWorldScene.h” e adicione as seguintes linhas de código:

cocos2d::CCSprite* infoSobre;

void abreSobre();

logo abaixo dessa:

cocos2d::CCSprite* botaoPerdeuNaoTentarDeNovo;

As informações sobre os desenvolvedores estão todas em um sprite frame contido no sprite sheet disponibilizado no tutorial passado. Precisamos inicializar o sprite com as informações sobre os desenvolvedores para que o possamos usar na tela “Sobre”. Para isso, abra o arquivo “HelloWorldScene.cpp” e adicione as seguintes linhas de código:

HelloWorld::infoSobre = CCSprite::createWithSpriteFrameName(“SobreInfo.png”);

HelloWorld::infoSobre->setScale((0.65*size.height)/HelloWorld::infoSobre->boundingBox().size.height);

HelloWorld::infoSobre->setPosition(ccp(size.width/2,0.4*size.height));

addChild(HelloWorld::infoSobre);

HelloWorld::infoSobre->setVisible(false);

logo abaixo dessa:

addChild(HelloWorld::botaoPerdeuNaoTentarDeNovo);

Note que deixamos o sprite “infoSobre” invisível. Isso porque ele só é apresentado ao jogador quando a tela “Sobre” é mostrada. Agora sim, podemos implementar o método responsável por mostrar essa tela ao jogador. Adicione as seguintes linhas de código no final do arquivo:

void HelloWorld::abreSobre() {

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

    HelloWorld::logo->setScale((0.17*size.height)/HelloWorld::logo->getContentSize().height);

    HelloWorld::logo->setPosition(ccp(size.width/2,0.85*size.height));

    HelloWorld::botaoNovoJogo->setVisible(false);

    HelloWorld::botaoSobre->setVisible(false);

    HelloWorld::botaoSair->setVisible(false);

    HelloWorld::infoSobre->setVisible(true);

}

InvisivelNote que nós apenas deixamos as opções do menu principal invisíveis, deixamos o sprite “infoSobre” visível e redimensionamos e reposicionamos o logo do jogo. É possível notar no código acrescido que a logo é modificada nessa tela. Assim sendo, se o jogador entrar na tela “Sobre” e voltar ao menu principal, o logo precisa ser atualizada. Além disso, o sprite “abreSobre” precisa voltar a ser invisível quando o jogador volta ao menu principal. Para arrumar essas inconsistências, adicione as seguintes linhas de código no final do método “abreMenu”:

HelloWorld::infoSobre->setVisible(false);

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

HelloWorld::logo->setScale((0.35*size.height)/HelloWorld::logo->getContentSize().height);

HelloWorld::logo->setPosition(ccp(size.width/2,0.78*size.height));

Com o sprite devidamente inicializado e o método “abreSobre” devidamente implementado, podemos mostrar a tela “Sobre” ao jogador apenas chamando o método “abreSobre”. Queremos mostrar tal tela quando o jogador clica no botão “Sobre” do menu principal. Para que isso seja feito, é necessário incluir mais algumas linhas de código no método “ccTouchesBegan”. Adicione no final desse método as seguintes linhas de código:

else if(HelloWorld::botaoSobre->isVisible()&&HelloWorld::botaoSobre->boundingBox().containsPoint(ponto)) {

    HelloWorld::abreSobre();

} else if(HelloWorld::infoSobre->isVisible()) {

    HelloWorld::abreMenu();

}

Perceba que, caso o menu principal esteja sendo mostrado e o jogador clique no botão “Sobre”, então o método “abreSobre” é chamado. Isso fará com que o menu principal saia da tela e seja apresentada a tela “Sobre”. Nessa tela, não há nenhum botão para voltar ao menu principal. Na verdade, essa ação é realizada unicamente com um toque em qualquer lugar na tela “Sobre”. Perceba que essa função já foi implementada nesse último código adicionado, onde o método “abreMenu” é chamado caso seja realizado um toque na tela enquanto o sprite “infoSobre” esteja sendo apresentado.

Figura 3 - Tela Sobre

Figura 3 – Tela Sobre

Pronto, a funcionalidade da tela “Sobre” já está adicionada ao Shark Pong. A Figura 3 ilustra essa tela sendo apresentada ao jogador.

Fechar o jogo

Para finalizar o artigo de hoje, vamos implementar a última função do menu principal: o botão “Sair”. Até o presente momento, a única forma de sair do Shark Pong era pelo botão sair padrão do Cocos2d-x, localizado, nesse jogo, no canto superior direito. Como vamos adicionar a opção de sair em um botão do próprio jogo, podemos retirar do Shark Pong o botão padrão do Cocos2d-x. Para isso, abra o arquivo “HelloWorldScene.cpp” e remova todo o código existente entre essas linhas de código (inclusive):

/////////////////////////////

// 2. add a menu item with “X” image, which is clicked to quit the program

// you may modify it.

// add a “close” icon to exit the progress. it’s an autorelease object

CCMenuItemImage *pCloseItem = CCMenuItemImage::create(

e essa (inclusive):

this->addChild(pMenu, 1);

Isso já o suficiente para retirarmos o botão de sair padrão do Cocos2d-x, porém ainda persiste nas pastas do projeto os seus arquivos de imagem. Para liberar espaço, apague os arquivos “CloseNormal.png” e “CloseSelected.png” da pasta “Resources”.

Agora, mais do que nunca, nós precisamos incluir a funcionalidade de sair do jogo no respectivo botão do menu principal. Para isso, apenas adicione as seguintes linhas de código no final do método “ccTouchesBegan”.

else if(HelloWorld::botaoSair->isVisible()&&HelloWorld::botaoSair->boundingBox().containsPoint(ponto)) {

    HelloWorld::menuCloseCallback(this);

}

Prooooonto … ufa. Agora assim finalizamos a implementação do menu no Shark Pong. Para relembrar, nós implementamos a funcionalidade do botão “Novo Jogo”. Para isso, nós precisamos organizar as rotinas de início de gameplay e de apresentação do menu principal. Também implementamos o término do jogo apresentando apenas uma etiqueta que dava uma posição do resultado do jogador.

Hoje, nós implementamos as telas de término de jogo com opções de jogar novamente e de voltar ao menu principal, a funcionalidade da tela “Sobre” e o botão de sair. Se você quiser saber como ficou o jogo na sua versão final, assista o vídeo a seguir. Também é possível baixar o Shark Pong para Android diretamente pelo Google Play. Você pode procurar por “SharkPong” ou acessar diretamente por aqui.

É isso aí pessoal, declaro finalizada a parte de programação do Shark Pong. Ainda temos mais uma publicação referente à saga “Desenvolvendo um Jogo do Zero”, a ser realizada pelo meu amigo e redator Fabiano.

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). Atualmente é programador da Céu Games (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.


  • SUPRAMATY

    Um jogo que queria jogar, mas só existe um e que ainda por cima é chato, é Aqua Teen Hunger Force.
    O desenho é uma bosta, porém se tivesse um joguinho legal, onde o Frylock pudesse teletransportar e soltar raio contra inimigos seria bem diferente. ^_^

Show Buttons
Hide Buttons