Escolar Documentos
Profissional Documentos
Cultura Documentos
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
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.
5
Construindo o jogo passo a passo
A partir de agora iremos construir passo a passo, o nosso “próprio” Flappy Bird.
Renomeando o projeto
6
Renomeando a constante “app_name”
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 :
7
Caixa de diálogo – Refactoring
Mensagem de confirmação
8
Basta clicar em “Yes” para confirmar.
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.
9
Ícone da aplicação alterado
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).
10
package gameutil2d.project;
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;
Image digito1[];
Image digito2[];
Image digito3[];
int score;
@Override
public void Draw(Canvas canvas) {
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());
}
digito1[valor_1_digito].Draw(canvas);
digito2[valor_2_digito].Draw(canvas);
}
else {
12
Irei explicar agora algumas linhas de código dentro dessa classe.
Irei explicar uma linha de código dentro do loop , que servirá para todas as linhas
restantes.
Se analisarmos a instrução:
13
Como realmente o Android trata nome de arquivos ?
A expressão :
“R.drawable.numero_0_branco”
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.
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;
AnimationSprites flappy_bird;
Context context;
Image tela_1, tela_2;
Image titulo_flappy_bird;
Image botao_play;
int contaFrameTelaPreta = 0;
SoundPool sp;
int ID_SOM_TROCA_TELA;
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);
flappy_bird.Start(6, true);
}
chao_1.MoveByX(-5);
chao_2.MoveByX(-5);
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);
}
}
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 :
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);
}
}
18
Quando pressionamos o botão “play” do jogo , é executado o seguinte código
destacado em azul dentro do método onTouch:
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;
}
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.
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;
Scene scene;
AnimationSprites flappy_bird;
Context context;
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;
int contaTempoExibirBotaoPlay ;
boolean mostrarBotaoPlay ;
int contaTempoTelaPreta;
int contaFrameFundoBranco;
this.context = context;
iniciar_jogo = false;
perdeu_o_jogo = false;
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);
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);
chao_1.MoveByX(-5);
chao_2.MoveByX(-5);
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) {
if(e.GetTag() == "cano_do_chao")
{
if(e.GetX() == 250)
{
executarSom(ID_SOM_PONTUACAO);
score++;
}
}
}
hudScore.SetScore(score);
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(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(contaTempoExibirBotaoPlay == 48)
{
mostrarBotaoPlay = true;
}
}
if(!mostrarTelaJogo)
{
contaTempoTelaPreta++;
if(contaTempoTelaPreta == 10)
{
contaTempoTelaPreta = 0;
mostrarTelaJogo = true;
}
}
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);
}
}
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;
}
}
}
}
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;
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);
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:
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);
}
}
Jogo em execução
30
Quando tocamos na tela do jogo, é executado o seguinte código destacado em
azul dentro do método onTouch:
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)
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.
33
Prévia do jogo - pássaro caindo
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(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.
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
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.*;
TelaInicial tela_inicial;
TelaFase1 tela_fase_1;
if(TelasDoJogo.tela == TelaJogo.TELA_INICIAL)
tela_inicial.Update();
else if(TelasDoJogo.tela == TelaJogo.TELA_FASE_1)
tela_fase_1.Update();
}
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);
}
38
Conclusão a respeito do Tutorial
Que tal experimentar o conhecimento adquiro neste tutorial para aprimorar o jogo
desenvolvido ?
Um forte abraço.
39