Tutorial: Criando um jogo ao estilo Angry Birds – Parte 2: Criando uma fase

Criamos um projeto no Cocos2d-x para o nosso próximo jogo, que promete ser muito bom.

Vimos uma fase simples para demonstrar como ficará quando o jogo for executado. Porém, não podíamos modificar facilmente essa fase porque ela era implementada direto no código.

Vamos mudar isso e criar nossas próprias fases de forma mais fácil.

No último tutorial nós criamos um projeto no Cocos2d-x para o nosso game e testamos os diferentes Sprites que serão adicionados nas fases. Fizemos uma tela inicial para termos ideia de como ficará o jogo antes mesmo de executarmos. Eu falei que no tutorial de hoje implementaríamos a física do arremesso da bala do canhão. Porém, eu achei mais interessante deixar esse conjunto de tutoriais mais dinâmico, possibilitando a vocês criarem as suas próprias fases. Dessa forma, hoje eu ensinarei vocês a utilizarem a ferramenta Tiled para criar seus próprios cenários. Vamos deixar a implementação da física para depois e vamos abrir asas a imaginação para criar cenários diferentes. =]

 

Criando uma fase no Tiled

Antes de tudo, como vamos criar uma fase na ferramenta Tiled, você precisa tê-la instalada no seu computador. Se você não faz ideia de qual é essa ferramenta ou para que ela serve, leia o conjunto de tutoriais que eu fiz sobre ela. Explica o que é o Tiled e mostra as principais funcionalidades dele, sendo algumas dessas utilizadas nesse tutorial. Caso seja necessário, acesse os tutoriais começando por esse.

Se você já está um passo a frente e já o conhece bem, vamos dar início à criação das nossas fases. Abra o Tiled e crie um novo projeto clicando no botão “Novo”, localizado na parte esquerda da barra de ferramentas, logo abaixo do menu superior. Na tela que abrir, deixe a “Orientação” como “Ortogonal”; a “Largura” e a “Altura” do “Tamanho do Mapa” como 1 tile de altura e 1 tile de largura; a “Largura” e a “Altura” do “Tamanho do Tile” como 470 px de largura e 320 px de altura e clique em “Ok”. A Figura 1 mostra como a tela ficará.

Figura 1 - Criando o projeto no Tiled

Figura 1 – Criando o projeto no Tiled

Acabamos de criar um mapa com somente um tile, que possui largura de 470 pixels e altura de 320 pixels. Isso porque adicionaremos nesse tile a imagem de fundo do jogo para que possamos ter uma deia do lugar onde colocaremos cada objeto na fase. Para colocarmos o fundo da fase nesse tile, precisamos incluir no projeto um tileset com o fundo e precisamos colocar o tile de fundo no único tile que o mapa possui. Para incluirmos o tileset, clique no menu superior “Mapa” e selecione “Novo Tileset…”. Abrirá a janela mostrada na Figura 2. Em “Nome”, dê um nome qualquer, eu nomeei como “TilesetFundo”. Em “Imagem”, selecione o arquivo de extensão PNG do sprite sheet que você baixou do último tutorial. Para as outras opções na janela, preencha conforme mostrado na Figura 2. Clique em “Ok”.

Figura 2 - Adicionando um tileset

Figura 2 – Adicionando um tileset

O tileset foi adicionado no projeto, mas único tile do nosso mapa ainda não está preenchido. Para isso, selecione o único tile do tileset adicionado, na parte inferior direita da tela principal, e pinte esse tile no tile do nosso mapa, no centro da tela principal. Note que temos o fundo do nosso jogo no mapa de tiles que criamos.

Agora vamos criar a fase inserindo objetos rígidos nela. Como vimos no tutorial passado, temos três tipos diferentes de objetos que podemos adicionar para criar a nossa estrutura. Esses objetos são: o bloco de pedra, o bloco de madeira e a viga. Para separarmos esses objetos no código, precisamos criar um conjunto de objetos no mapa para cada um desses tipos de objetos citados.

Criaremos, primeiramente, o conjunto de blocos de pedra. Para isso, clique com o botão direito do mouse na janela de “Camadas”, no canto superior direito da tela principal, selecione “Adicionar Camada de Objetos” e a nomeie como “Pedras”. Selecione essa camada e adicione retângulos que representam as pedras que estarão na estrutura da fase. Para adicionar retângulos, clique no botão “Inserir Retângulo (R)”, localizado na barra de ferramentas, logo abaixo do menu superior. Com essa opção selecionada, é possível adicionar retângulos. Tenha bom senso de deixar esses retângulos o mais quadrado possível. Isso porque os Sprites são quadrados. Após você inserir os retângulos das pedras, crie um conjunto de objetos com o nome “Madeiras” e adicione os blocos de madeira com retângulos, assim como você fez para os blocos de pedra. Agora, adicione um conjunto de objetos nomeado como “Vigas”. Inclua retângulos em forma de vigas onde você deseja. Novamente tenha o bom senso de adicionar vigas com altura grande e largura pequena ou com largura grande e altura pequena. Isso porque utilizaremos o Sprite de viga no jogo. Acabamos de modelar a estrutura que protegerá o vaso da bala de canhão.

Agora incluiremos os dois objetos principais do jogo: o canhão e o vaso a ser quebrado. Para isso, adicione um conjunto de objetos chamado “Parametros”. Inclua nesse conjunto de objetos um retângulo que representará o vaso. Coloque-o em algum lugar da sua estrutura formada de blocos de pedra, madeira e vigas. Novamente, tenha o bom senso de criar um vaso mais eu menos em forma de quadrado. Após você posicionar o retângulo que representa o vaso, clique com o botão direito do mouse sobre o retângulo e selecione a opção “Propriedades do Objeto…”. Em “Nome”, digite “Vaso” e clique em “Ok”. Acabamos de inserir o vaso na fase. Ainda nesse mesmo conjunto de objetos, adicione outro retângulo onde você quer que o canhão esteja localizado na fase. Esse retângulo é um pouco diferente. A ponta de trás do canhão estará localizada bem no centro do retângulo adicionado e a altura e largura desse retângulo pouco importa para o jogo. Então, posicione esse retângulo tomando esse cuidado. Clique com o botão direito sobre o retângulo adicionado e selecione a opção “Propriedades do Objeto”. Em “Nome”, coloque “Canhao” e adicione uma nova propriedade nesse objeto. Para isso, clique em “<nova propriedade>” e digite “orientacao”. Clique logo ao lado e digite “1”. Isso existe porque, se você quiser mudar a orientação do canhão, ou seja, fazê-lo apontar para a esquerda, você coloca o valor “0” em “orientação”. Como queremos que ele aponte para a direita, então coloque o valor “1”. Pronto, a sua fase está pronta para ser aberta no jogo. Para se ter uma comparação com o resultado final, a minha fase ficou no Tiled assim como mostra a Figura 3.

Figura 3 - Fase criada no Tiled

Figura 3 – Fase criada no Tiled

Salve o projeto em um arquivo com o nome “fase1.tmx” e o coloque na pasta “Resources”. Vamos ao código. o

 

Abrindo a fase criada no Cocos2d-x

Vamos abrir a nossa fase no aparelho. Volto a lembrar que, para você fazer esse tutorial, é necessário que você já tenha feito o anterior. Abra o arquivo “HelloWorldScene.h” e adicione as seguintes linhas de código:

cocos2d::CCArray* objetosRigidosSprites;

cocos2d::CCSprite* canhao;

cocos2d::CCSprite* suporteCanhao;

cocos2d::CCSprite* vaso;

logo abaixo dessa:

static cocos2d::CCScene* scene();

Adicionamos uma referência para os objetos pertencentes à estrutura que protege o vaso. Perceba que temos uma lista de objetos e você verá posteriormente que incluiremos Sprites nessa lista. Criamos também uma referência para o Sprite do canhão, uma para o Sprite do suporte do canhão e uma para o Sprite do vaso. Ainda não utilizaremos esses objetos em diferentes métodos da classe HelloWorld, porém, quando o nosso jogo tomar corpo, precisaremos ter uma referência deles para podermos manipulá-los. Agora abra o arquivo “HelloWorldScene.cpp” e remova todo o código entre essa linha (inclusive):

CCSprite* suporteCanhao = CCSprite::createWithSpriteFrameName(“SuporteCanhao.png”);

e essa (inclusive):

addChild(seta);

Isso que excluímos tinha como função mostrar aquela tela inicial de teste do primeiro tutorial. Como vamos abrir uma fase própria, precisamos eliminar tudo o que é inserido na tela até agora. Nesse mesmo lugar, onde foi excluído o código, adicione as seguintes linhas de código:

int i;

float x,y,largura,altura;

HelloWorld::objetosRigidosSprites = CCArray::create();

HelloWorld::objetosRigidosSprites->retain();

CCTMXTiledMap* tiledMap = CCTMXTiledMap::create(“fase1.tmx”);

CCTMXObjectGroup* camadaObjetos = tiledMap->objectGroupNamed(“Vigas”);

CCArray* objetos = camadaObjetos->getObjects();

CCDictionary* descricao;

CCSprite* spriteObjeto;

for(i=0;i<objetos->count();i++) {

    descricao = static_cast<CCDictionary*>(objetos->objectAtIndex(i));

    x = descricao->valueForKey(“x”)->floatValue();

    y = descricao->valueForKey(“y”)->floatValue();

    largura = descricao->valueForKey(“width”)->floatValue();

    altura = descricao->valueForKey(“height”)->floatValue();

    spriteObjeto = CCSprite::createWithSpriteFrameName(“Viga.png”);

    spriteObjeto->setScaleX(fundo->getScale()*(fmaxf(largura,altura)/fmaxf(spriteObjeto->boundingBox().size.width,spriteObjeto->boundingBox().size.height)));

    spriteObjeto->setScaleY(fundo->getScale()*(fminf(largura,altura)/fminf(spriteObjeto->boundingBox().size.width,spriteObjeto->boundingBox().size.height)));

    if(largura<altura)

        spriteObjeto->setRotation(90);

    spriteObjeto->setPosition(ccp(fundo->getScale()*x + spriteObjeto->boundingBox().size.width/2,fundo->getScale()*y + spriteObjeto->boundingBox().size.height/2));

    HelloWorld::objetosRigidosSprites->addObject(spriteObjeto);

    addChild(spriteObjeto);

}

camadaObjetos = tiledMap->objectGroupNamed(“Madeiras”);

objetos = camadaObjetos->getObjects();

for(i=0;i<objetos->count();i++) {

    descricao = static_cast<CCDictionary*>(objetos->objectAtIndex(i));

    x = descricao->valueForKey(“x”)->floatValue();

    y = descricao->valueForKey(“y”)->floatValue();

    largura = descricao->valueForKey(“width”)->floatValue();

    altura = descricao->valueForKey(“height”)->floatValue();

    spriteObjeto = CCSprite::createWithSpriteFrameName(“BlocoMadeira.png”);

    spriteObjeto->setScaleX(fundo->getScale()*(largura/spriteObjeto->boundingBox().size.width));

    spriteObjeto->setScaleY(fundo->getScale()*(altura/spriteObjeto->boundingBox().size.height));

    spriteObjeto->setPosition(ccp(fundo->getScale()*x + spriteObjeto->boundingBox().size.width/2,fundo->getScale()*y + spriteObjeto->boundingBox().size.height/2));

    HelloWorld::objetosRigidosSprites->addObject(spriteObjeto);

    addChild(spriteObjeto);

}

camadaObjetos = tiledMap->objectGroupNamed(“Pedras”);

objetos = camadaObjetos->getObjects();

for(i=0;i<objetos->count();i++) {

    descricao = static_cast<CCDictionary*>(objetos->objectAtIndex(i));

    x = descricao->valueForKey(“x”)->floatValue();

    y = descricao->valueForKey(“y”)->floatValue();

    largura = descricao->valueForKey(“width”)->floatValue();

    altura = descricao->valueForKey(“height”)->floatValue();

    spriteObjeto = CCSprite::createWithSpriteFrameName(“BlocoPedra.png”);

    spriteObjeto->setScaleX(fundo->getScale()*(largura/spriteObjeto->boundingBox().size.width));

    spriteObjeto->setScaleY(fundo->getScale()*(altura/spriteObjeto->boundingBox().size.height));

    spriteObjeto->setPosition(ccp(fundo->getScale()*x + spriteObjeto->boundingBox().size.width/2,fundo->getScale()*y + spriteObjeto->boundingBox().size.height/2));

    HelloWorld::objetosRigidosSprites->addObject(spriteObjeto);

    addChild(spriteObjeto);

}

camadaObjetos = tiledMap->objectGroupNamed(“Parametros”);

descricao = camadaObjetos->objectNamed(“Vaso”);

x = descricao->valueForKey(“x”)->floatValue();

y = descricao->valueForKey(“y”)->floatValue();

largura = descricao->valueForKey(“width”)->floatValue();

altura = descricao->valueForKey(“height”)->floatValue();

HelloWorld::vaso = CCSprite::createWithSpriteFrameName(“Vaso.png”);

HelloWorld::vaso->setScaleX(fundo->getScale()*(largura/HelloWorld::vaso->boundingBox().size.width));

HelloWorld::vaso->setScaleY(fundo->getScale()*(altura/HelloWorld::vaso->boundingBox().size.height));

HelloWorld::vaso->setPosition(ccp(fundo->getScale()*x + HelloWorld::vaso->boundingBox().size.width/2,fundo->getScale()*y + HelloWorld::vaso->boundingBox().size.height/2));

addChild(HelloWorld::vaso);

descricao = camadaObjetos->objectNamed(“Canhao”);

x = descricao->valueForKey(“x”)->floatValue();

y = descricao->valueForKey(“y”)->floatValue();

HelloWorld::suporteCanhao = CCSprite::createWithSpriteFrameName(“SuporteCanhao.png”);

HelloWorld::suporteCanhao->setScale(fundo->getScale());

if(descricao->valueForKey(“orientacao”)->intValue()==0) {

    HelloWorld::suporteCanhao->setFlipX(true);

    HelloWorld::suporteCanhao->setAnchorPoint(ccp(1,0));

}

else

    HelloWorld::suporteCanhao->setAnchorPoint(ccp(0,0));

HelloWorld::suporteCanhao->setPosition(ccp(fundo->getScale()*x,fundo->getScale()*y));

HelloWorld::canhao = CCSprite::createWithSpriteFrameName(“Canhao.png”);

HelloWorld::canhao->setScale(fundo->getScale());

if(descricao->valueForKey(“orientacao”)->intValue()==0) {

    HelloWorld::canhao->setFlipX(true);

    HelloWorld::canhao->setAnchorPoint(ccp(1 – 18.0/62.0,20.0/47.0));

    HelloWorld::canhao->setPosition(ccp(HelloWorld::suporteCanhao->getPositionX() – (22.0/44.0)*HelloWorld::suporteCanhao->boundingBox().size.width,HelloWorld::suporteCanhao->getPositionY() + (31.0/38.0)*HelloWorld::suporteCanhao->boundingBox().size.height));

    HelloWorld::canhao->setRotation(35);

}

else {

    HelloWorld::canhao->setAnchorPoint(ccp(18.0/62.0,20.0/47.0));

    HelloWorld::canhao->setPosition(ccp(HelloWorld::suporteCanhao->getPositionX() + (22.0/44.0)*HelloWorld::suporteCanhao->boundingBox().size.width,HelloWorld::suporteCanhao->getPositionY() + (31.0/38.0)*HelloWorld::suporteCanhao->boundingBox().size.height));

    HelloWorld::canhao->setRotation(-35);

}

addChild(HelloWorld::canhao);

addChild(HelloWorld::suporteCanhao);

No início do código nós criamos a lista de Sprites que utilizaremos em outros tutoriais. Logo após, nós abrimos o arquivo “fase1.tmx”, criado no Tiled. Após isso, criamos um Sprite para cada objeto do conjunto de objetos nomeado “Vigas”. Depois de criar, redimensionamos as suas larguras e as suas alturas, posicionamos no lugar correto na tela e rotacionamos, caso o objeto (viga) esteja em pé. Logo após, nós adicionamos o Sprite criado na lista de Sprites e o adicionamos na tela. Perceba que fizemos a mesma coisa para o conjunto de objetos nomeado “Madeiras” e para o conjunto nomeado “Pedras”, com exceção da rotação, que é desnecessária para esses tipos de objetos. Logo após, nós criamos, escalamos, posicionamos e adicionamos na tela o Sprite referente ao vaso. Por fim, fizemos a mesma coisa com o canhão e com o seu suporte. Existe um pequeno tratamento para o canhão que o faz apontar para a esquerda, caso a orientação seja igual a “0”, ou para a direita, caso a orientação seja igual a “1”. Agora compile o código e execute o programa. Note que a mesma estrutura criada por você será apresentada na tela do aparelho. A estrutura montada por mim, mostrada na Figura 3, executou no aparelho assim como é mostrado na Figura 4. Legal ver a sua fase criada no Tiled sendo executada no aparelho, né?! Agora criem suas próprias fases, fiéis leitores. =]

Figura 4 - Jogo executando a fase criada

Figura 4 – Jogo executando a fase criada

Nesse tutorial nós vimos como criar uma fase para o nosso jogo ao estilo Angry Birds (créditos a Rovio) na ferramenta Tiled. Vimos que precisávamos separar cada objeto conforme o seu grupo (bloco de pedra, bloco de madeira e viga) e também que existiam dois elementos que não poderiam faltar em nenhum mapa, o canhão e o vaso. Esses objetos foram incluídos dentro de outro conjunto de objetos obrigatórios (grupo “Parâmetros”). Incluímo-os, nomeamos e adicionamos uma propriedade no canhão que define para qual lado ele aponta. Logo após, nós abrimos o arquivo criado no código e incluímos cada objeto na sua respectiva localização e com o seu respectivo tamanho. No próximo tutorial incluiremos a física de objetos rígidos, ou seja, agora sim eles agirão como blocos de pedra, blocos de madeira, vigas e vasos.

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