Você está na página 1de 39

Aviso sobre este Tutorial

Antes de iniciar a leitura deste material, veja esses avisos importantes:

Esse material NÃO PODERÁ SER DISTRIBUÍDO, em hipótese alguma, em


outros sites da Internet ou através outros processos/meios .

Essa material , em hipótese alguma, NÃO PODE SER COMERCIALIZADO tanto


pela Internet ou de forma impressa.

Se por acaso você ver este material sendo distribuído em outro site ou sendo
comercializado (sem ser pelo site oficial da apostila), por favor, entre em contato
com o autor (ver e-mail na primeira página).

2
Sobre o Autor do Tutorial

Luciano Alves da Silva é Bacharelado em Ciência da Computação pela UNISUAM


(Rio de Janeiro – RJ) e Pós-Graduado em Docência do Ensino Superior pelo
Instituto A Vez do Mestre (Universidade Cândido Mendes – UCAM, no Rio de
Janeiro). Possui conhecimento e domínio das linguagens de programação Pascal,
Java, C/C++, C#, Visual Basic, Delphi, PHP e HTML. Já criou Ambientes de
Desenvolvimento Integrado (conhecidos como IDE) como o MakeWare (que
trabalha com as linguagens Pascal, C++ e Java) e o AlgoWare (interpretador de
algoritmos).

É autor também dos seguintes livros, pela editora AGBOOK

 Aprenda Passo a Passo a Programar em Android – Guia Essencial para


Desenvolvedores

 Desenvolvendo Jogos com a Plataforma XNA – Guia para


Desenvolvedores.

 Desenvolvendo Jogos com a Ferramenta RPG Maker VX– Guia do


Usuário.

3
Apresentação
Este material mostra passo a passo a construção de um jogo baseado no “Flappy
Bird” para a plataforma Android.

Caso você, que esteja lendo este tutorial, seja iniciante em programação com
jogos para Android, recomendo antes adquirir a “Apostila de Android :
Desenvolvimento de Jogos 2ª Edição” (que pode ser encontrada em meu site
www.apostilaandroid.net), para melhor compreensão e entendimento dos
comandos e instruções utilizadas para o desenvolvimento do jogo.

4
Tutorial Criando um jogo baseado
no Flappy Bird

N
este tutorial iremos aprender passo a passo a construir um jogo baseado
no famoso “Flappy Bird”. Se você ainda não leu a minha “Apostila de
Android : Desenvolvimento de Jogos” (2ª Edição), recomendo adquirir o
material citado para melhor compreensão do desenvolvimento.

Sobre o jogo Flappy Bird

O “Flappy Bird” é um jogo considerado simples (e “viciante”) onde você controla


um pássaro voador que precisa se desviar dos canos sem colidir. A cada
obstáculo ultrapassado, é somado o seu score. Qualquer toque que o pássaro
fizer em um obstáculo, o jogo é dado como “Game Over”. Veja a imagem do
nosso jogo abaixo:

Jogo do Flappy Bird

5
Construindo o jogo passo a passo

A partir de agora iremos construir passo a passo, o nosso “próprio” Flappy Bird.

A primeira coisa que precisamos fazer é importar a nossa framework GameUtil2D


(neste caso, o arquivo “GameUtil2DV1_1.zip”, que você poderá baixar pelo
seguinte link : http://bit.ly/1pgpwzz) para dentro do Android Developer Tools
(qualquer dúvida, consulte a “Apostila de Android : Desenvolvimento de Jogos 2 ª
Edição”).

Depois de importar o framework para o ADT, vamos realizar algumas tarefas


dentro dele.

Primeiramente, vamos renomear o nosso projeto (simplesmente selecionando-o


dentro do ADT, na seção “Package Explorer”, e em seguida pressionando a tecla
“F2”). Iremos chamar nosso projeto de “Flappy Bird”, conforme podemos conferir
abaixo:

Renomeando o projeto

Clique em “OK” para confirmarmos a operação acima.

Após renomearmos o projeto, vamos abrir o arquivo “strings.xml” (presente dentro


do diretório “values”, situado dentro da pasta “res” do nosso projeto). Feito isso,
vamos atribuir um novo valor a constante “app_name”, que será o nome do nosso
jogo : “Flappy Bird (Android)”. Veja o resultado na figura seguinte:

6
Renomeando a constante “app_name”

Depois de atribuir o valor a constante, salve o arquivo para que as alterações


tenham efeito.

Agora vamos clicar com o botão direito sobre o nosso projeto criado e em seguida
vamos selecionar a opção “Android Tools” / “Rename Application Package”. Feito
isso será aberta a seguinte caixa de diálogo :

Caixa de diálogo – Rename Application Package

Vamos atribuir um novo nome para o pacote , que vai se chamar


“gameutil2d.flappybird”. Feito isso clique em “OK”, que surgirá a seguinte caixa de
diálogo:

7
Caixa de diálogo – Refactoring

Clique em “Finish” para concluirmos o processo de renomeação do pacote.


Possivelmente em seguida deverá ser mostrada a seguite mensagem :

Mensagem de confirmação

8
Basta clicar em “Yes” para confirmar.

Agora vamos obter as imagens e arquivos de sons de efeito que utilizaremos


dentro do nosso jogo, que você pode baixar pelo seguinte link : bit.ly/1jqQ4Xo

Copiando as imagens e os arquivos de sons de efeito

Agora vamos copiar todas as imagens “png” que baixamos do link acima para
dentro da pasta “drawable-mdpi”.

Em seguida, vamos criar uma pasta dentro do diretório “res” do nosso projeto
chamada “raw” , que irá guardar todos os sons de efeito do nosso jogo. Após criar
a pasta copie todos os arquivos “wav” para dentro desse diretório que acabamos
de criar.

Definindo o ícone da nossa aplicação

Agora vamos abrir o arquivo “AndroidManifest.xml” (que se encontra dentro da


pasta principal do projeto) para alterarmos o ícone do nosso jogo.

Após abrir o arquivo “AndroidManifest.xml”, selecione a guia “Application” para


definirmos ícone do nosso jogo (no campo “Icon”). Vamos escolher a imagem
“ícone_jogo_flappy_bird”. Vejamos o resultado abaixo:

9
Ícone da aplicação alterado

Feito isso, salve as alterações para que elas tenham efeito.

Desenvolvendo as classes e o código do jogo

O projeto do nosso jogo terá somente as seguintes classes:

TelaInicial : Tela inicial do jogo, onde é mostrado nosso pássaro voando, o


título do jogo (“Flappy Bird”) e o botão “play”.

TelaFase1 : Essa é a tela onde o jogo todo ocorre, onde controlaremos


nosso pássaro para se desviar dos obstáculos (simplesmente utilizando o
“touch”).

HUDScore : Essa classe será responsável por exibir o “score” do jogo (que
na verdade é uma contagem de cada cano que nosso pássaro consegue se
desviar).

TelasDoJogo : Essa classe conterá as informações de todas as telas que


utilizaremos no jogo.

Primeiramente vamos criar a classe TelasDoJogo (dentro do pacote


“gameutil2d.project”, presente dentro da pasta “src”, situado dentro do nosso
projeto), com o seguinte código abaixo:

10
package gameutil2d.project;

public class TelasDoJogo {

public enum TelaJogo { TELA_INICIAL, TELA_FASE_1 }

public static TelaJogo tela;

Em seguida, dentro do pacote “gameutil2d.project” vamos criar a classe chamada


HUDScore , com o seguinte código em seguida:

package gameutil2d.project;

import android.content.Context;
import android.graphics.Canvas;
import gameutil2d.classes.basic.GameElement;
import gameutil2d.classes.basic.Image;
import gameutil2d.framework.flappybird.R;

public class HUDScore extends GameElement{

Image digito1[];
Image digito2[];
Image digito3[];

int score;

public HUDScore(Context context) {

digito1 = new Image[10];


digito2 = new Image[10];
digito3 = new Image[10];

for(int x = 0; x < 10 ; x++)


{
digito1[x] = new Image(context, R.drawable.numero_0_branco +
x, 400 - 20, 35, 40, 58);
digito2[x] = new Image(context, R.drawable.numero_0_branco +
x, 400 - 20, 35, 40, 58);
digito3[x] = new Image(context, R.drawable.numero_0_branco +
x, 400 - 20, 35, 40, 58);
}
}

@Override
public void Draw(Canvas canvas) {

if(score < 10)


{
for(int x = 0; x < 10 ; x++)

11
digito1[x].SetX(400 - 20);

digito1[score].Draw(canvas);
}
else if(score < 100)
{
for(int x = 0; x < 10 ; x++)
{
digito1[x].SetX(400 - 40);
digito2[x].SetX(digito1[x].GetX() +
digito1[x].GetWidth());
}

String score_string = String.valueOf(score);

int valor_1_digito = Integer.parseInt(String.valueOf


(score_string.charAt(0)));
int valor_2_digito = Integer.parseInt(String.valueOf
(score_string.charAt(1)));

digito1[valor_1_digito].Draw(canvas);
digito2[valor_2_digito].Draw(canvas);
}
else {

for(int x = 0; x < 10 ; x++)


{
digito1[x].SetX(400 - 60);
digito2[x].SetX(digito1[x].GetX() +
digito1[x].GetWidth());
digito3[x].SetX(digito2[x].GetX() +
digito2[x].GetWidth());
}

String score_string = String.valueOf(score);

int valor_1_digito = Integer.parseInt(String.valueOf


(score_string.charAt(0)));
int valor_2_digito = Integer.parseInt(String.valueOf
(score_string.charAt(1)));
int valor_3_digito = Integer.parseInt(String.valueOf
(score_string.charAt(2)));
digito1[valor_1_digito].Draw(canvas);
digito2[valor_2_digito].Draw(canvas);
digito3[valor_3_digito].Draw(canvas);
}

public void SetScore(int score)


{
this.score = score;
}

12
Irei explicar agora algumas linhas de código dentro dessa classe.

Se observamos a classe acima trabalhamos com três “arrays” do tipo Image :


digito1, digito2 e digito3. Cada um deles irá carregar 10 imagens (na verdade
números, que irão representar o score que irá aparecer na tela) :
numero_0_branco.png até numero_9_branco.png.

Cada imagem é um arquivo a ser carregado dentro do “array”, mas, se


observarmos na linha de código (dentro do construtor HUDScore) que carrega
cada um dos dígitos dentro dos três vetores, temos o seguinte bloco de instruções
:

for(int x = 0; x < 10 ; x++)


{
digito1[x] = new Image(context, R.drawable.numero_0_branco +
x, 400 - 20, 35, 40, 58);
digito2[x] = new Image(context, R.drawable.numero_0_branco +
x, 400 - 20, 35, 40, 58);
digito3[x] = new Image(context, R.drawable.numero_0_branco +
x, 400 - 20, 35, 40, 58);
}

Irei explicar uma linha de código dentro do loop , que servirá para todas as linhas
restantes.

Se analisarmos a instrução:

digito1[x] = new Image(context, R.drawable.numero_0_branco + x, 400 - 20,


35, 40, 58);

Ocorre uma “soma” com o nome do arquivo “numero_0_branco”. Ué , Soma ?


Mas não está acontecendo uma concatenação ? Por incrível que pareça NÃO! O
QUE ESTÁ REALMENTE ACONTECENDO É UMA SOMA. Vamos entender o
porquê :

13
Como realmente o Android trata nome de arquivos ?

Pessoal, o Android na verdade trata os nomes dos arquivos como se fossem


“constantes numéricas”, isso mesmo, como “constantes numéricas”.

A expressão :

“R.drawable.numero_0_branco”

Na verdade é uma constante numérica. O “R” da expressão acima na


verdade é uma classe, que dentro dela possui uma “classe interna estática”
chamada drawable, que dentro dessa classe possui uma constante estática
chamada numero_0_branco, que possui um valor inteiro (expresso em
“hexadecimal”).

Como é estruturado essas classes ?

As classes são estruturadas aproximadamente desta forma:

public final class R {

public static final class drawable {


:
public static final int numero_0_branco=0x7f02000;
public static final int numero_1_branco=0x7f02001;
public static final int numero_2_branco=0x7f02002;
public static final int numero_3_branco=0x7f02003;
public static final int numero_4_branco=0x7f02004;
public static final int numero_5_branco=0x7f02005;
public static final int numero_6_branco=0x7f02006;
public static final int numero_7_branco=0x7f02007;
public static final int numero_8_branco=0x7f02008;
public static final int numero_9_branco=0x7f02009;
:
}
}

Observe que há uma sequência numérica entre as constantes, ou seja, se


efetuarmos a soma do valor da constante numero_0_branco com mais 1,
teremos o valor da constante numero_1_branco. Entenderam agora ?

14
Dentro do método Draw ocorre a exibição do score da tela. Se o valor do score
for menor que 10 será exibido um dígito na tela, se por acaso for maior ou igual a
10 e menor que 100, serão exibidos dois dígitos, caso contrário, três dígitos.

Agora dentro do pacote “gameutil2d.project” vamos criar uma classe chamada


TelaInicial , e em seguida vamos digitar o seu código abaixo:

package gameutil2d.project;

import gameutil2d.classes.basic.AnimationSprites;
import gameutil2d.classes.basic.Image;
import gameutil2d.framework.OrientationMode;
import gameutil2d.framework.flappybird.R;
import gameutil2d.project.TelasDoJogo.TelaJogo;
import android.content.Context;
import android.graphics.Canvas;
import android.media.AudioManager;
import android.media.SoundPool;
import android.view.MotionEvent;

public class TelaInicial {

AnimationSprites flappy_bird;
Context context;
Image tela_1, tela_2;

Image chao_1, chao_2;

Image titulo_flappy_bird;

Image botao_play;

boolean exibe_tela_preta = false;

int contaFrameTelaPreta = 0;

SoundPool sp;

int ID_SOM_TROCA_TELA;

public TelaInicial(Context context) {


this.context = context;
sp = new SoundPool(1, AudioManager.STREAM_MUSIC, 0);
ID_SOM_TROCA_TELA = sp.load(context,
R.raw.som_troca_tela_game_over, 0);
}

public void onScreenLoading(int w, int h, OrientationMode orientation)


{
flappy_bird = new AnimationSprites(context, (w / 2) - 25, 180, 54,
43);

15
flappy_bird.Add(R.drawable.flappy_bird_voando_1);
flappy_bird.Add(R.drawable.flappy_bird_voando_2);
flappy_bird.Add(R.drawable.flappy_bird_voando_3);
tela_1 = new Image(context, R.drawable.tela_1, 0, 0, w, h);
tela_2 = new Image(context, R.drawable.tela_2, w, 0, w, h);

chao_1 = new Image(context, R.drawable.chao, 0, h - 70, w,


110,"chao");
chao_2 = new Image(context, R.drawable.chao, w, h - 70, w, 110,
"chao");

titulo_flappy_bird = new Image(context,


R.drawable.titulo_flappy_bird, (w / 2) - (272 / 2), 80, 272, 78);

botao_play = new Image(context, R.drawable.botao_play, (w / 2) –


80, 270, 160, 93);

flappy_bird.Start(6, true);
}

public void Update()


{
if (!exibe_tela_preta) {
tela_1.MoveByX(-5);
tela_2.MoveByX(-5);

if (tela_1.GetX() < -tela_1.GetWidth())


tela_1.SetX(tela_2.GetX() + tela_2.GetWidth());

if (tela_2.GetX() < -tela_2.GetWidth())


tela_2.SetX(tela_1.GetX() + tela_1.GetWidth());

chao_1.MoveByX(-5);
chao_2.MoveByX(-5);

if (chao_1.GetX() < -chao_1.GetWidth())


chao_1.SetX(chao_2.GetX() + chao_2.GetWidth());
if (chao_2.GetX() < -chao_2.GetWidth())
chao_2.SetX(chao_1.GetX() + chao_1.GetWidth());
}
else {
contaFrameTelaPreta++;

if(contaFrameTelaPreta == 10) {
contaFrameTelaPreta = 0;
exibe_tela_preta = false;
TelasDoJogo.tela = TelaJogo.TELA_FASE_1;
}
}
}

16
public void Draw(Canvas canvas)
{
if (!exibe_tela_preta) {

tela_1.Draw(canvas);
tela_2.Draw(canvas);
chao_1.Draw(canvas);
chao_2.Draw(canvas);
flappy_bird.Draw(canvas);
titulo_flappy_bird.Draw(canvas);
botao_play.Draw(canvas);
}
}

public void onTouch(MotionEvent e)


{
if(e.getAction() == MotionEvent.ACTION_DOWN)
{
if(botao_play.IsTouch(e.getX(), e.getY())) {
executarSom(ID_SOM_TROCA_TELA);
exibe_tela_preta = true;
}
}
}

public void executarSom(int ID)


{
sp.play(ID, 1, 1, 1, 0, 1f);
}

Na figura seguinte temos uma prévia de como será a nossa tela inicial:

17
Prévia da execução da Tela Inicial

Irei comentar algumas linhas de código desta classe. Vejamos agora o código
dentro do método Draw :

public void Draw(Canvas canvas)


{
if (!exibe_tela_preta) {

tela_1.Draw(canvas);
tela_2.Draw(canvas);
chao_1.Draw(canvas);
chao_2.Draw(canvas);
flappy_bird.Draw(canvas);
titulo_flappy_bird.Draw(canvas);
botao_play.Draw(canvas);
}
}

Observe que toda a tela inicial do jogo só é exibida se a condição acima do


código for verdadeira (que verifica se o atributo exibe_tela_preta possui o valor
false).

18
Quando pressionamos o botão “play” do jogo , é executado o seguinte código
destacado em azul dentro do método onTouch:

public void onTouch(MotionEvent e)


{
if(e.getAction() == MotionEvent.ACTION_DOWN)
{
if(botao_play.IsTouch(e.getX(), e.getY())) {
executarSom(ID_SOM_TROCA_TELA);
exibe_tela_preta = true;
}
}
}

Que reproduz um som de efeito do jogo e atribui a variável exibe_tela_preta o


valor true.

Dentro do método Update nós temos o seguinte código:

if (!exibe_tela_preta) {

tela_1.MoveByX(-5);
tela_2.MoveByX(-5);
:
chao_2.SetX(chao_1.GetX() + chao_1.GetWidth());
}
else {
contaFrameTelaPreta++;
if(contaFrameTelaPreta == 10) {
contaFrameTelaPreta = 0;
exibe_tela_preta = false;
TelasDoJogo.tela = TelaJogo.TELA_FASE_1;
}

Verifica se a variável exibe_tela_preta assume o valor false. Se a condição for


verdadeira, realiza todo o processamento da tela inicial (como deslocamento do
cenário, do chão e etc.). Agora, se a condição acima for falsa (que significa que
está sendo exibida uma tela preta no jogo, ou melhor, nada está sendo
desenhado na tela), é executado o seguinte bloco de código abaixo:

contaFrameTelaPreta++;
if(contaFrameTelaPreta == 10) {
contaFrameTelaPreta = 0;
exibe_tela_preta = false;
TelasDoJogo.tela = TelaJogo.TELA_FASE_1;
}

19
Que realiza uma contagem de frames , determinando quanto tempo será exibida a
tela preta antes de ir para a próxima tela.

Agora dentro do pacote “gameutil2d.project” vamos criar uma classe chamada


TelaFase1, com o seguinte código abaixo:

package gameutil2d.project;

import gameutil2d.classes.basic.AnimationSprites;
import gameutil2d.classes.basic.Collision;
import gameutil2d.classes.basic.GameElement;
import gameutil2d.classes.basic.Image;
import gameutil2d.classes.scene.Scene;
import gameutil2d.framework.OrientationMode;
import gameutil2d.framework.flappybird.R;
import android.content.Context;
import android.graphics.Canvas;
import android.media.AudioManager;
import android.media.SoundPool;
import android.view.MotionEvent;

public class TelaFase1 {

Scene scene;

AnimationSprites flappy_bird;

Context context;

Image tela_1, tela_2;

Image chao_1, chao_2;

Image titulo_get_ready;

Image titulo_game_over;

Image botao_play;

int largura_tela;

int altura_tela;

Image play_info;

Image flappy_bird_caindo;

Image fundo_branco;

boolean iniciar_jogo;

20
int deslocamento;

boolean perdeu_o_jogo;

int contaFrameCriaCano;

HUDScore hudScore;

int score;

boolean tocou_no_chao;

SoundPool sp;

int ID_SOM_BATENDO_ASAS;
int ID_SOM_BATENDO_E_CAINDO;
int ID_SOM_BATENDO_NO_CHAO;
int ID_SOM_GAME_OVER;
int ID_SOM_REINICIA;
int ID_SOM_PONTUACAO;

int contaFrameTempoSumirScore;

boolean exibirScore = false;


boolean mostrarGameOver = false;
int deslocaTituloGameOver;

int contaTempoExibirBotaoPlay ;
boolean mostrarBotaoPlay ;

boolean mostrarTelaJogo = true;

int contaTempoTelaPreta;

int contaFrameFundoBranco;

boolean exibirFundoBranco = false;

boolean colidiu_no_cano = false;

public TelaFase1(Context context) {

this.context = context;

iniciar_jogo = false;

perdeu_o_jogo = false;

hudScore = new HUDScore(context);


tocou_no_chao = false;

sp = new SoundPool(6, AudioManager.STREAM_MUSIC, 0);

21
ID_SOM_BATENDO_ASAS = sp.load(context, R.raw.som_batendo_asas, 0);
ID_SOM_BATENDO_E_CAINDO = sp.load(context, R.raw.som_batendo_e_caindo,
0);
ID_SOM_GAME_OVER = sp.load(context, R.raw.som_troca_tela_game_over, 0);
ID_SOM_REINICIA = sp.load(context, R.raw.som_troca_tela_game_over, 0);
ID_SOM_PONTUACAO = sp.load(context, R.raw.som_pontuacao, 0);
ID_SOM_BATENDO_NO_CHAO = sp.load(context, R.raw.som_batendo_no_chao, 0);

public void onScreenLoading(int w, int h, OrientationMode orientation)


{

flappy_bird = new AnimationSprites(context, (w / 2) - 25, 180, 54, 43);


flappy_bird.Add(R.drawable.flappy_bird_voando_1);
flappy_bird.Add(R.drawable.flappy_bird_voando_2);
flappy_bird.Add(R.drawable.flappy_bird_voando_3);

flappy_bird_caindo = new Image(context, R.drawable.flappy_caindo, 0, 0,


43, 54);

tela_1 = new Image(context, R.drawable.tela_1, 0, 0, w, h);


tela_2 = new Image(context, R.drawable.tela_2, w, 0, w, h);

play_info = new Image(context, R.drawable.play_info, (w / 2) - (172 / 2),


250, 172, 154);

chao_1 = new Image(context, R.drawable.chao, 0, h - 70, w, 110,"chao");


chao_2 = new Image(context, R.drawable.chao, w, h - 70, w, 110, "chao");

titulo_get_ready = new Image(context, R.drawable.titulo_get_ready, (w /


2) - (282 / 2), 80, 282, 83);

titulo_game_over = new Image(context, R.drawable.titulo_game_over, (w /


2) - (291 / 2) , h, 291, 67);

botao_play = new Image(context, R.drawable.botao_play, (w / 2) - 80, 270,


160, 93);

fundo_branco = new Image(context, R.drawable.fundo_branco, 0, 0, w, h);

flappy_bird.Start(6, true);

largura_tela = w;
altura_tela = h;

CriarTelaFase();

22
public void Update()
{

if (!perdeu_o_jogo) {
tela_1.MoveByX(-5);
tela_2.MoveByX(-5);

if (tela_1.GetX() < -tela_1.GetWidth())


tela_1.SetX(tela_2.GetX() + tela_2.GetWidth());

if (tela_2.GetX() < -tela_2.GetWidth())


tela_2.SetX(tela_1.GetX() + tela_1.GetWidth());

chao_1.MoveByX(-5);
chao_2.MoveByX(-5);

if (chao_1.GetX() < -chao_1.GetWidth())


chao_1.SetX(chao_2.GetX() + chao_2.GetWidth());

if (chao_2.GetX() < -chao_2.GetWidth())


chao_2.SetX(chao_1.GetX() + chao_1.GetWidth());
}

if (iniciar_jogo) {

if (!tocou_no_chao) {

deslocamento++;

if ((deslocamento % 2) == 0)
if(!perdeu_o_jogo)
flappy_bird.MoveByY(deslocamento);
else
flappy_bird_caindo.MoveByY(deslocamento);

}
}

if (!perdeu_o_jogo) {
if (iniciar_jogo) {
contaFrameCriaCano++;

if (contaFrameCriaCano == 80) {
contaFrameCriaCano = 0;
CriaObstaculos();
}
}
}

23
if (!perdeu_o_jogo) {

for (GameElement e : scene.Elements()) {


if ((e.GetTag() == "cano_de_cima") || (e.GetTag() ==
"cano_do_chao"))
e.MoveByX(-5);

if(e.GetTag() == "cano_do_chao")
{
if(e.GetX() == 250)
{
executarSom(ID_SOM_PONTUACAO);
score++;

}
}
}

hudScore.SetScore(score);

// Verifica se flappy bird colidiu com algum dos canos


if (!perdeu_o_jogo) {
for (GameElement e : scene.Elements()) {

if ((e.GetTag() == "cano_de_cima")
|| (e.GetTag() == "cano_do_chao")) {
if (Collision.Check(flappy_bird, e)) {

perdeu_o_jogo = true;
colidiu_no_cano = true;
exibirFundoBranco = true;
contaFrameFundoBranco = 0;

executarSom(ID_SOM_BATENDO_E_CAINDO);
flappy_bird_caindo.SetX(flappy_bird.GetX());
flappy_bird_caindo.SetY(flappy_bird.GetY());

deslocamento = 0;
break;
}
}

24
if (Collision.Check(flappy_bird, chao_1) || Collision.Check(flappy_bird,
chao_2) || Collision.Check(flappy_bird_caindo, chao_1) ||
Collision.Check(flappy_bird_caindo, chao_2))
{

tocou_no_chao = true;

if(!perdeu_o_jogo) {
perdeu_o_jogo = true;
exibirFundoBranco = true;
contaFrameFundoBranco = 0;
executarSom(ID_SOM_BATENDO_NO_CHAO);
}

flappy_bird_caindo.SetX(flappy_bird.GetX());
flappy_bird_caindo.SetY(chao_1.GetY() - flappy_bird_caindo.GetHeight());

flappy_bird.SetY(chao_1.GetY() - flappy_bird.GetHeight());

flappy_bird.Stop();
}

if(perdeu_o_jogo && tocou_no_chao)


{
contaFrameTempoSumirScore++;

if(contaFrameTempoSumirScore == 48)
{
exibirScore = false;
mostrarGameOver = true;
deslocaTituloGameOver = -28;
executarSom(ID_SOM_GAME_OVER);

}
}

if(exibirFundoBranco)
{
contaFrameFundoBranco++;

if(contaFrameFundoBranco == 3)
{
exibirFundoBranco = false;
contaFrameFundoBranco = 0;

}
}

if(mostrarGameOver)
{
if(deslocaTituloGameOver < 0)
deslocaTituloGameOver++;

25
titulo_game_over.MoveByY(deslocaTituloGameOver);
}

if(mostrarGameOver && (deslocaTituloGameOver == 0))


{
contaTempoExibirBotaoPlay++;

if(contaTempoExibirBotaoPlay == 48)
{
mostrarBotaoPlay = true;
}
}

if(!mostrarTelaJogo)
{
contaTempoTelaPreta++;
if(contaTempoTelaPreta == 10)
{
contaTempoTelaPreta = 0;
mostrarTelaJogo = true;
}
}

public void Draw(Canvas canvas)


{
if (mostrarTelaJogo) {

scene.Draw(canvas);
if (!iniciar_jogo) {
titulo_get_ready.Draw(canvas);
play_info.Draw(canvas);
}
chao_1.Draw(canvas);
chao_2.Draw(canvas);
if (!perdeu_o_jogo)
flappy_bird.Draw(canvas);
else
{
if(colidiu_no_cano)
flappy_bird_caindo.Draw(canvas);
else
flappy_bird.Draw(canvas);
}

if (exibirScore)
hudScore.Draw(canvas);

if (mostrarGameOver)
titulo_game_over.Draw(canvas);

26
if (mostrarBotaoPlay)
botao_play.Draw(canvas);

if(exibirFundoBranco)
fundo_branco.Draw(canvas);

}
}

public void onTouch(MotionEvent e)


{
if(e.getAction() == MotionEvent.ACTION_DOWN)
{
if (!perdeu_o_jogo) {

if (!iniciar_jogo) {
iniciar_jogo = true;
exibirScore = true;
} else {
deslocamento = -15;
}
executarSom(ID_SOM_BATENDO_ASAS);
}

if(mostrarBotaoPlay)
{
if(botao_play.IsTouch(e.getX(), e.getY())) {
executarSom(ID_SOM_REINICIA);
mostrarTelaJogo = false;
CriarTelaFase();
contaTempoTelaPreta = 0;
return;
}
}
}
}

public void CriarTelaFase()


{

scene = new Scene();

contaFrameCriaCano = 0;
contaFrameTempoSumirScore = 0;

deslocamento = -15;
contaTempoExibirBotaoPlay = 0;
exibirFundoBranco = false;
contaFrameFundoBranco = 0;
colidiu_no_cano = false;
mostrarGameOver = false;
mostrarBotaoPlay = false;
exibirScore = false;
iniciar_jogo = false;

27
perdeu_o_jogo = false;
tocou_no_chao = false;

score = 0;

//Adiciona os elementos na tela


tela_1.SetX(0);
tela_2.SetX(largura_tela);

flappy_bird.SetX((largura_tela / 2) - 25);
flappy_bird.SetY(180);
flappy_bird.Start(6, true);
flappy_bird_caindo.SetX(0);
flappy_bird_caindo.SetY(0);
titulo_game_over.SetY(altura_tela);
scene.Add(tela_1);
scene.Add(tela_2);

public void CriaObstaculos()


{

int posy = 200 + (int) Math.round(Math.random() * 150);

scene.Add(new Image(context,R.drawable.cano_do_chao, largura_tela, posy,


78,358,"cano_do_chao"));
scene.Add(new Image(context,R.drawable.cano_de_cima, largura_tela, posy
- 358 - 120, 78,358,"cano_de_cima"));

public void executarSom(int ID)


{
sp.play(ID, 1, 1, 1, 0, 1f);
}

Na figura seguinte temos uma prévia de como será a nossa tela do jogo:

28
Prévia da exibição da Tela do Jogo

Agora irei explicar algumas linhas de código desta classe (com o auxílio de
alguns prints do jogo para facilitar). Vamos ver o código dentro do método Draw,
conforme podemos conferir em seguida:

public void Draw(Canvas canvas)


{
if (mostrarTelaJogo) {

scene.Draw(canvas);
if (!iniciar_jogo) {
titulo_get_ready.Draw(canvas);
play_info.Draw(canvas);
}
chao_1.Draw(canvas);
chao_2.Draw(canvas);

if (!perdeu_o_jogo)
flappy_bird.Draw(canvas);
else
{

29
if(colidiu_no_cano)
flappy_bird_caindo.Draw(canvas);
else
flappy_bird.Draw(canvas);
}
if (exibirScore)
hudScore.Draw(canvas);

if (mostrarGameOver)
titulo_game_over.Draw(canvas);

if (mostrarBotaoPlay)
botao_play.Draw(canvas);

if(exibirFundoBranco)
fundo_branco.Draw(canvas);

}
}

Se observarmos o código acima , a tela do jogo só é exibida quando a variável


mostrarTelaJogo assume o valor true. Quando essa classe processa a exibição
da tela do jogo, temos o seguinte resultado:

Jogo em execução

30
Quando tocamos na tela do jogo, é executado o seguinte código destacado em
azul dentro do método onTouch:

public void onTouch(MotionEvent e)


{
if(e.getAction() == MotionEvent.ACTION_DOWN)
{
if (!perdeu_o_jogo) {
if (!iniciar_jogo) {
iniciar_jogo = true;
exibirScore = true;
} else {
deslocamento = -15;
}
executarSom(ID_SOM_BATENDO_ASAS);
}
}
:
}

Que é responsável por inicializar o jogo. Após o toque inicial, devemos manter o
pássaro voando na tela, fazendo vários e constantes toques na tela. Vejamos a
seguinte tela abaixo:

Jogo em execução

31
Dentro do método Update temos o seguinte código abaixo:

if (iniciar_jogo) {

if (!tocou_no_chao) {

deslocamento++;

if ((deslocamento % 2) == 0)
if(!perdeu_o_jogo)
flappy_bird.MoveByY(deslocamento);
else
flappy_bird_caindo.MoveByY(deslocamento);

}
}

Que é responsável por fazer o pássaro se deslocar para cima e para abaixo (a
medida que você vai tocando a tela), seguindo as seguintes condições:

Se o jogo tiver sido iniciado (se o atributo iniciar_jogo for igual a true)
Se o pássaro não tiver tocado o chão (se o atributo tocou_chao for igual a
false)
Se o usuário não perder o jogo (se o atributo perdeu_o_jogo for igual a
false)

Ainda dentro do método Update encontramos o seguinte bloco de código abaixo:

if (!perdeu_o_jogo) {

if (iniciar_jogo) {
contaFrameCriaCano++;

if (contaFrameCriaCano == 80) {
contaFrameCriaCano = 0;
CriaObstaculos();
}
}
}

Que realiza a criação dos obstáculos pela tela do jogo a cada 80 frames (através
do uso do atributo contaCriaCano). Os obstáculos (canos) são criados através do
método CriaObstaculos.

32
Quando nosso pássaro colide com algum cano, é executado o seguinte bloco de
código:

if (!perdeu_o_jogo) {
for (GameElement e : scene.Elements()) {

if ((e.GetTag() == "cano_de_cima")
|| (e.GetTag() == "cano_do_chao")) {
if (Collision.Check(flappy_bird, e)) {
perdeu_o_jogo = true;
colidiu_no_cano = true;
exibirFundoBranco = true;
contaFrameFundoBranco = 0;
executarSom(ID_SOM_BATENDO_E_CAINDO);
flappy_bird_caindo.SetX(flappy_bird.GetX());
flappy_bird_caindo.SetY(flappy_bird.GetY());
deslocamento = 0;
break;
}
}
}
}

Que realiza algumas operações como alterar o valor de alguns atributos como
perdeu_o_jogo, colidiu_no_cano, exibirFundoBranco (para true), reproduzir o som
do pássaro levando a pancada e caindo, além de preparar a imagem que mostra
o personagem caindo.

Vejamos o resultado no jogo em execução:

33
Prévia do jogo - pássaro caindo

Quando o passaro toca, o chão do jogo, é executado o seguinte código (dentro do


método Update):

if (Collision.Check(flappy_bird, chao_1) || Collision.Check(flappy_bird,


chao_2) || Collision.Check(flappy_bird_caindo, chao_1) ||
Collision.Check(flappy_bird_caindo, chao_2)) {
tocou_no_chao = true;
if(!perdeu_o_jogo)
{
perdeu_o_jogo = true;
exibirFundoBranco = true;
contaFrameFundoBranco = 0;
executarSom(ID_SOM_BATENDO_NO_CHAO);
}
flappy_bird_caindo.SetX(flappy_bird.GetX());
flappy_bird_caindo.SetY(chao_1.GetY() - flappy_bird_caindo.GetHeight());
flappy_bird.SetY(chao_1.GetY() - flappy_bird.GetHeight());
flappy_bird.Stop();
}

34
Que faz o pássaro colidir e ficar no chão. Após o pássaro cair no chão, é
executado o seguinte bloco de código dentro do método Update:

if(perdeu_o_jogo && tocou_no_chao)


{
contaFrameTempoSumirScore++;

if(contaFrameTempoSumirScore == 48)
{
exibirScore = false;
mostrarGameOver = true;
deslocaTituloGameOver = -28;
executarSom(ID_SOM_GAME_OVER);

}
}

Que faz uma conta de frames (intervalo) para a exibição do título “Game Over” e
também para ocultar o “score” do jogo.

Após a exibição do título “Game Over” na tela, é executado o seguinte código


abaixo (dentro do método Update):

if(mostrarGameOver && (deslocaTituloGameOver == 0))


{
contaTempoExibirBotaoPlay++;

if(contaTempoExibirBotaoPlay == 48)
{
mostrarBotaoPlay = true;
}
}

Que faz uma conta de frames (intervalo) para a exibição do botão play para
reiniciarmos o jogo. Vejamos a tela do jogo em seguida:

35
Jogo em execução – Game Over

Quando pressionamos o botão “play”, é executado o seguinte código destacado


em azul dentro do método onTouch:

public void onTouch(MotionEvent e)


{
if(e.getAction() == MotionEvent.ACTION_DOWN)
{
:
if(mostrarBotaoPlay)
{
if(botao_play.IsTouch(e.getX(), e.getY()))
{
executarSom(ID_SOM_REINICIA);
mostrarTelaJogo = false;
CriarTelaFase();
contaTempoTelaPreta = 0;
return;
}
}
}
}

Que é responsável por reiniciar todo o jogo.


36
Agora dentro da classe GameMain (que já existe dentro do pacote
“gameutil2d.project”) vamos digitar o seguinte código destacado em azul abaixo:

import gameutil2d.framework.*;
import gameutil2d.project.TelasDoJogo.TelaJogo;
import android.content.Context;
import android.graphics.Canvas;
import android.view.MotionEvent;
import gameutil2d.classes.basic.*;
import gameutil2d.classes.scene.*;
import gameutil2d.classes.character.*;
import gameutil2d.classes.text.*;

public class GameMain {

TelaInicial tela_inicial;
TelaFase1 tela_fase_1;

public GameMain(Context context)


{

tela_inicial = new TelaInicial(context);


tela_fase_1 = new TelaFase1(context);
TelasDoJogo.tela = TelaJogo.TELA_INICIAL;

public void onScreenLoading(int w, int h, OrientationMode orientation)


{
tela_inicial.onScreenLoading(w, h, orientation);
tela_fase_1.onScreenLoading(w, h, orientation);
}

public void Update()


{

if(TelasDoJogo.tela == TelaJogo.TELA_INICIAL)
tela_inicial.Update();
else if(TelasDoJogo.tela == TelaJogo.TELA_FASE_1)
tela_fase_1.Update();
}

public void Draw(Canvas canvas)


{

if(TelasDoJogo.tela == TelaJogo.TELA_INICIAL)
tela_inicial.Draw(canvas);
else if(TelasDoJogo.tela == TelaJogo.TELA_FASE_1)
tela_fase_1.Draw(canvas);
}

37
public void onTouch(MotionEvent e)
{

if(TelasDoJogo.tela == TelaJogo.TELA_INICIAL)
tela_inicial.onTouch(e);
else if(TelasDoJogo.tela == TelaJogo.TELA_FASE_1)
tela_fase_1.onTouch(e);
}

Depois de escrever todos os códigos solicitados , vamos executar o nosso jogo


para conferir os resultados. Divirta-se!

Vejamos o resultado abaixo:

Jogo em execução – Flappy Bird

38
Conclusão a respeito do Tutorial

Neste tutorial aprendemos passo a passo como construir um jogo baseado no


“Flappy Bird” (um jogo bastante conhecido e que fez um grande sucesso devido a
sua simplicidade e dificuldade), através de todas as técnicas apresentadas neste
tutorial.

Que tal experimentar o conhecimento adquiro neste tutorial para aprimorar o jogo
desenvolvido ?

Espero que meu tutorial lhe tenha sido útil

Um forte abraço.

39

Você também pode gostar