Tutorial: Criando um jogo ao estilo Robot Unicorn Attack – Parte 3: Criando as plataformas

Nosso Sonic (créditos a SEGA) já salta … mas, não corre.

Ele precisa correr para que tenhamos a primeira versão jogável.

Movimentá-lo propicia a queda da plataforma e adiciona um desafio ao jogo.

Vimos no último tutorial como funciona o sistema de detecção de colisão entre o Sonic e a plataforma e vimos como fazê-lo parar de cair, caso ele a encontre durante a queda. Também implementamos um sistema de saltos que faz com que um pulo com o dedo encostado mais tempo na tela seja mais alto. Isso, porém, não adicionou qualquer desafio ao nosso jogo até agora, pelo fato do Sonic não se mover para frente e se manter sempre sobre a plataforma. Nesse tutorial, nós faremos o nosso personagem jogável correr, de forma que as plataformas se movimentem da direita para a esquerda e possibilitem a queda do personagem no buraco. Bora programar.

Para incluirmos essa funcionalidade no nosso game, precisaremos adicionar uma nova variável e um novo método na classe “HelloWorld”. Essa variável armazenará uma referência a uma segunda plataforma, para que sejam mostradas ao mesmo tempo na tela duas plataformas enquanto o Sonic está em transição de uma para a outra. O método adicionado é nomeado como “reposicionaChao” e possui a função de criar uma plataforma caso uma delas tenha desaparecido da tela. Para adicionarmos esses dois elementos na classe “HelloWorld”, abra o arquivo “HelloWorldScene.h” e adicione as seguintes linhas de código:

cocos2d::CCTMXTiledMap* plataforma2;

void reposicionaChao();

logo abaixo dessa:

cocos2d::CCTMXTiledMap* plataforma;

Salve esse arquivo. Agora, vamos estender e reposicionar a plataforma criada no primeiro tutorial. Isso será necessário porque serão mostradas somente duas plataformas ao mesmo tempo na tela. Dessa forma, precisaremos fazer com que elas sejam mais largas e possuam certa distância entre si, para que os buracos sejam bem distribuídos quando o Sonic começar a correr. Para aumentar a primeira plataforma, abra o arquivo “HelloWorldScene.cpp” e adicione a seguinte linha de código:

plataforma->layerNamed(“Camada1”)->setTileGID(1,ccp(4,0));

logo abaixo dessa:

plataforma->layerNamed(“Camada1”)->setTileGID(1,ccp(3,0));

Como eu disse anteriormente, precisamos estender as plataformas, mas também precisamos deixá-las bem espaçadas. Dessa forma, posicionaremos a plataforma criada anteriormente no lugar correto na tela, para que ela também tenha a distância correta da próxima. Assim sendo, modifique a seguinte linha de código:

plataforma->setPosition(ccp(size.width/4.8,size.height/2-plataforma->boundingBox().size.height-sonic->boundingBox().size.height/2));

de forma que ela fique assim:

plataforma->setPosition(ccp(size.width/5,size.height/2-plataforma->boundingBox().size.height-sonic->boundingBox().size.height/2));

A próxima modificação que faremos consistirá na criação de uma nova plataforma e no posicionamento dela na tela de forma que, quando as plataformas comecem a se movimentar, a distância entre elas seja sempre a mesma. Adicione as seguintes linhas de código:

float dist = plataforma->getPositionX() + plataforma->boundingBox().size.width/4;

CCCallFunc* chao = CCCallFunc::create(this,callfunc_selector(HelloWorld::reposicionaChao));

plataforma->runAction(CCSequence::create(

CCMoveTo::create(dist/VELOCIDADE,ccp(plataforma->getPositionX()-dist,plataforma->getPositionY())),chao,NULL));

plataforma2 = CCTMXTiledMap::create(“plataforma.tmx”);

plataforma2->setScale((0.075*size.height)/plataforma2->boundingBox().size.height);

plataforma2->layerNamed(“Camada1”)->setTileGID(1,ccp(1,0));

plataforma2->layerNamed(“Camada1”)->setTileGID(1,ccp(2,0));

plataforma2->layerNamed(“Camada1”)->setTileGID(1,ccp(3,0));

plataforma2->layerNamed(“Camada1”)->setTileGID(1,ccp(4,0));

plataforma2->setPosition(ccp((4*size.width)/5,plataforma->getPositionY()));

addChild(plataforma2);

dist = plataforma2->getPositionX() + plataforma2->boundingBox().size.width/4;

plataforma2->runAction(CCSequence::create(

CCMoveTo::create(dist/VELOCIDADE,ccp(plataforma2->getPositionX()-dist,plataforma2->getPositionY())),chao,NULL));

logo abaixo dessa:

addChild(plataforma);

Se você for analisar esse último código adicionado, perceberá que eu fiz algo a mais, além de criar e posicionar a segunda plataforma. Perceba que eu fiz com que elas andassem na tela da direita para a esquerda por meio do método “runAction”. Elas se movimentarão conforme a velocidade especificada na constante que eu nomeei como “VELOCIDADE”. Além disso, quando as plataformas chegarem ao final da tela, será executado o método nomeado “reposicionaChao”, declarado anteriormente no arquivo “HelloWorldScene.h”. Dessa forma, adicione na primeira linha do arquivo “HelloWorldScene.cpp”, a declaração da constante “VELOCIDADE”, mostrada na linha abaixo:

#define VELOCIDADE 400

Agora adicione a implementação do método “reposicionaChao” no final do arquivo:

void HelloWorld::reposicionaChao() {

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

    CCTMXTiledMap* plat;

    if(HelloWorld::plataforma->getPositionX()<HelloWorld::plataforma2->getPositionX())

        plat = HelloWorld::plataforma;

    else

        plat = HelloWorld::plataforma2;

    plat->setPosition(ccp(size.width,plat->getPositionY()));

    float dist = plat->getPositionX() + plat->boundingBox().size.width/4;

    CCCallFunc* chao = CCCallFunc::create(this,callfunc_selector(HelloWorld::reposicionaChao));

    plat->runAction(CCSequence::create(

      CCMoveTo::create(dist/VELOCIDADE,ccp(plat->getPositionX()-dist,plat->getPositionY())),chao,NULL));

}

No método “reposicionaChao”, nós primeiramente verificamos qual plataforma chegou ao final da tela. Após isso, nós a reposicionamos para o lado direito da tela e fazemos com que ela se movimente até o lado esquerdo com a velocidade especificada pela constante nomeada “VELOCIDADE”, ou seja, 400 pixels por segundo. Se você for um programador mais experiente, perceberá que esse valor é muito relativo. Caso o jogo seja executado em um tablet, por exemplo, as plataformas se movimentarão mais devagar em relação à execução em um celular. Existe uma forma de tratar esse problema, porém esse não é o foco desse tutorial. Deixo isso como um exercício para vocês. =]

Agora, precisaremos repensar na detecção de colisão entre o personagem e as plataformas. Antes nós tínhamos uma versão onde o Sonic não se movimentava e o único tratamento necessário era vertical. Agora o problema é um pouco mais elaborado e precisaremos reprogramar a detecção de colisão entre o personagem, o chão e a resposta dessa colisão. Para que possamos entender melhor, dividiremos o problema em partes. Primeiramente, substitua as seguintes linhas de código:

if(s.intersectsRect(HelloWorld::plataforma->boundingBox())) {

    HelloWorld::gravidade = 0.0;

    HelloWorld::posicaoYInicial = HelloWorld::plataforma->getPositionY()+

    HelloWorld::plataforma->boundingBox().size.height+HelloWorld::sonic->boundingBox().size.height/2;

    HelloWorld::velocidadeYInicial = 0.0;

    HelloWorld::tempoCorrido = 0.0;

}

por essas:

if(gravidade!=0.0) {

    CCTMXTiledMap* plat = NULL;

    if(s.intersectsRect(HelloWorld::plataforma->boundingBox())&&

      HelloWorld::sonic->getPositionX()>=HelloWorld::plataforma->getPositionX()&&

      HelloWorld::sonic->getPositionX()<=HelloWorld::plataforma->getPositionX()+HelloWorld::plataforma->boundingBox().size.width/4)

        plat = HelloWorld::plataforma;

    if(s.intersectsRect(HelloWorld::plataforma2->boundingBox())&&

      HelloWorld::sonic->getPositionX()>=HelloWorld::plataforma2->getPositionX()&&

      HelloWorld::sonic->getPositionX()<=HelloWorld::plataforma2->getPositionX()+HelloWorld::plataforma2->boundingBox().size.width/4)

        plat = HelloWorld::plataforma2;

    if(plat!=NULL) {

        HelloWorld::gravidade = 0.0;

        HelloWorld::posicaoYInicial = plat->getPositionY()+plat->boundingBox().size.height+HelloWorld::sonic->boundingBox().size.height/2;

        HelloWorld::velocidadeYInicial = 0.0;

        HelloWorld::tempoCorrido = 0.0;

    }

}

Durante a execução do jogo, poderá haver dois momentos que o Sonic modifica a sua movimentação sem a interferência do jogador. O momento em que ele está caindo e encontra uma plataforma e o momento em que ele está andando sobre uma plataforma e encontra o fim dela. No primeiro momento, o personagem pode cair sobre a plataforma criada no primeiro tutorial ou sobre a que foi criada nesse tutorial. Note que é exatamente isso que o código acima processa. Primeiramente, é verificado em qual plataforma o Sonic caiu. Logo após, ele é reposicionado conforme a posição da plataforma que ele caiu. Se o personagem não caiu sobre nenhuma plataforma, nada é feito e o seu movimento é continuado.

Para finalizar o presente tutorial, basta realizarmos o tratamento do segundo evento citado no último parágrafo. Quando o personagem está correndo sobre uma plataforma e encontra o fim dela, é necessário adicionar gravidade ao ambiente para que o personagem caia. Dessa forma, adicione as seguintes linhas de código logo abaixo dessas adicionadas anteriormente:

else if((HelloWorld::sonic->getPositionX()>HelloWorld::plataforma->getPositionX()+HelloWorld::plataforma->boundingBox().size.width/4&&

  HelloWorld::sonic->getPositionX()<HelloWorld::plataforma2->getPositionX())||

  (HelloWorld::sonic->getPositionX()>HelloWorld::plataforma2->getPositionX()+HelloWorld::plataforma2->boundingBox().size.width/4&&

  HelloWorld::sonic->getPositionX()<HelloWorld::plataforma->getPositionX())) {

    HelloWorld::gravidade = -40*HelloWorld::sonic->boundingBox().size.height;

    HelloWorld::posicaoYInicial = HelloWorld::sonic->getPositionY();

    HelloWorld::velocidadeYInicial = 0.0;

    HelloWorld::tempoCorrido = 0.0;

}

Perceba que, primeiramente, o jogo verifica se o Sonic está entre as duas plataformas. Caso isso aconteça, é adicionada gravidade no personagem, que faz com que ele caia. Pronto, agora basta compilar e ver o resultado. Note que ainda não implementamos a animação do Sonic correndo nem saltando. Deixaremos isso para o próximo tutorial. A Figura 1 mostra o jogo em execução, para os curiosos de plantão.

Figura 1 - Jogo executando

Figura 1 – Jogo executando

Implementamos nesse tutorial a funcionalidade de criação de plataformas e movimentação do Sonic. Vimos que precisamos aumentar as plataformas e as posicionar de forma que elas tivessem uma certa distância entre si. Além disso, precisamos também implementar a detecção de colisão com as duas plataformas e que precisamos implementar uma queda quando o jogador chega no final de uma plataforma e cai no buraco. No próximo tutorial nós veremos como fazer para que as plataformas sejam criadas em alturas aleatórias.

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.

Send this to a friend