Até agora, a simulação física de movimentação das bolas de bilhar já acontece nos conformes.
Hoje nos preocuparemos com a jogabilidade.
Então, vamos implementar o sistema de tacadas.
Parte 1 – Parte 2 – Parte 3 – Parte 4 – Parte 5 – Parte 6
Implementamos no último tutorial a detecção de colisão entre os corpos rígidos das bolas de bilhar e da mesa. Quando uma colisão é detectada, os corpos rígidos passam a sofrer ação de uma nova força de atrito entre eles mesmos e a superfície da mesa. Por último, nós realizamos uma tacada para ver a simulação física das bolas rolando sobre a mesa de bilhar.
Continuaremos nesse tutorial a implementação do nosso jogo de sinuca incluindo a funcionalidade de tacadas. Como normalmente acontece em jogos de sinuca, o jogador tem a possibilidade de ver a direção por onde a bola branca vai rolar antes de realizar uma tacada. No nosso jogo não será diferente. Quando o jogador encostar o dedo na tela no momento de uma tacada, será apresentada uma seta que mostra a direção e a força com que tacada será realizada na bola branca.
Assim sendo, começaremos implementando o sistema de direcionamento e força da tacada.
Mirando a tacada
Como no padrão de jogos de sinuca, a tacada será mirada pelo jogador por meio de uma seta. Tal seta possuirá sua base na bola branca, onde será realizada a tacada, e apontará para o ponto na tela onde o jogador está encostando o dedo. Além disso, a seta terá o comprimento exato para que a sua base fique na bola branca e a sua ponta fique exatamente onde o jogador encosta o dedo. A direção da seta determinará a direção da tacada e o comprimento da seta determinará a força que a tacada terá.
Dada a explicação do sistema de tacadas, começaremos a codificar. Precisamos criar um atributo na classe “HelloWorld” que armazena a referência do sprite da seta. Também percebe-se que precisaremos de um objeto que detecta toques na tela. Em decorrência disso, precisamos implementar alguns métodos de toque na tela. Neste caso, precisaremos implementar os métodos “onTouchBegan”, “onTouchMoved” e “onTouchEnded”. Assim sendo, adicionaremos tais atributos e protótipos na classe “HelloWorld”. Abra o arquivo “HelloWorldScene.h” e adicione as seguintes linhas de código:
cocos2d::Sprite* seta;
cocos2d::EventListenerTouchOneByOne* toqueNaTela;
bool onTouchBegan(cocos2d::Touch *touch,cocos2d::Event *unused_event);
void onTouchMoved(cocos2d::Touch *touch,cocos2d::Event *unused_event);
void onTouchEnded(cocos2d::Touch *touch,cocos2d::Event *unused_event);
logo abaixo dessa:
cocos2d::PhysicsBody* mesaCorpo;
Com os atributos devidamente criados e com os protótipos declarados, agora podemos instanciar os objetos e implementar os métodos. Começaremos instanciando o objeto do sprite da seta. Para isso, abra o arquivo “HelloWorldScene.cpp” e adicione as seguintes linhas de código:
HelloWorld::seta = Sprite::createWithSpriteFrameName(“Seta.png”);
HelloWorld::seta->setScaleY(HelloWorld::mesa->getScale());
HelloWorld::seta->setAnchorPoint(Vec2(1.0,0.5));
HelloWorld::seta->setVisible(false);
addChild(HelloWorld::seta);
logo abaixo dessa:
HelloWorld::bolaBrancaCorpo->setVelocity(Vec2(-300.0,0.0));
Note que a seta inicia invisível, pois ela aparece apenas no momento em que o jogador encostar o dedo na tela antes de uma tacada. Além disso, nós modificamos o ponto âncora da seta, visto que, a partir de agora, nós a posicionaremos na tela a partir de sua base.
Agora implementaremos os métodos em que declaramos os protótipos anteriormente. Quando o jogador iniciar um toque na tela, será chamado o método “onTouchBegan”. Nele implementaremos a sub-rotina que apresenta a seta na tela, posiciona a base da seta sobre a bola branca, rotaciona a seta conforme a angulação entre o eixo horizontal e a posição do dedo do jogador e alonga a seta de forma que ela fique com a ponta sob o dedo do jogador. Para implementar o método “onTouchBegan”, adicione as seguintes linhas de código no final do arquivo “HelloWorldScene.cpp”.
bool HelloWorld::onTouchBegan(Touch *touch,Event *unused_event) {
Size size = Director::getInstance()->getVisibleSize();
Vec2 p = touch->getLocationInView();
p.y = size.height – p.y;
p.operator-=(HelloWorld::bolaBranca->getPosition());
if(p.y>0.0)
HelloWorld::seta->setRotation((Vec2::angle(Vec2(-1.0,0.0),p)*180.0)/M_PI);
else
HelloWorld::seta->setRotation(-(Vec2::angle(Vec2(-1.0,0.0),p)*180.0)/M_PI);
HelloWorld::seta->setScaleX(p.length()/HelloWorld::seta->getContentSize().width);
HelloWorld::seta->setPosition(HelloWorld::bolaBranca->getPosition());
HelloWorld::seta->setVisible(true);
return true;
}
Quando o jogador mover o dedo que já está encostado na tela, é necessário apenas atualizar a angulação e o comprimento da seta. Essa sub-rotina será realizada pelo método “onTouchMoved”. Para implementar esse método, adicione as seguintes linhas de código no final do arquivo “HelloWorldScene.cpp”.
void HelloWorld::onTouchMoved(Touch *touch,Event *unused_event) {
Size size = Director::getInstance()->getVisibleSize();
Vec2 p = touch->getLocationInView();
p.y = size.height – p.y;
p.operator-=(HelloWorld::bolaBranca->getPosition());
if(p.y>0.0)
HelloWorld::seta->setRotation((Vec2::angle(Vec2(-1.0,0.0),p)*180.0)/M_PI);
else
HelloWorld::seta->setRotation(-(Vec2::angle(Vec2(-1.0,0.0),p)*180.0)/M_PI);
HelloWorld::seta->setScaleX(p.length()/HelloWorld::seta->getContentSize().width);
}
Quando o jogador tirar o dedo da tela, o método “onTouchEnded” é chamado. Esse método precisa de um pouco mais de atenção para implementá-lo. Deixaremos isso para a próxima seção do tutorial. Apenas adicione a implementação dele vazia, adicionando as seguintes linhas de código no final do arquivo “HelloWorldScene.cpp”.
void HelloWorld::onTouchEnded(Touch *touch,Event *unused_event) {
}
Com os três métodos responsáveis pelo tratamento de toques na tela devidamente implementados, podemos criar uma instância do objeto detector de toques na tela. Criaremos uma instância desse objeto e faremos ele executar os métodos implementados quando os eventos de toque na tela acontecer. Adicione as seguintes linhas de código:
HelloWorld::toqueNaTela = EventListenerTouchOneByOne::create();
HelloWorld::toqueNaTela->onTouchBegan = CC_CALLBACK_2(HelloWorld::onTouchBegan,this);
HelloWorld::toqueNaTela->onTouchMoved = CC_CALLBACK_2(HelloWorld::onTouchMoved,this);
HelloWorld::toqueNaTela->onTouchEnded = CC_CALLBACK_2(HelloWorld::onTouchEnded,this);
getEventDispatcher()->addEventListenerWithSceneGraphPriority(HelloWorld::toqueNaTela,this);
logo abaixo dessa:
addChild(HelloWorld::seta);
Programadores mais experientes notarão que ainda não excluímos a linha de código adicionada no último tutorial que realiza uma tacada inicial na bola branca. Essa linha foi adicionada apenas para teste e deve ser removida. Assim sendo, apague a seguinte linha de código do arquivo “HelloWorldScene.cpp”.
HelloWorld::bolaBrancaCorpo->setVelocity(Vec2(-300.0,0.0));
Agora podemos finalmente pensar em realizar a tacada quando o jogador soltar o dedo da tela.
Realizando a tacada
Quando o jogador soltar o dedo da tela, será atribuída uma velocidade para a bola branca com base na direção e comprimento da seta. Além disso, será aplicada uma força de atrito na bola branca, exatamente como fizemos no tutorial passado. Outro ponto importante, após a execução de uma tacada, o jogador não poderá realizar novas tacadas até o momento em que todas as bolas estejam paradas. Para limitar isso, a tela de toque será desabilitada após a tacada e será reabilitada somente quando todas as bolas pararem. Para finalizar, a seta que mirava a última tacada será deixada invisível para que a simulação física seja mostrada naturalmente ao jogador.
Todas as rotinas descritas serão realizadas pelo método “onTouchEnded”. Assim sendo, no arquivo “HelloWorldScene.cpp”, adicione as seguintes linhas e código:
static float g = 9.8;
static float mi = 3.0;
Vec2 dir,fa;
float t;
Size size = Director::getInstance()->getVisibleSize();
Vec2 p = touch->getLocationInView();
p.y = size.height – p.y;
p.operator-=(HelloWorld::bolaBranca->getPosition());
HelloWorld::seta->setVisible(false);
HelloWorld::bolaBrancaCorpo->setVelocity(p.operator/(HelloWorld::mesa->getScale()));
dir = HelloWorld::bolaBrancaCorpo->getVelocity();
dir.normalize();
dir.negate();
fa = dir.operator*(HelloWorld::bolaBrancaCorpo->getMass()*g*mi);
HelloWorld::bolaBrancaCorpo->applyForce(fa);
t = (HelloWorld::bolaBrancaCorpo->getVelocity().length()*HelloWorld::bolaBrancaCorpo->getMass())/fa.length();
schedule(schedule_selector(HelloWorld::paraBolaBranca),t);
HelloWorld::toqueNaTela->setEnabled(false);
logo após essa:
void HelloWorld::onTouchEnded(Touch *touch,Event *unused_event) {
Para finalizar o tutorial de hoje, precisamos habilitar a tela de toque para que seja possível o jogador realizar a próxima tacada após todas as bolas entrarem em repouso. Para isso, quando cada bola parar, testaremos se todas as outras também já estão paradas. Caso isso seja verdade, então a tela de toque é habilitada para que o jogador possa realizar a próxima tacada. Assim, no arquivo “HelloWorldScene.cpp”, adicione as seguintes linhas de código:
if(HelloWorld::bolaVermelhaCorpo->getVelocity().isZero()&&
HelloWorld::bolaAmarelaCorpo->getVelocity().isZero())
HelloWorld::toqueNaTela->setEnabled(true);
logo abaixo dessas:
HelloWorld::bolaBrancaCorpo->setVelocity(Vec2::ZERO);
unschedule(schedule_selector(HelloWorld::paraBolaBranca));
as seguintes linhas de código:
if(HelloWorld::bolaBrancaCorpo->getVelocity().isZero()&&
HelloWorld::bolaAmarelaCorpo->getVelocity().isZero())
HelloWorld::toqueNaTela->setEnabled(true);
logo abaixo dessas:
HelloWorld::bolaVermelhaCorpo->setVelocity(Vec2::ZERO);
unschedule(schedule_selector(HelloWorld::paraBolaVermelha));
e as seguintes linhas de código:
if(HelloWorld::bolaBrancaCorpo->getVelocity().isZero()&&
HelloWorld::bolaVermelhaCorpo->getVelocity().isZero())
HelloWorld::toqueNaTela->setEnabled(true);
logo abaixo dessas:
HelloWorld::bolaAmarelaCorpo->setVelocity(Vec2::ZERO);
unschedule(schedule_selector(HelloWorld::paraBolaAmarela));
Agora sim. Finalizamos a implementação do tutorial de hoje. Já podemos compilar o código e executar o jogo. Você perceberá que será possível realizar a primeira tacada ao encostar o dedo na tela. Após realizar a primeira tacada, o jogo não deixa o jogador realizar outra enquanto existir pelo menos uma bola em movimento. Quando todas as bolas pararem, o jogo habilita a tela de toque e deixa o jogador realizar a próxima tacada, exatamente como queríamos que acontecesse. A Figura 1 ilustra a seta apresentada quando o jogador está prestes a realizar uma tacada na bola vermelha.

Implementamos nesse tutorial o sistema de tacadas do nosso jogo de sinuca. Para implementá-lo, nós precisamos criar um sprite de seta para que o jogador possa visualizar a direção e a força com as quais a bola será tacada. Além disso, nós criamos também um objeto detector de toques na tela e implementamos as rotinas necessárias para executar a tacada. Tais rotinas são a mira da tacada e a execução dela.
Veremos no próximo tutorial como implementar o sistema das caçapas. Faremos com que as bolas que chegarem as caçapas sejas retiradas da mesa.
Um grande abraço e até lá. []