Tutorial: Criando um jogo ao estilo Flappy Bird – Parte 2: Fazendo o pato voar

Tivemos uma ideia de como ficará a parte gráfica do nosso game ao estilo Flappy Bird.

Porém, uma imagem estática não diz nada sobre como ficará a sua jogabilidade.

No tutorial de hoje vamos implementar o voo do pato.

No último tutorial, nós criamos o projeto do nosso jogo no Cocos2d-x e vimos como funcionava o mecanismo do jogo Flappy Bird (créditos a .GEARS). Criamos um sprite sheet e testamos os Sprites para termos uma ideia de como ficará o jogo na sua versão final. Daremos continuidade na implementação do game programando a funcionalidade de bater as asas do pato. Obviamente, ainda não o faremos andar para frente, mas faremos com que o pato bata as asas de forma que ele possa se manter no ar, por meio de consecutivos toques na tela. Partiu.

Primeiramente, vamos editar o arquivo “HelloWorldScene.h” para que possamos programar as funcionalidades citadas. Abra-o e adicione as seguintes linhas de código:

#include “Box2D/Box2D.h”

#define PTM_RATIO 40

logo abaixo dessa:

#define __HELLOWORLD_SCENE_H__

No mesmo arquivo, adicione as seguintes linhas de código:

b2World* mundoFisico;

b2Body* corpoPato;

cocos2d::CCSprite* pato;

void atualiza(float dt);

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

logo abaixo dessa:

static cocos2d::CCScene* scene();

Criamos nessas linhas de código, três variáveis indispensáveis ao nosso game: uma que armazena uma referência ao mundo físico do Box2D (mundoFisico), uma para o corpo rígido que representa o pato no mundo do Box2D (corpoPato) e uma que referencia o Sprite do pato (pato). Todas são importantes para que possamos ter domínio sobre o pato no mundo físico e fazer as animações no mundo do Cocos2d-x. Adicionamos também dois protótipos de métodos, um responsável por processar as mudanças no ambiente em cada quadro (atualiza) e um responsável por gerenciar toques na tela (ccTouchesBegan). Como a única interação com o jogo é por meio do toque na tela, esse método se faz indispensável para que o pato bata as asas e se mantenha no ar.

Salve e feche o arquivo “HelloWorldScene.h”. Abra o arquivo “HelloWorldScene.cpp” e modifique a seguinte linha de código:

CCSprite* pato = CCSprite::createWithSpriteFrameName(“Passaro1.png”);

para isso:

pato = CCSprite::createWithSpriteFrameName(“Passaro1.png”);

Quando escrevemos essa linha de código no último tutorial, não pensamos na ideia do Sprite “pato” fazer parte de uma das variáveis necessárias para manipular o pato durante o jogo. Criamos, nesse tutorial, uma variável que armazena uma referência para o Sprite do pato e a inicializamos nessa linha de código. Se não houvesse essa modificação, a variável que criamos seria inutilizada. No mesmo arquivo, adicione as seguintes linhas de código:

HelloWorld::mundoFisico = new b2World(b2Vec2(0.0f,-10.0));

b2BodyDef corpoChaoDefinicao;

corpoChaoDefinicao.type = b2_staticBody;

corpoChaoDefinicao.position.Set(0.0,0.0);

b2Body* corpoChao = HelloWorld::mundoFisico->CreateBody(&corpoChaoDefinicao);

b2EdgeShape chaoGeometria;

chaoGeometria.Set(b2Vec2(0.0,0.0),b2Vec2(size.width/PTM_RATIO,0.0));

b2FixtureDef cascaChao;

cascaChao.shape = &chaoGeometria;

corpoChao->CreateFixture(&cascaChao);

b2BodyDef corpoTopoDefinicao;

corpoTopoDefinicao.type = b2_staticBody;

corpoTopoDefinicao.position.Set(0.0,size.height/PTM_RATIO);

b2Body* corpoTopo = HelloWorld::mundoFisico->CreateBody(&corpoTopoDefinicao);

b2EdgeShape topoGeometria;

topoGeometria.Set(b2Vec2(0.0,0.0),b2Vec2(size.width/PTM_RATIO,0.0));

b2FixtureDef cascaTopo;

cascaTopo.shape = &topoGeometria;

corpoTopo->CreateFixture(&cascaTopo);

b2BodyDef corpoPatoDefinicao;

corpoPatoDefinicao.type = b2_dynamicBody;

corpoPatoDefinicao.position.Set(HelloWorld::pato->getPositionX()/PTM_RATIO,HelloWorld::pato->getPositionY()/PTM_RATIO);

HelloWorld::corpoPato = HelloWorld::mundoFisico->CreateBody(&corpoPatoDefinicao);

b2CircleShape circulo;

circulo.m_radius = (0.25*HelloWorld::pato->boundingBox().size.width)/PTM_RATIO;

b2FixtureDef cascaPato;

cascaPato.shape = &circulo;

cascaPato.density = 1.0;

HelloWorld::corpoPato->CreateFixture(&cascaPato);

schedule(schedule_selector(HelloWorld::atualiza));

setTouchEnabled(true);

HelloWorld::corpoPato->ApplyLinearImpulse(b2Vec2(0.0,3.0),HelloWorld::corpoPato->GetWorldCenter());

logo abaixo dessa:

addChild(cabeca4);

Nesse último bloco de código, nós criamos um mundo no Box2D com gravidade para baixo e armazenamos uma referência dele na variável “mundoFisico”, criada anteriormente no arquivo “HelloWorldScene.h”. Criamos um corpo rígido estático para o chão e outro para o teto, fazendo com que o pato não possa passar por cima nem por baixo dos tubos. Logo após, nós criamos um corpo rígido dinâmico que representará o pato no mundo físico do Box2D. Esse corpo possui a forma de um círculo e é posicionado exatamente no centro do Sprite do pato. A Figura 1 mostra onde está o corpo rígido do pato no cenário. Por último, apontamos ao Cocos2d-x qual é o método responsável por executar os processamentos necessários para criar cada quadro (atualiza), acionamos a função de toque na tela e aplicamos no pato um impulso inicial apontado para cima, para que ele não inicie o jogo caindo.

Figura 1 - Corpo rígido circular no centro do pato

Figura 1 – Corpo rígido circular no centro do pato

Para finalizar, adicione as seguintes linhas de código ao final do arquivo:

void HelloWorld::atualiza(float dt) {

    HelloWorld::mundoFisico->Step(dt,3,2);

    HelloWorld::pato->setPosition(ccp(HelloWorld::corpoPato->GetPosition().x*PTM_RATIO,HelloWorld::corpoPato->GetPosition().y*PTM_RATIO));

}

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

    HelloWorld::corpoPato->SetLinearVelocity(b2Vec2(0.0,0.0));

    HelloWorld::corpoPato->ApplyLinearImpulse(b2Vec2(0.0,3.0),HelloWorld::corpoPato->GetWorldCenter());

    HelloWorld::pato->cleanup();

    HelloWorld::pato->setDisplayFrame(CCSpriteFrameCache::sharedSpriteFrameCache()->spriteFrameByName(“Passaro1.png”));

    CCArray* spriteFrames = CCArray::create();

    spriteFrames->addObject(CCSpriteFrameCache::sharedSpriteFrameCache()->spriteFrameByName(“Passaro2.png”));

    spriteFrames->addObject(CCSpriteFrameCache::sharedSpriteFrameCache()->spriteFrameByName(“Passaro1.png”));

    HelloWorld::pato->runAction(CCAnimate::create(CCAnimation::createWithSpriteFrames(spriteFrames,0.1)));

}

Acabamos de implementar os dois métodos faltantes, um responsável por atualizar o ambiente entre dois quadros (atualiza) e outro responsável por processar um batido de asas do pato (ccTouchesBegan). Para atualizar o ambiente entre dois quadros, é necessário, por enquanto, apenas atualizar o mundo físico com base no tempo entre um quadro e o outro (dt) e posicionar o Sprite do pato exatamente onde se encontra o corpo rígido que o representa no mundo do Box2D. Quando o jogador encosta o dedo na tela, o jogo exclui qualquer velocidade do pato, para que o impulso seja aplicado como se ele estivesse parado no momento. Isso porque, quando o jogador encosta na tela, ele espera, pelo menos, que o pato suba um pouco. Aplicando um impulso para cima logo após excluirmos a velocidade do pato fará com que ele suba pelo menos um pouco, independente se ele esteja caindo em alta velocidade. Para finalizar esse método, nós iniciamos a animação de bater asas do pato. Simples, não? O resultado do tutorial de hoje é mostrado no vídeo a seguir.

Vimos nesse tutorial como incluir a funcionalidade de bater asas do pato. Para isso, tivemos que acionar o toque na tela do aparelho e excluir a velocidade do objeto rígido que representa o pato antes da submissão de um impulso para cima. Vimos a necessidade de criar outras três variáveis para que pudéssemos implementar essa função: uma que referencia o mundo físico do Box2D, uma que referencia o corpo rígido do pato e outra que referencia o Sprite do pato. No próximo tutorial, nós implementaremos o sistema aleatório de criação dos tubos. Não concluiremos o desenvolvimento do jogo já, porém teremos o núcleo do jogo quase pronto.

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