Tutorial: Desenvolvendo um Jogo de Batalha Naval em Cocos2d-x: Parte 2 – Gerando o cenário aleatoriamente

Iniciamos mais um conjunto de tutoriais que objetiva um jogo finalizado. Dessa vez nós programaremos um jogo de batalha naval em Cocos2d.

Hoje faremos com que o cenário seja criado aleatoriamente antes do início do jogo.

Parte 1 – Parte 2 – Parte 3Parte 4

No último tutorial, nós criamos o projeto Cocos2d-x do nosso jogo de batalha naval. Programamos a tela de gameplay adicionando um barco pequeno, um médio e um grande.

No tutorial de hoje nós programaremos a criação aleatória do cenário. Todos sabemos que o cenário deve ser diferente a cada nova execução do jogo. Isso porque o jogo se torna previsível e, consequentemente, sem diversão.

Criando o Cenário de Batalha Naval em Cocos2d-x

Primeiramente vamos pensar como funcionará a geração do cenário. Temos em mãos dois navios grandes, quatro navios médios e seis navios pequenos. A forma mais inteligente de distribuirmos os navios é, posicionar os grandes, depois os médios e, por último, os pequenos. Outro ponto importante é posicionar os navios perfeitamente dentro das células da grade de opções de tiro.

gradeDe inicio, é preciso declarar quatro métodos na classe “HelloWorld”. Um deles será responsável por gerar o cenário e os outros três serão responsáveis por posicionar os barcos grandes, médios e pequenos, respectivamente. Assim sendo, abra o arquivo “HelloWorldScene.h” e adicione as seguintes linhas de código:

void criaCenario();

void posicionaNaviosG();

void posicionaNaviosM();

void posicionaNaviosP();

logo abaixo dessa:

cocos2d::Sprite *naviosP[6];

Salve e feche esse arquivo. Começaremos com a implementação do método “criaCenario”. Abra o arquivo “HelloWorldScene.cpp” e adicione as seguintes linhas de código no final do arquivo.

void HelloWorld::criaCenario() {

    HelloWorld::posicionaNaviosG();

    HelloWorld::posicionaNaviosM();

    HelloWorld::posicionaNaviosP();

}

Não há muito o que explicar nesse método. Ele por si só já é autoexplicativo. Primeiro posicionamos os barcos grandes, depois os médios e pequenos. Vamos começar com a implementação do método “posicionaNaviosG”. Adivinhem o objetivo dele … No final do mesmo arquivo, adicione as seguintes linhas de código.

void HelloWorld::posicionaNaviosG() {

    int i;

    int p[2],lim[2][2];

    static long r = time(NULL);

    for(i=0;i<2;i++) {

        srand(r);

        r = rand();

        if(((float)r)/RAND_MAX<0.25) {

            HelloWorld::naviosG[i]->setRotation(0.0);

            lim[0][0] = 0;

            lim[0][1] = 1;

            lim[1][0] = 10;

            lim[1][1] = 5;

        } else if(((float)r)/RAND_MAX<0.5) {

            HelloWorld::naviosG[i]->setRotation(90.0);

            lim[0][0] = 1;

            lim[0][1] = 0;

            lim[1][0] = 9;

            lim[1][1] = 6;

        } else if(((float)r)/RAND_MAX<0.75) {

            HelloWorld::naviosG[i]->setRotation(180.0);

            lim[0][0] = 0;

            lim[0][1] = 1;

            lim[1][0] = 10;

            lim[1][1] = 5;

        } else {

            HelloWorld::naviosG[i]->setRotation(270.0);

            lim[0][0] = 1;

            lim[0][1] = 0;

            lim[1][0] = 9;

            lim[1][1] = 6;

        }

        srand(r);

        r = rand();

        p[0] = lim[0][0] + (lim[1][0]-lim[0][0])*((float)r)/RAND_MAX;

        if(p[0]==lim[1][0])

            p[0] = p[0]-1;

        srand(r);

        r = rand();

        p[1] = lim[0][1] + (lim[1][1]-lim[0][1])*((float)r)/RAND_MAX;

        if(p[1]==lim[1][1])

            p[1] = p[1]-1;

        HelloWorld::naviosG[i]->setVisible(true);

        HelloWorld::naviosG[i]->setPosition(HelloWorld::grade[p[1]][p[0]]->getPosition());

        if(HelloWorld::naviosG[0]->getBoundingBox().intersectsRect(HelloWorld::naviosG[1]->getBoundingBox()))

            i = i-1;

    }

}

iateNotem que posicionamos dois barcos grandes. Ao posicionar um deles, nós verificamos se o último está sobre primeiro. Caso isso aconteça, nós reposicionamos o último em outro lugar para evitar que um barco esteja sobre o outro. Cada posicionamento é realizado aleatoriamente, tanto na rotação do barco como na posição na grade. Percebam também que posicionamos de forma que os barcos estejam inteiramente na tela, evitando que eles sejam apresentados pela metade. Nada muito complicado, na verdade.

Continuando, vamos implementar o método que posiciona os barcos médios. Adicione as seguintes linhas de código no final do mesmo arquivo.

void HelloWorld::posicionaNaviosM() {

    int i,j,flag;

    int p[2],lim[2][2];

    static long r = time(NULL);

    for(i=0;i<4;i++) {

        srand(r);

        r = rand();

        if(((float)r)/RAND_MAX<0.25) {

            HelloWorld::naviosM[i]->setRotation(0.0);

            lim[0][0] = 0;

            lim[0][1] = 1;

            lim[1][0] = 10;

            lim[1][1] = 6;

        } else if(((float)r)/RAND_MAX<0.5) {

            HelloWorld::naviosM[i]->setRotation(90.0);

            lim[0][0] = 1;

            lim[0][1] = 0;

            lim[1][0] = 10;

            lim[1][1] = 6;

        } else if(((float)r)/RAND_MAX<0.75) {

            HelloWorld::naviosM[i]->setRotation(180.0);

            lim[0][0] = 0;

            lim[0][1] = 1;

            lim[1][0] = 10;

            lim[1][1] = 6;

        } else {

            HelloWorld::naviosM[i]->setRotation(270.0);

            lim[0][0] = 1;

            lim[0][1] = 0;

            lim[1][0] = 10;

            lim[1][1] = 6;

        }

        srand(r);

        r = rand();

        p[0] = lim[0][0] + (lim[1][0]-lim[0][0])*((float)r)/RAND_MAX;

        if(p[0]==lim[1][0])

            p[0] = p[0]-1;

        srand(r);

        r = rand();

        p[1] = lim[0][1] + (lim[1][1]-lim[0][1])*((float)r)/RAND_MAX;

        if(p[1]==lim[1][1])

            p[1] = p[1]-1;

        HelloWorld::naviosM[i]->setVisible(true);

        if(HelloWorld::naviosM[i]->getRotation()==0.0||HelloWorld::naviosM[i]->getRotation()==180.0)

            HelloWorld::naviosM[i]->setPosition(Vec2(

              HelloWorld::grade[p[1]][p[0]]->getPositionX(),

              HelloWorld::grade[p[1]][p[0]]->getPositionY()+HelloWorld::grade[0][0]->getBoundingBox().size.height/2));

        else

            HelloWorld::naviosM[i]->setPosition(Vec2(

              HelloWorld::grade[p[1]][p[0]]->getPositionX()-HelloWorld::grade[0][0]->getBoundingBox().size.width/2,

              HelloWorld::grade[p[1]][p[0]]->getPositionY()));

        flag = 0;

        for(j=0;j<2;j++)

            if(HelloWorld::naviosM[i]->getBoundingBox().intersectsRect(HelloWorld::naviosG[j]->getBoundingBox())) {

                flag = 1;

                break;

            }

        if(!flag)

            for(j=0;j<i;j++)

                if(HelloWorld::naviosM[i]->getBoundingBox().intersectsRect(HelloWorld::naviosM[j]->getBoundingBox())) {

                    flag = 1;

                    break;

                }

        if(flag)

            i = i-1;

    }

}

NcolisaBarcoso posicionamento dos barcos médios, nós levamos em consideração outros dois pontos: o tamanho menor dos barcos e a possibilidade deles serem posicionados sobre barcos médios ou grandes. Dessa forma, houve uma pequena alteração na hora de posicionar os barcos e na hora de identificar se o último barco foi posicionado sobre outro. Um método parecido ao anterior e sem muita programação complicada.

Agora implementaremos o método “posicionaNaviosP”, para os navios pequenos. Adicione as seguintes linhas de código no final do mesmo arquivo.

void HelloWorld::posicionaNaviosP() {

    int i,j,flag;

    int p[2];

    static long r = time(NULL);

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

        srand(r);

        r = rand();

        if(((float)r)/RAND_MAX<0.25)

            HelloWorld::naviosP[i]->setRotation(0.0);

        else if(((float)r)/RAND_MAX<0.5)

            HelloWorld::naviosP[i]->setRotation(90.0);

        else if(((float)r)/RAND_MAX<0.75)

            HelloWorld::naviosP[i]->setRotation(180.0);

        else

            HelloWorld::naviosP[i]->setRotation(270.0);

        srand(r);

        r = rand();

        p[0] = 10*((float)r)/RAND_MAX;

        if(p[0]==10)

            p[0] = 9;

        srand(r);

        r = rand();

        p[1] = 6*((float)r)/RAND_MAX;

        if(p[1]==6)

            p[1] = 5;

        HelloWorld::naviosP[i]->setVisible(true);

        HelloWorld::naviosP[i]->setPosition(HelloWorld::grade[p[1]][p[0]]->getPosition());

        flag = 0;

        for(j=0;j<2;j++)

            if(HelloWorld::naviosP[i]->getBoundingBox().intersectsRect(HelloWorld::naviosG[j]->getBoundingBox())) {

                flag = 1;

                break;

            }

        if(!flag)

            for(j=0;j<4;j++)

                if(HelloWorld::naviosP[i]->getBoundingBox().intersectsRect(HelloWorld::naviosM[j]->getBoundingBox())) {

                    flag = 1;

                    break;

                }

        if(!flag)

            for(j=0;j<i;j++)

                if(HelloWorld::naviosP[i]->getBoundingBox().intersectsRect(HelloWorld::naviosP[j]->getBoundingBox())) {

                    flag = 1;

                    break;

                }

        if(flag)

            i = i-1;

    }

}

Esse método é ainda mais simples em comparação aos outros dois, pelo fato de que os barcos pequenos utilizam apenas uma célula da grade. Seu funcionamento é baseado em rotacionar, posicionar aleatoriamente o barco na grade e verificar se ele foi posicionado sobre um barco grande, médio ou pequeno.

A funcionalidade de criação aleatória do cenário foi implementada. Agora, basta chamar o método “criaCenario” ao iniciar o jogo para que tudo esteja finalizado. Vamos deixar a tela limpa, excluindo código responsável por criar a tela de gameplay adicionado no último tutorial, e incluir a chamada do método “criaCenario”. No mesmo arquivo, remova todo o código existente entre essa linha (inclusive):

HelloWorld::grade[1][4]->setVisible(false);

e essa linha (inclusive):

HelloWorld::grade[4][2]->setSpriteFrame(“Erro.png”);

Para finalizar, no mesmo lugar onde foi retirado o código acima, adicione a seguinte linha de código.

HelloWorld::criaCenario();

Prooonto… Agora temos a funcionalidade de criação aleatória do cenário implementada no nosso jogo de batalha naval. Note que, a cada execução do jogo, é criado um cenário distinto com os barcos em diferentes direções e posições. Para os curiosos de plantão, vejam um cenário criado aleatoriamente na Figura 1.

Figura 1 - Cenário criado aleatoriamente
Figura 1 – Cenário criado aleatoriamente

 

Vimos no tutorial de hoje como programar o sistema de geração aleatória de cenário do nosso jogo de batalha naval. Implementamos quatro métodos: um responsável por gerar o cenário e os outros três por posicionar os navios. Após a implementação dos métodos, nós chamamos o método responsável por gerar o cenário durante a inicialização do jogo.

No próximo tutorial, nós iniciaremos a implementação de toques na tela. Faremos com que os navios iniciem no modo invisível e os faremos aparecerem quando jogador clicar em um navio ainda não abatido.

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