Tutorial: Desenvolvendo um shoot em up game: Parte 1 – Criando o projeto no Cocos2d-x

Voltamos ao desenvolvimento de mais um conjunto de tutoriais que resultam em um game.

Jogos de naves que atiram foram clássicos que sempre divertiram pessoas no mundo todo.

Hoje daremos início ao desenvolvimento de um jogo ao estilo shoot em up.

 

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

Alguns leitores talvez perceberam que eu fiquei um pouco ausente do blog durante um tempo. Tive meus motivos ( PC deu problema =õ[ ) mas agora estou de volta. Relembrando, as últimas postagens minhas foram sobre o desenvolvimento de um jogo do zero. Caso você ainda não saiba do que se trata, confira nesse link. Finalizei a programação de um game ao estilo Pong nesse artigo.

Hoje nós iniciaremos o desenvolvimento de um jogo ao estilo shoot em up. Eu explicarei o que é um shoot em up game, criaremos um projeto no Cocos2d-x e teremos uma ideia de como o jogo ficará, esboçando a tela de gameplay. Partiu.

 

O que é um shoot em up game?

SonicWingsQuando, em um jogo, você pilota um avião de guerra que possui a missão de destruir com tiros praticamente tudo o que aparece, então, provavelmente, você está jogando um shoot em up game. Existem vários tipos de jogos assim, onde a câmera é atrás do avião, do lado ou sobre ele. Focaremos no desenvolvimento de um shoot em up game com visão superior, ou seja, você vê o cenário de uma perspectiva acima do avião e dos inimigos. Um bom exemplo é o Aero Fighters, da Mc O’River.

 

Criando o projeto e preparando o repositório

Para darmos início a programação do nosso próximo game, precisamos criar um projeto no Cocos2d-x. Crie um projeto com nome “Nave” e com pacote “com.Santy.Nave”. Escolha a API Android que melhor se adéqua a você. Caso você não saiba os procedimentos necessários para criar um projeto Cocos2d-x, veja como nesse tutorial.

RepositorioComo de praxe, o Cocos2d-x cria um projeto padrão com um código exemplo e uma imagem exemplo. Antes de sairmos corrigindo isso, vamos adicionar ao repositório do jogo o sprite sheet que será utilizado. O sprite sheet pode ser baixado aqui. Esse sprite sheet foi criado por mim com base em algumas imagens de sprite obtidas no site The Spriters Resource. Caso você queira saber como se cria arquivos de sprite sheet, veja como nesse tutorial.

Tendo em mãos os arquivos de sprite sheet, copie-os para a pasta “Resources”, ou seja, descompacte o arquivo de sprite sheet baixado na pasta. Nessa mesma pasta, existe um aquivo nomeado “HelloWorld.png”. Apague-o, pois não precisaremos dele.

Com isso, temos o projeto do jogo criado e o seu repositório completo.

 

Programando a tela de gameplay

Esse jogo terá um ponto que, pelo que eu me lembre, será a primeira vez que eu abordo em um tutorial. Todos os jogos que vínhamos desenvolvendo até então, eram programados com tela em orientação horizontal, também conhecida como landscape. Por padrão, os projetos Cocos2d-x recém-criados possuem essa orientação de tela. Porém, esse jogo será implementado com a orientação vertical de tela (portrait). Assim, teremos um espaço maior para os tiros percorrerem e o jogador morrerá menos no jogo.

PortraitLandscapePara você mudar a orientação da tela do aparelho, é necessário realizar uma modificação no código existente no arquivo “AndroidManifest.xml”. Assim, entre na pasta “proj.android”, a partir da raiz do projeto do jogo, e abra o arquivo citado. Ele é um arquivo XML descritor da sua aplicação. Adicione a seguinte linha de código:

android:screenOrientation=“portrait”

logo abaixo dessa:

android:theme=“@android:style/Theme.NoTitleBar.Fullscreen”

Isso é suficiente para deixar o jogo com a orientação de tela vertical.

Agora mexeremos no código. Primeiro, excluiremos todo o código padrão desnecessário para o jogo. Assim, abra o arquivo “HelloWorldScene.cpp” e apague todas as linhas de código existentes entre essas (inclusive):

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

// 3. add your codes below…

// add a label shows “Hello World”

// create and initialize a label

CCLabelTTF* pLabel = CCLabelTTF::create(“Hello World”, “Thonburi”, 34);

e essa (inclusive):

this->addChild(pSprite, 0);

No mesmo lugar onde você apagou o código acima, adicione as seguintes linhas:

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

CCSpriteFrameCache::sharedSpriteFrameCache()->addSpriteFramesWithFile(“spriteSheet.plist”);

CCSprite* fundo = CCSprite::createWithSpriteFrameName(“Mar1.png”);

if(size.width/size.height>fundo->boundingBox().size.width/fundo->boundingBox().size.height)

    fundo->setScale(size.width/fundo->boundingBox().size.width);

else

    fundo->setScale(size.height/fundo->boundingBox().size.height);

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

addChild(fundo,0);

CCArray* framesMar = CCArray::create();

framesMar->addObject(CCSpriteFrameCache::sharedSpriteFrameCache()->spriteFrameByName(“Mar1.png”));

framesMar->addObject(CCSpriteFrameCache::sharedSpriteFrameCache()->spriteFrameByName(“Mar2.png”));

framesMar->addObject(CCSpriteFrameCache::sharedSpriteFrameCache()->spriteFrameByName(“Mar3.png”));

framesMar->addObject(CCSpriteFrameCache::sharedSpriteFrameCache()->spriteFrameByName(“Mar4.png”));

fundo->runAction(CCRepeatForever::create(

  CCAnimate::create(CCAnimation::createWithSpriteFrames(framesMar,0.2))));

HelloWorld::jogador = CCSprite::createWithSpriteFrameName(“Jogador1.png”);

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

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

addChild(HelloWorld::jogador,2);

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->runAction(CCRepeatForever::create(

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

HelloWorld::sombraJogador = CCSprite::createWithSpriteFrameName(“JogadorSombra1.png”);

HelloWorld::sombraJogador->setScale((0.2*(15.0/32.0)*size.width)/HelloWorld::sombraJogador->boundingBox().size.width);

HelloWorld::sombraJogador->setPosition(ccp(size.width/2+0.15*size.width,0.15*size.height-0.07*size.width));

addChild(HelloWorld::sombraJogador,1);

CCArray* framesInimigos = CCArray::create();

framesInimigos->addObject(CCSpriteFrameCache::sharedSpriteFrameCache()->spriteFrameByName(“Inimigo1.png”));

framesInimigos->addObject(CCSpriteFrameCache::sharedSpriteFrameCache()->spriteFrameByName(“Inimigo2.png”));

framesInimigos->addObject(CCSpriteFrameCache::sharedSpriteFrameCache()->spriteFrameByName(“Inimigo3.png”));

for(int i=0;i<6;i++) {

    HelloWorld::inimigos[i] = CCSprite::createWithSpriteFrameName(“Inimigo1.png”);

    HelloWorld::inimigos[i]->setScale((0.2*size.width)/HelloWorld::inimigos[i]->boundingBox().size.width);

    HelloWorld::inimigos[i]->setVisible(false);

    addChild(HelloWorld::inimigos[i],2);

    HelloWorld::inimigos[i]->runAction(CCRepeatForever::create(

      CCAnimate::create(CCAnimation::createWithSpriteFrames(framesInimigos,0.1))));

    HelloWorld::sombraInimigos[i] = CCSprite::createWithSpriteFrameName(“InimigoSombra.png”);

    HelloWorld::sombraInimigos[i]->setScale((0.2*(14.0/32.0)*size.width)/HelloWorld::sombraInimigos[i]->boundingBox().size.width);

    HelloWorld::sombraInimigos[i]->setVisible(false);

    addChild(HelloWorld::sombraInimigos[i],1);

}

HelloWorld::inimigos[0]->setPosition(ccp(size.width/2,0.9*size.height));

HelloWorld::inimigos[0]->setVisible(true);

HelloWorld::sombraInimigos[0]->setPosition(ccp(size.width/2+0.15*size.width,0.9*size.height-0.07*size.width));

HelloWorld::sombraInimigos[0]->setVisible(true);

MarSe você analisar o código inserido, perceberá que, primeiramente, eu abri o sprite sheet para que pudéssemos incluir sprites no jogo. Logo após, nós criamos um sprite de fundo que toma a tela inteira e que realiza uma animação da água do mar. Depois, nós criamos um sprite que representa a nave do jogador e a fizemos animar. Adicionamos logo do lado um sprite para a sombra dela. Seguindo, nós criamos seis sprites que representam os inimigos e os animamos também. Note que esses sprites ficam invisíveis de início. Também criamos os sprites das sombras dos inimigos. Para finalizar, nós posicionamos um desses sprites dos inimigos na parte superior da tela, posicionamos uma das sombras do lado do inimigo e os fizemos ficar visíveis ao jogador.

Os programadores mais experientes notarão que ainda falta uma modificação para que a compilação resulte sem erros. Precisamos declarar na classe “HelloWorld” as variáveis globais criadas. Assim, salve o arquivo “HelloWorldScene.cpp” e abra o arquivo “HelloWorldScene.h”. Adicione as seguintes linhas código:

cocos2d::CCSprite* jogador;

cocos2d::CCSprite* sombraJogador;

cocos2d::CCSprite* inimigos[6];

cocos2d::CCSprite* sombraInimigos[6];

logo abaixo dessa:

static cocos2d::CCScene* scene();

Prooooonto. Agora basta compilar o código e ver o resultado. Aparecerá um mar no fundo com o avião que será manipulado pelo jogador na parte inferior da tela e a sua sombra. Na parte superior, terá um helicóptero, que será o seu inimigo, também com a sua respectiva sombra. Tanto o avião do jogador quanto o helicóptero estão animando. A Figura 1 mostra como ficou a tela de gameplay.

Figura 1 - Gameplay do jogo
Figura 1 – Gameplay do jogo

Vimos nesse tutorial como é um jogo shoot em up. Criamos um projeto Cocos2d-x do nosso próximo jogo e programamos a tela de gameplay. Foi preciso modificar a orientação da tela do jogo para vertical e mudamos o código padrão para que pudéssemos ter uma ideia de como o jogo ficará.

No próximo tutorial nós veremos como fazer os inimigos aparecerem e se movimentarem de forma aleatória.

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.