universidade regional de blumenaucampeche.inf.furb.br/tccs/2011-ii/tcc2011-2-19-vf... ·...
TRANSCRIPT
UNIVERSIDADE REGIONAL DE BLUMENAU
CENTRO DE CIÊNCIAS EXATAS E NATURAIS
CURSO DE CIÊNCIA DA COMPUTAÇÃO – BACHARELADO
IMPLEMENTAÇÃO DE UM MOTOR DE JOGOS DE CARTA
PARA A PLATAFORMA ANDROID
MASSAMI WELINGTON KAMIGASHIMA
BLUMENAU
2011
2011/2-19
MASSAMI WELINGTON KAMIGASHIMA
IMPLEMENTAÇÃO DE UM MOTOR DE JOGOS DE CARTA
PARA A PLATAFORMA ANDROID
Trabalho de Conclusão de Curso submetido à
Universidade Regional de Blumenau para a
obtenção dos créditos na disciplina Trabalho
de Conclusão de Curso II do curso de Ciência
da Computação — Bacharelado.
Prof. Paulo César Rodacki Gomes, Dr. - Orientador
BLUMENAU
2011
2011/2-19
IMPLEMENTAÇÃO DE UM MOTOR DE JOGOS DE CARTA
PARA A PLATAFORMA ANDROID
Por
MASSAMI WELINGTON KAMIGASHIMA
Trabalho aprovado para obtenção dos créditos
na disciplina de Trabalho de Conclusão de
Curso II, pela banca examinadora formada
por:
______________________________________________________
Presidente: Prof. Paulo César Rodacki Gomes, Dr. – Orientador, FURB
______________________________________________________
Membro: Prof. Dalton Solano dos Reis, M. – FURB
______________________________________________________
Membro: Prof. Mauro Marcelo Mattos, Dr. – FURB
Blumenau, 12 de dezembro de 2011
Dedico este trabalho a minha família, amigos e
principalmente a todos que me apoiaram direta
e indiretamente no desenvolvimento deste
trabalho.
AGRADECIMENTOS
A Deus, pela vida que possuo.
À minha família, que está sempre ao meu lado em todos os momentos.
A todos os meus amigos, que me apoiaram e deram forças para continuar.
Ao meu orientador, Paulo César Rodacki Gomes, por ter me dado a idéia e todo apoio
para que este trabalho fosse possível.
RESUMO
Este trabalho apresenta a elaboração de um motor de jogos para a plataforma Android em
forma de uma biblioteca e sua implementação um protótipo de jogo de pôquer. São
apresentados conceitos básicos sobre os principais recursos disponíveis para o
desenvolvimento de uma aplicação nesta plataforma. O motor disponibiliza ao desenvolvedor
a estrutura básica de um jogo de cartas, tais como a gerência das cartas do baralho,
posicionamento dos elementos na tela e a interação das cartas com o usuário.
Palavras-chave: Android. Jogos. Motor de jogos.
ABSTRACT
This work describes the development of a game engine for Android platform in the form of a
library and the implementation of a poker game prototype. Basic concepts are also presented
on the main resources available to develop an application on this platform. The engine
provides the designer with the basic structure of a card game, such as management of the
cards in the deck, positioning of elements on the screen and interact of the user with the cards.
Key-words: Android. Games. Game engine.
LISTA DE ILUSTRAÇÕES
Figura 1- Imagens de uma partida de pôquer (esq.) e uma partida de paciência (dir.) ............ 17
Figura 2 - Arquitetura da plataforma Android ...................................................................... 18
Figura 3 - Representação da pilha de atividades ................................................................... 21
Figura 4 - LinearLayout ....................................................................................................... 23
Figura 5 - TableLayout......................................................................................................... 24
Figura 6 – RelativeLayout .................................................................................................... 25
Figura 7 – Exemplo de aplicação do MJ3I ............................................................................ 35
Figura 8 - Interface do BGE ................................................................................................. 37
Figura 9 - Interface do Havok Physics .................................................................................. 38
Figura 10 – Diagrama de casos de uso .................................................................................. 40
Figura 11 – Diagrama de classes do motor de jogos ............................................................. 41
Figura 12 – Pacote engine.Model ................................................................................... 42
Figura 13 – Pacote engine.View ..................................................................................... 43
Figura 14 - Pacote engine.View (cont.)........................................................................... 43
Figura 15 – Tela principal .................................................................................................... 44
Figura 16 – Tela de seleção de jogo...................................................................................... 44
Figura 17 – Tela de seleção de jogadores ............................................................................. 44
Figura 18 – Menu de exibição .............................................................................................. 45
Figura 19 – engine.Control ......................................................................................... 45
Figura 20 – Pacote engine.GameMode ............................................................................ 47
Figura 21 – Diagrama de sequência do caso de uso UC01 .................................................... 48
Figura 22 – Tela de opções................................................................................................... 62
Figura 23 – Listagem de cores .............................................................................................. 62
Figura 24 – Tela de jogo ...................................................................................................... 63
Figura 25 – Opções de apostas ............................................................................................. 63
Figura 26 – Regulamentos do jogo ....................................................................................... 64
Figura 27 – Pontuação do jogo ............................................................................................. 64
LISTA DE QUADROS
Quadro 1 - Estrutura XML de um TableLayout .................................................................... 24
Quadro 2 – Estrutura XML de um RelativeLayout ............................................................... 25
Quadro 3 - Exemplo de inserção de recursos ........................................................................ 27
Quadro 4 – AndroidManifest.xml ......................................................................................... 29
Quadro 5 – res/layout/gamevalues.xml ................................................................................. 31
Quadro 6 – res/values/strings.xml ........................................................................................ 32
Quadro 7 – res/values/attrs.xml ............................................................................................ 33
Quadro 8 – Múltipla tipagem de constantes .......................................................................... 33
Quadro 9 – Acessando constantes tipadas............................................................................. 33
Quadro 10 – res/menu/game_menu.xml ............................................................................... 34
Quadro 11 – Instância de menus e tratamento dos botões ..................................................... 34
Quadro 12 – XML parcial da tela de entrada ........................................................................ 44
Quadro 13 – Inicialização do GameDisplay ..................................................................... 50
Quadro 14 – Inicialização de variáveis do jogo .................................................................... 51
Quadro 15 – setGame() ................................................................................................... 51
Quadro 16 – Definindo as propriedades do jogo ................................................................... 52
Quadro 17 – Rotina de distribuição das cartas ...................................................................... 53
Quadro 18 – Ciclo de desenho .............................................................................................. 54
Quadro 19 – Atualização da posição das cartas .................................................................... 55
Quadro 20 – Verificação de posição ..................................................................................... 55
Quadro 21 – Ciclo de desenho de cartas em movimento ....................................................... 56
Quadro 22 – Função onBackPressed() ......................................................................... 57
Quadro 23 – Verificação do botão de apostas ....................................................................... 58
Quadro 24 – onPause() ................................................................................................... 60
Quadro 25 – MergeSort de cartas ..................................................................................... 61
Quadro 26 – Lista de pontuações do jogo de pôquer ............................................................. 61
Quadro 27 – Caso de uso UC01 ........................................................................................... 70
Quadro 28 – Caso de uso UC02 ........................................................................................... 71
Quadro 29 – Caso de uso UC03 ........................................................................................... 72
Quadro 30 – Caso de uso UC04 ........................................................................................... 73
Quadro 31 – Caso de uso UC05 ........................................................................................... 74
Quadro 32 – Caso de uso UC06 ........................................................................................... 75
LISTA DE TABELAS
Tabela 1 – Média de quadros por segundo (fps) ................................................................... 65
LISTA DE SIGLAS
AAPT - Android Asset Packaging Tool
ADT – Android Development Tools
AI - Artificial Intelligence
API – Application Programming Interface
ARGB - Alpha, Red, Green, Blue
BGE - Blender Game Engine
GB – Giga Bytes
GHz – Giga Hertz
GPU – Graphic Processing Unit
IDE - Integrated Development Environment
JIT – Just In Time
J2ME - Java 2 Micro Edition
M3GE - Mobile 3D Game Engine
MB – Mega Bytes
MJ3I - Motor de Jogos 3D para o IPhone OS
OpenGL ES - Open Graphic Library for Embedded Systems
RAM - Random Access Memory
SDK - Software Development Kit
UML - Unified Modeling Language
USB - Universal Serial Bus
XML - eXtensible Markup Language
SUMÁRIO
1 INTRODUÇÃO ............................................................................................................. 13
1.1 OBJETIVOS DO TRABALHO..................................................................................... 14
1.2 ESTRUTURA DO TRABALHO .................................................................................. 14
2 FUNDAMENTAÇÃO TEÓRICA................................................................................. 15
2.1 MOTOR DE JOGOS..................................................................................................... 15
2.2 PADRÕES DE JOGOS DE CARTAS ........................................................................... 16
2.3 ANDROID .................................................................................................................... 17
2.4 PROGRAMAÇÃO JAVA PARA ANDROID ............................................................... 19
2.4.1 FrameLayout............................................................................................................... 22
2.4.2 LinearLayout .............................................................................................................. 22
2.4.3 TableLayout ................................................................................................................ 23
2.4.4 RelativeLayout............................................................................................................ 24
2.5 RECURSOS GRÁFICOS NO ANDROID .................................................................... 26
2.5.1 Bibliotecas 2D ............................................................................................................ 26
2.5.1.1 Views ....................................................................................................................... 26
2.5.1.2 Canvas ...................................................................................................................... 27
2.5.2 OpenGL ES ................................................................................................................ 27
2.5.2.1 GLSurfaceView ........................................................................................................ 28
2.5.2.2 Renderer ................................................................................................................... 28
2.6 RESOURCES E XMLS ................................................................................................ 28
2.6.1 AndroidManifest.xml .................................................................................................. 29
2.6.2 res.layout.............................................................................................................. 30
2.6.3 res.drawable ......................................................................................................... 31
2.6.4 res.values.............................................................................................................. 32
2.6.5 Personalizando menus do sistema ............................................................................... 33
2.7 TRABALHOS CORRELATOS .................................................................................... 35
2.7.1 MJ3I ........................................................................................................................... 35
2.7.2 M3GE ......................................................................................................................... 36
2.7.3 Blender Game Engine (BGE) ...................................................................................... 36
2.7.4 Havok ......................................................................................................................... 37
3 DESENVOLVIMENTO ................................................................................................ 39
3.1 REQUISITOS PRINCIPAIS DO PROBLEMA A SER TRABALHADO ..................... 39
3.2 ESPECIFICAÇÃO ........................................................................................................ 40
3.2.1 Casos de uso ............................................................................................................... 40
3.2.2 Diagramas de classe .................................................................................................... 40
3.2.2.1 Pacote engine.Model .......................................................................................... 41
3.2.2.2 Pacote View .............................................................................................................. 43
3.2.2.3 Pacote engine.Control ............................................................................ 45
3.2.2.4 Pacote engine.GameMode ................................................................................... 46
3.2.3 Diagrama de Sequência ............................................................................................... 48
3.3 IMPLEMENTAÇÃO .................................................................................................... 48
3.3.1 Técnicas e ferramentas utilizadas ................................................................................ 49
3.3.2 Inicialização da partida ............................................................................................... 49
3.3.3 Algoritmos gráficos .................................................................................................... 53
3.3.4 Algoritmos de controle................................................................................................ 57
3.3.4.1 onBackPressed() .............................................................................................. 57
3.3.4.2 onTouch() ............................................................................................................ 58
3.3.4.3 onPause() ............................................................................................................ 59
3.3.4.4 roundWinner() ................................................................................................... 60
3.3.5 Operacionalidade da implementação ........................................................................... 62
3.4 RESULTADOS E DISCUSSÃO ................................................................................... 64
4 CONCLUSÕES ............................................................................................................. 66
4.1 EXTENSÕES ............................................................................................................... 67
REFERÊNCIAS BIBLIOGRÁFICAS .............................................................................. 68
ANEXO A – UC01 - Iniciar a partida ............................................................................... 70
ANEXO B – UC02 - Alterar as configurações de jogo ..................................................... 71
ANEXO C – UC03 - Selecionar as opções de jogo ............................................................ 72
ANEXO D – UC04 - Distribuir as cartas .......................................................................... 73
ANEXO E – UC05 - Mover as cartas da mão ................................................................... 74
ANEXO F – UC06 - Modificar o valor da aposta ............................................................. 75
13
1 INTRODUÇÃO
Para 2011, tem-se que a previsão de vendas dos smartphones será superior a dos
computadores desktop no Brasil (UOL TECNOLOGIA, 2011). Entre os smartphones mais
populares estão os que utilizam a plataforma Android. Com esta popularização, há uma
grande demanda por desenvolvimento de aplicativos para estes dispositivos, nas quais os
aplicativos direcionados para o entretenimento, especialmente jogos, e outros são bastante
visados. Porém, para se desenvolver jogos para smartphones, assim como para outras
plataformas, é necessário tempo e conhecimento. Neste ponto os motores de jogos1 são muito
utilizados para reduzir o tempo de produção dos jogos, pois são reaproveitados vários
recursos comuns a múltiplos jogos, de forma que o desenvolvedor possa se dedicar mais aos
detalhes específicos do jogo que está desenvolvendo do que com as necessidades padrões dos
jogos do gênero.
Diante disso, este trabalho tem como propósito facilitar o entendimento sobre o
desenvolvimento de aplicativos dentro da plataforma Android a partir da implementação de
um motor de jogos de cartas. Este motor tem como principal característica a gerência dos
componentes vitais de um jogo de cartas, tais como o baralho, a pilha de descartes, a
diferenciação de valores e naipes das cartas, entre outras características. Este motor atua em
três frentes principais: o gerenciamento da estrutura de cartas, a interface com o usuário e as
regras de jogo. A primeira frente visa a geração de cartas do baralho e sua sequência de
retirada das cartas. A segunda é encarregada de produzir o desenho dos objetos do aplicativo
na tela, a interação do toque do usuário com o aplicativo e efetuar as rotinas de movimento
das cartas dentro do espaço gráfico. A terceira deve ser responsável pela definição de regras
que um jogo deve seguir, tais como a determinação da ordem de prioridade das cartas, a
pontuação da partida, os critérios de desempate entre outros. Para validar o motor proposto,
foi implementado um protótipo de jogo de pôquer, o qual foi testado tanto em simuladores
quanto em um dispositivo Android.
1 Um motor de jogo é um software ou biblioteca responsável por implementar parcialmente ou integralmente as
funcionalidades de um jogo, podendo ser encontrada na forma de ferramentas ou bibliotecas.
14
1.1 OBJETIVOS DO TRABALHO
O objetivo deste trabalho é desenvolver um motor de jogo para facilitar o
desenvolvimento de jogos de cartas para a plataforma Android.
Os objetivos específicos do trabalho são:
a) disponibilizar um gerador de cartas de um baralho que permita armazenar uma
sequência fixa ou aleatória do baralho durante a partida;
b) disponibilizar funções para a distribuição das cartas entre o(s) jogador(es);
c) disponibilizar recursos gráficos para a manipulação de cartas pelo cenário;
d) determinar um conjunto limitado de constantes para auxiliar na implementação do
protótipo de jogo, sendo algumas delas: a quantidade de cartas que cada jogador
poderá segurar, a sequência das cartas disponíveis no jogo e o valor de cada carta
entre outros valores pré-definidos;
e) disponibilizar um motor para jogos de cartas e validá-lo através de um estudo de
caso;
f) implementar um protótipo de jogo utilizando o motor desenvolvido;
g) disponibilizar os conjunto de cartas do baralho padrão francês2 e suas variantes.
1.2 ESTRUTURA DO TRABALHO
A estrutura deste trabalho é dividido em quatro capítulos, sendo que o segundo
capítulo contém a fundamentação teórica necessária para o entendimento do trabalho.
O terceiro capítulo apresenta o desenvolvimento do trabalho utilizando diagramas de
casos de uso, de classes e de sequência que definem o motor e o protótipo. Neste capítulo
também é apresentado as principais rotinas utilizadas na aplicação além de resultados e
discussões que ocorreram durante o desenvolvimento deste trabalho.
Por fim, o quarto capítulo apresenta as conclusões do trabalho e sugestões para
trabalhos futuros.
2 O baralho padrão francês é o mais utilizado atualmente e é composto por 52 cartas divididas em quatro naipes
(espadas, ouros, copas e paus). Em alguns países, foi inserido ao baralho um conjunto de cartas denominadas
curingas, ou jokers, que são cartas independentes de valor ou naipe.
15
2 FUNDAMENTAÇÃO TEÓRICA
A seção 2.1 aborda o conceito de motores de jogos. A seção 2.2 trata da definição dos
jogos de cartas. A seção 2.3 apresenta um conceito geral sobre a plataforma Android. A seção
2.4 descreve as peculiaridades da programação Java dentro da plataforma. A seção 2.5 traz
conceitos sobre os recursos gráficos disponíveis na plataforma. A seção 2.6 descreve a forma
como os arquivos XML são utilizados no aplicativo. Por fim, a seção 2.7 apresenta trabalhos
correlatos ao projeto.
2.1 MOTOR DE JOGOS
Segundo Ward (2008), um motor de jogos, também conhecido como game engine, é
uma abstração de detalhes como as tarefas comuns a jogos, como renderização, física, entrada
de dados, interface gráfica com o usuário e até inteligência artificial. Porém, o conteúdo, a
modelagem gráfica e texturas, o significado atrás das entradas, colisões e a interação dos
objetos no ambiente são os componentes que definem o jogo.
O termo game engine originou-se durante a década de 90 com a popularização de
jogos 3D de tiro em primeira pessoa (First Person Shooter) tais como Doom e Quake. Devido
ao fato de suas arquiteturas consistirem em componentes bem definidos e separados do núcleo
do jogo (renderização, tratamento de colisões ou áudio) da arte, regras de jogo e experiência
de jogo, permitiram que seus desenvolvedores criassem novos produtos alterando elementos
do mundo e as regras de jogo com mínimas alterações no motor, marcando desta forma o
início da ―comunidade de mods‖. Esta comunidade é composta por grupos de jogadores e
pequenos estúdios que constroem novos módulos (mods) ou jogos utilizando ferramentas
disponibilizadas pelos desenvolvedores do jogo original (GREGORY; LANDER, 2009,
pg.11-12).
Um motor de jogos pode ser desenvolvido generalizando-se partes da implementação
para qualquer gênero de jogo, inclusive jogos multiplataforma. Porém, vários autores, como
Gregory e Lander (2009, pg.11-12), afirmam que para fins de otimização e melhor resultado,
deve ser definida uma plataforma e um gênero específico para o motor.
16
Atualmente são encontrados na internet inúmeros motores de jogos disponíveis
gratuitamente ou licenciados por seus desenvolvedores para diversos fins. Não
necessariamente específicas para jogos, estes motores também podem ser encontrados na
produção de animações e filmes, além de simuladores para fins educativos, militares entre
outros. Na maior parte dos casos, costuma-se encontrá-las em forma de APIs ou SDKs, e
principalmente no caso das gratuitas, é comum encontrar comunidades de desenvolvimento
através de fóruns nas quais os desenvolvedores podem sugerir e compartilhar novos recursos,
melhorias ou corrigir problemas encontrados.
2.2 PADRÕES DE JOGOS DE CARTAS
Segundo Schuytema e Manyen (2005, p. 92), um jogo é uma atividade composta por
um conjunto de ações limitadas a um conjunto de regras, direcionadas para um determinado
fim. Estas regras servem para delimitar e contextualizar as ações do jogador, além de criar
situações de desafio e oposição ao jogador. Os jogos de cartas, muitas vezes associados como
―jogos de azar‖, também compartilham um conjunto de regras com o intuito de se alcançar
um fim. Atualmente são encontrados vários jogos de cartas com suas próprias regras e cartas
personalizadas, porém, este documento trata somente de jogos nas quais são utilizadas as
cartas existentes no baralho francês.
O baralho francês, assim como o espanhol, é composto por quatro conjuntos de cartas
denominados naipes: Paus (♣), Espadas (♠), Copas (♥) e Ouros (♦). Cada naipe é composto
por um conjunto de 13 cartas, contendo os valores de 2 a 10 e as cartas: Ás (A ou Ace), Valete
(J ou Jack), Dama (Q ou Queen) e Rei (K ou King), que representam os valores 1,11,12 e 13,
respectivamente. Em alguns jogos, utiliza-se um par de cartas especiais sem naipe chamados
―Coringas‖. Existem, porém, alguns jogos onde há uma sequência diferente de valores e
naipes que ainda podem conter variações dependendo da regionalidade dos jogadores, como
por exemplo, o jogo de truco, que possui regras diferentes em cada região do Brasil e em
outros países onde é jogado. Ainda há regras como as do jogo de canastra que permite o uso
do Ás como o décimo quarto valor (após o K), porém se esta carta for utilizada para este fim,
não poderá ser utilizada para iniciar uma nova sequência.
A origem dos jogos de cartas é desconhecida, mas acredita-se que originou na China
17
por volta do século X sendo trazido por árabes para a Europa durante o século XIV
(JOGATINA, 2009). Acredita-se que o baralho francês foi inventado por um pintor chamado
Jacquemin Gringonneur, sob encomenda do rei Carlos VI de França. Gringonneur elaborou
um baralho de forma que cada naipe representasse um grupo social da França: copas para o
clero, ouro para a burguesia, espadas para os militares e paus para os camponeses (JOGOS
DE CARTAS, 2007). A Figura 1, ilustra alguns jogos populares na atualidade.
Fonte: Genkistar (2011).
Figura 1- Imagens de uma partida de pôquer (esq.) e uma partida de paciência (dir.)
2.3 ANDROID
O Android é uma plataforma de software para dispositivos móveis que inclui um
sistema operacional, uma camada intermediária (middleware) e aplicativos chave (ANDROID
DEVELOPERS, 2011). A Figura 2 demonstra a estrutura do sistema operacional do Android
dividida em grandes componentes.
18
Fonte: Android Developers (2011).
Figura 2 - Arquitetura da plataforma Android
O desenvolvimento de aplicações para Android é feito utilizando a linguagem Java na
máquina virtual Dalvik, que se trata de uma máquina virtual otimizada para dispositivos
móveis. Esta máquina virtual utiliza um baixo consumo de memória, para que seja possível
instanciá-la múltiplas vezes, auxiliando no gerenciamento do sistema operacional através de
threads3. Para controlar o gerenciamento de memória e de threads, esta máquina virtual
utiliza o kernel Linux. Cada aplicação utiliza um ID próprio e é executado em uma máquina
virtual distinta. Uma ferramenta chamada DX que está inserida junto ao Android SDK trata de
comprimir e converter arquivos .class Java em arquivos .dex utilizados pela máquina
virtual Dalvik.
Todas aplicações do Android devem conter um arquivo chamado
AndroidManifest.xml que é responsável por informar ao sistema operacional os
componentes utilizados pela aplicação. O manifest também é responsável por fornecer a
ordem de prioridade dos componentes, mapear as Application Programming Interface (API)
utilizadas pela aplicação, definir a versão do sistema operacional a ser utilizada, recursos de
software e hardware que a aplicação irá utilizar e definir parâmetros de comportamento de
cada componente (ANDROID DEVELOPERS, 2011).
Aplicações desenvolvidas para o Android podem ser distribuídas gratuitamente ou
vendidas utilizando o serviço Android Market, mantido pela Google, que permite a
3 Thread é um processo ou tarefa concorrente utilizado em sistemas multitarefas que dependente de um criador.
19
divulgação, distribuição e licenciamento dos aplicativos registrados no site (ANDROID
DEVELOPERS, 2011).
O Android disponibiliza várias classes relacionadas ao hardware dos dispositivos que o
comportam, dentre elas está a classe de câmera e sensores de toque, acelerômetro e bússola.
Também está disponível classes de entrada USB, mídia, banco de dados, entre outros
(ANDROID DEVELOPERS, 2011). A seção 2.4 irá detalhar um pouco mais sobre algumas
dessas classes.
2.4 PROGRAMAÇÃO JAVA PARA ANDROID
Como anteriormente citado, as aplicações desenvolvidas para a plataforma Android
são escritas na linguagem Java. Entretanto, para se desenvolver estes aplicativos, é necessária
a utilização do Android SDK, responsável por compilar o código e arquivos do projeto e
armazená-los em um único ―pacote‖, um arquivo com extensão .apk que será responsável pela
instalação da aplicação no dispositivo (ANDROID DEVELOPERS, 2011).
Pela preocupação na utilização de recursos nos dispositivos móveis, ao se tratar de
desempenho nas aplicações para o Android, no qual pode implicar diretamente no consumo
de bateria que a aplicação utilizará, segundo os desenvolvedores do Android, duas regras
básicas devem ser seguidas: ―Não faça trabalho desnecessário‖ e ―Evite alocar memória se
possível‖ (ANDROID DEVELOPERS, 2011).
Com base nas regras citadas acima, os desenvolvedores do Android consideram
algumas formas de reduzir a utilização de recursos do aplicativo (ANDROID DEVELOPERS,
2011), sendo elas:
a) evitar criar objetos desnecessários: variáveis temporárias de curta utilização devem
ser evitadas, pois uma menor quantidade de objetos significa chamadas menos
frequentes do Garbage Collector, reduzindo assim consideravelmente o consumo
de memória;
b) preferir o uso de métodos estáticos a virtuais: o uso de métodos estáticos tornam-
nos de 15% a 20% mais rápidos;
c) evitar o uso de getters e setters: a utilização destas funções são bastante difundidas
na programação orientada a objetos, porém as chamadas destas funções são de um
20
alto custo, muito maiores que as chamadas diretas de suas respectivas variáveis. É
aceitável o uso das funções ao utilizar chamadas entre classes. Porém dentro de
uma mesma classe, a variável deve ser sempre acessada diretamente, pois além do
consumo de memória, o acesso a variáveis diretamente é três vezes mais rápida, ou
até 7 vezes em sistemas com Just-In-Time4 (JIT);
d) utilizar static final para constantes: a utilização do parâmetro final facilita o
acesso ao valor da constante, pois ao invés de consultar uma tabela de constantes, o
valor é diretamente acessado. Esta regra aplica-se somente para tipos primitivos
como int e String;
e) utilizar o loop For otimizado: aconselha-se o uso de for-each na interação de
vetores, pois ele pode tornar o loop até três vezes mais rápido;
f) conhecer e usar as bibliotecas: métodos da biblioteca do sistema podem ser
melhores que o melhor método produzido pelo JIT para um equivalente escrito
manualmente em Java.
Os aplicativos desenvolvidos para o Android contêm quatro principais componentes
(ANDROID DEVELOPERS, 2011):
a) atividades (activities): representa uma tela única de interface com o usuário. Cada
atividade é independente das outras e outra aplicação pode chamá-la se a atividade
permitir;
b) serviços (services): representa um componente sem interface com o usuário que
roda ocultamente para executar processos de longa duração ou remotos;
c) provedores de conteúdo (content providers): gerenciador de dados da aplicação,
controlando seu acesso ao banco de dados e se necessário, alterar dados do usuário;
d) receptores de transmissão (broadcast receivers): este componente gerencia as
notificações e alertas da aplicação, sendo utilizado para comunicar-se com outros
aplicativos quando uma requisição for concluída.
Dos quatro componentes citados acima, o mais utilizado é a atividade pelo fato desta
ser o componente responsável por exibir uma interface que permita ao usuário interagir. Uma
aplicação pode conter apenas uma tela ou múltiplas, contidas em suas respectivas atividades.
Cada atividade permite que seja chamada uma nova atividade que é colocada à frente da atual,
4 JIT é um compilador desenvolvido para a máquina virtual Dalvik, disponível a partir do Android 2.2, que
analisa o código da aplicação e o converte para uma forma mais eficiente enquanto o aplicativo está rodando
(ANDROID DEVELOPERS, 2010).
21
sendo armazenado numa pilha de atividades, também conhecido como back-stack, conforme
ilustra a Figura 3.
Fonte: Android Developers (2011).
Figura 3 - Representação da pilha de atividades
Cada atividade chamada antes da atual é armazenada na pilha guardando as variáveis
disponíveis no momento e quando pressionado o botão back, a atividade atual é destruída e o
conteúdo da atividade anterior é recuperado. Ao chamar uma nova atividade, a anterior é
parada, eventos de mudança de estado são disparados, como o onPause(), e recomenda-se
que ao mudar o estado da atividade os dados utilizados nesta atividade sejam gravados pois
não há garantias que o usuário retorne a esta atividade. Sugere-se também que ao parar uma
atividade, objetos grandes como conexões de rede ou bancos de dados, devem ser liberados e
recuperados no momento que a atividade for restituída (ANDROID DEVELOPERS, 2011).
Um conjunto de atividades pode ser agrupado em tarefas, permitindo que esse
conjunto possa ser movido para o plano de fundo quando pressionado o botão home sem que
as atividades sejam destruídas. Ao mover a tarefa para o plano de fundo, os dados das
atividades são armazenados e serão recuperados assim que o usuário retornar à aplicação.
Porém, caso o desenvolvedor desejar que as informações da atividade atual sejam preservadas
quando o usuário pressionar o botão back, será necessário armazená-las de alguma forma, nas
quais os meios mais utilizados são armazenando na classe SharedPreferences, que permite
armazenar dados de tipos primitivos como int, String, long entre outros, salvando no banco de
dados sqllite, nativo do Android ou armazenando-os em um arquivo (ANDROID
DEVELOPERS, 2011).
Os serviços são componentes que não necessitam de uma interface com usuário e
podem rodar operações de longo prazo em plano de fundo. Um serviço iniciado pela aplicação
22
continuará rodando mesmo após a aplicação perder o foco. Este componente costuma ser
utilizado para gerenciar conexões, tocar músicas e comunicar com bancos e dados. Cada
serviço, como os outros componentes, deve ser declarado no manifest e caso ele necessite
utilizar um grande consumo de recursos, ele deverá ser criado em um thread separada para
reduzir o risco de travar a aplicação (ANDROID DEVELOPERS, 2011).
Os provedores de conteúdo são os componentes responsáveis por efetuar a
comunicação entre aplicações, permitindo integrar vários tipos de dados comuns, tais quais
arquivos de áudio, vídeo, imagens, dados pessoais entre outros. Há duas formas de utilizar os
provedores: criando um nova instância da classe ContentResolver, ou utilizando um
provedor já existente desde que este seja de mesmo tipo e haja permissão de escrita
(ANDROID DEVELOPERS, 2011).
Para se implementar uma Graphic User Interface (GUI) para a aplicação, utiliza-se
derivados da classe View e ViewGroup. Cada View é responsável por uma região retangular na
atividade e permite que o usuário interaja nesta região. A classe View contém subgrupos
chamados widgets, objetos de interface como botões e campos de texto, enquanto o
ViewGroup contém as subclasses de layouts, responsáveis por estruturar os componentes na
tela (ANDROID DEVELOPERS, 2011).
Há quatro tipos principais de layouts: FrameLayout, LinearLayout, TableLayout e
RelativeLayout.
2.4.1 FrameLayout
O tipo mais simples de layout. É composto por um elemento que ocupa todo o espaço
da tela e pode ser ocupado total ou parcialmente por somente um elemento, como uma
imagem por exemplo. O objeto inserido neste layout sempre será fixo no canto superior
esquerdo da tela, e novos objetos inseridos neste layout irão sobrepor os objetos anteriores.
2.4.2 LinearLayout
Este layout alinha os componentes associados a ele na direção horizontal ou vertical
23
dependendo de qual for o parâmetro definido na propriedade orientation. Cada elemento
filho do layout é empilhado abaixo do elemento anterior. Este layout permite também que
seus elementos tenham pesos diferenciados, permitindo assim que elementos com maior peso
preencham um espaço maior na tela, como pode ser visto na Figura 4. A imagem da esquerda
ilustra elementos sem peso definido, que por padrão o valor é zero, enquanto na imagem da
direita, o campo de comentários tem um peso igual a 1 dando maior prioridade a ele. Caso o
campo de nome também tivesse peso 1, os dois campos teriam o mesmo tamanho.
Fonte: Android Developers (2011).
Figura 4 - LinearLayout
2.4.3 TableLayout
Este layout utiliza uma estrutura de tabela, dividido em linhas e colunas. Cada tabela
irá possuir a quantidade de colunas igual à linha com o maior número de células dentro dela.
Cada linha deste layout é definido pelo objeto TableRow, que pode conter zero ou mais
células e cada célula é definida por qualquer componente da classe View. O Quadro 1
demonstra o eXtensible Model Language (XML) que estrutura uma tabela simples contendo
duas linhas e duas colunas, como pode ser visualizado na Figura 5.
24
<?xml version="1.0" encoding="utf-8"?>
<TableLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:stretchColumns="1">
<TableRow>
<TextView
android:text="@string/table_layout_4_open"
android:padding="3dip" />
<TextView
android:text="@string/table_layout_4_open_shortcut"
android:gravity="right"
android:padding="3dip" />
</TableRow>
<TableRow>
<TextView
android:text="@string/table_layout_4_save"
android:padding="3dip" />
<TextView
android:text="@string/table_layout_4_save_shortcut"
android:gravity="right"
android:padding="3dip" />
</TableRow>
</TableLayout>
Fonte: Android Developers (2011).
Quadro 1 - Estrutura XML de um TableLayout
Fonte: Android Developers (2011).
Figura 5 - TableLayout
2.4.4 RelativeLayout
O RelativeLayout permite que os componentes dispostos nele sejam posicionados em
relação ao container ou a algum outro elemento pelo seu id. Caso seja utilizado um arquivo
XML para determinar os elementos do layout, deve-se criar o elemento base antes do
elemento que irá referenciá-lo. O Quadro 2 exemplifica uma estrutura simples de um layout
relativo com quatro componentes básicos, como pode ser visto na Figura 6.
25
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="@drawable/blue"
android:padding="10px" >
<TextView android:id="@+id/label"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Type here:" />
<EditText android:id="@+id/entry"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:background="@android:drawable/editbox_background"
android:layout_below="@id/label" />
<Button android:id="@+id/ok"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/entry"
android:layout_alignParentRight="true"
android:layout_marginLeft="10px"
android:text="OK" />
<Button android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toLeftOf="@id/ok"
android:layout_alignTop="@id/ok"
android:text="Cancel" />
</RelativeLayout>
Fonte: Android Developers (2011).
Quadro 2 – Estrutura XML de um RelativeLayout
Fonte: Android Developers (2011).
Figura 6 – RelativeLayout
26
2.5 RECURSOS GRÁFICOS NO ANDROID
Android Developers (2011), comenta que há três principais recursos gráficos
disponíveis na plataforma, sendo eles as bibliotecas gráficas 2D do pacote drawable, as APIs
de Open Graphic Library for Embedded Systems (OpenGL ES) e as APIs do RenderScript.
Essas bibliotecas devem ser utilizadas com consciência da necessidade que a aplicação irá
utilizar, pois cada uma possui características que permitem facilitar o desenvolvimento em
determinados casos, conforme comentado a seguir.
2.5.1 Bibliotecas 2D
Há duas formas principais de se desenhar gráficos em 2D, sendo elas a utilização de
Views ou desenhando diretamente em um Canvas (ANDROID DEVELOPERS,2011).
2.5.1.1 Views
Para o desenvolvimento de gráficos e animações em 2D, existem duas bibliotecas
disponíveis no Android para desenhar imagens e formas, sendo elas a
android.graphics.drawable e a android.view.animation.
Segundo Android Developers (2011), um objeto da classe Drawable é ―algo que pode
ser desenhado‖. Há três formas de se instanciar um objeto Drawable: utilizando uma imagem
existente dentro do projeto, utilizando um arquivo XML para definir as propriedades do
objeto ou utilizando construtores da classe.
Utilizar imagens salvas dentro do projeto são recomendadas para a utilização em
ícones, logos ou outros gráficos como os utilizados em um jogo. As imagens suportadas pela
classe Drawable são: Portable Network Graphics (PNG) que é preferível,
Joint Photographic Experts Group (JPG) que é considerado aceitável e
Graphics Interchange Format (GIF) é desencorajado pela plataforma. Para utilizar uma
imagem, ela deve estar inserida na pasta res/drawable/ ou na pasta res/raw/. Caso ela
esteja na pasta res/drawable/, na construção do projeto, uma ferramenta chamada Android
27
Asset Packaging Tool (AAPT), responsável por criar a classe R.java, que automaticamente
efetua a compressão otimizada de imagens sem perda de qualidade, reduzindo assim o
consumo de memória ao utilizar esta imagem. Por exemplo, uma imagem PNG que utiliza
menos que 256 cores, é convertido para um PNG 8 bits. Porém, caso a imagem deva ser
utilizada em seu estado original na forma de bitmap, a imagem deve ser inserida na pasta
res/raw/. O Quadro 3 demonstra um exemplo de implementação de uma imagem chamada
card_background_blue.png existente dentro da pasta drawable/nodpi/ no projeto
(ANDROID DEVELOPERS, 2011).
Resources res = getContext().getResources();
Drawable img = res.getDrawable(R.drawable.card_background_blue);
Quadro 3 - Exemplo de inserção de recursos
2.5.1.2 Canvas
Segundo Android Developers (2011), desenhar utilizando o Canvas é melhor em casos
onde é necessário que a tela seja redesenhada regularmente. O Canvas atua como um
intermediador armazenando as chamadas de desenho do aplicativo e convertendo-os em um
Bitmap que será inserido na tela. É possível criar um novo Canvas passando como parâmetro
um Bitmap que define a área de desenho, porém recomenda-se que seja utilizado o Canvas
disponível pela função View.onDraw() ou SurfaceHolder.lockCanvas(). A classe
disponibiliza várias funções de desenho como o drawRect() e o drawBitmap(), permitindo
também utilizar recursos da classe Drawable.
2.5.2 OpenGL ES
A biblioteca OpenGL para a plataforma Android disponibiliza gráficos de alta
performance tanto para 2D como 3D utilizando a API OpenGL ES. No Android, há duas
classes fundamentais que permitem a criação e manipulação de gráficos pelo OpenGL ES.
28
2.5.2.1 GLSurfaceView
Esta classe derivada da classe View, é muito semelhante à classe SurfaceView, que é
uma View que permite ser alterado seu formato e tamanho utilizando ordenação por
profundidade para organizar seus objetos. Esta classe é responsável por gerenciar superfícies
que são componentes especiais na visualização no Android, renderização utilizando threads
dedicadas para melhorar o desempenho e suporta renderização sob demanda ou contínuo.
Toda GLSurfaceView necessita de um Renderer, que é registrado pela função
setRenderer(Renderer). Após definido o Renderer, é possível alterar seu modo padrão de
renderização contínua para sob demanda.
2.5.2.2 Renderer
A interface genérica Renderer deriva do pacote GLSurfaceView e é responsável por
fazer o OpenGL desenhar os quadros na tela. Cada Renderer gera uma nova thread, para que
o desempenho da aplicação não seja prejudicada.
2.6 RESOURCES E XMLS
Os aplicativos do Android têm forte vínculo com arquivos XML, desde o mapeamento
dos componentes do sistema até os layouts e atributos das classes. A partir do arquivo
AndroidManifest.xml (Quadro 4), ao compilar o projeto são mapeados os recursos e
propriedades utilizados pelo aplicativo que deseja-se aplicar a todas as atividades ou a um
algumas em específico.
Ao criar um novo projeto Android no Eclipse, é criada uma pasta chamada res que
possui por padrão as pastas drawable, layout e values. Os arquivos inseridos dentro da
pasta res devem utilizar somente letras minúsculas, números e sublinhado, sendo que o
primeiro caractere deve ser uma letra.
29
2.6.1 AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="(...)/apk/res/android" package="engine.View">
<uses-sdk android:targetSdkVersion="10" android:minSdkVersion="10" />
<application android:icon="@drawable/card_background_blue"
android:debuggable="true"
android:label="Card Engine"
android:theme="@android:style/Theme.NoTitleBar.Fullscreen">
<activity android:name="Home">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<activity android:name="GameDisplay"
android:screenOrientation="landscape" />
<activity android:name="Option" />
<activity android:name="ScoreBoard" />
<activity android:name="GameRules" />
</application>
</manifest>
Quadro 4 – AndroidManifest.xml
No Manifest descrito no quadro acima, dentro do nó raiz <manifest> é necessário
utilizar o atributo package, pois este define o caminho onde a classe R se localiza. Em geral a
maioria das propriedades que se dizem respeito ao às configurações do aplicativo iniciam-se
com o prefixo ―android:‖.
Dentro do nó raiz, o primeiro nó <uses-sdk> define a versão alvo do aplicativo. O
valor 10 dentro desses parâmetros se refere à versão 2.3.3 do Android.
O segundo nó detalha as informações necessárias para a aplicação em si, onde o
primeiro parâmetro android:icon se refere ao caminho do ícone que será exibido no
dispositivo. O parâmetro android:debuggable com valor true permite que o desenvolvedor
possa utilizar o modo debug da ferramenta no aplicativo. O parâmetro android:label define
o título do aplicativo que o usuário verá no dispositivo e na barra de título caso esta esteja
disponível. O valor Theme.NoTitleBar.Fullscreen do ultimo parâmetro define que em
todos os momentos dessa aplicação não haverá nem a barra de título do aplicativo nem a barra
de status do sistema.
Dentro do segundo nó estão contidos os nodos que definem as atividades (telas) que
compõem a aplicação. Cada atividade deve conter o nome da classe estendida de Activity
que será utilizada na aplicação, caso contrário, ao instanciar esta classe, será disparado um
erro de atividade não encontrada.
30
O nó Home possui um sub-nó <intent-filter> onde é definido que esta atividade
inicia a aplicação.
No nó GameDisplay, a propriedade android:screenOrientation, força que esta
atividade seja somente exibida em modo paisagem. Contudo, pode ser definido também para
somente modo retrato (portrait) e por padrão o sistema adapta os elementos da tela em relação
a orientação do dispositivo.
2.6.2 res.layout
A pasta layout contém as estruturas das atividades que são utilizadas no projeto,
podendo cada XML utilizar tanto Views pré-definidas pelo ambiente, como botões, campos
de texto e rótulo, como mapear novas Views personalizadas que serão criadas pelo
desenvolvedor.
Ao definir o layout de uma atividade, todas as Views inseridas dentro do XML devem
conter os atributos android:layout_height e android:layout_width que podem receber
os valores wrap_content, match_parent e fill_parent ou valores numéricos fixos nos
seguintes formatos de medidas: px (pixels), dp (pixels independentes de densidade), sp (pixels
escalados, baseados no tamanho preferencial de fonte), in (polegadas) e mm (milímetros)
(ANDROID DEVELOPERS, 2011). O valor fill_parent a partir da versão 8 da API do
Android foi depreciado em favor do valor match_parent e ambos tem a função de
dimensionar a View de forma a preencher todo o espaço ocupado pela View que a contém, ou
preencher toda a tela caso esta seja a View raiz. No Quadro 5 é possível visualizar o layout da
tela de regras da atividade GameRules descrito no arquivo gamerules.xml.
31
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/bghome">
<ScrollView
android:padding="15px"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fillViewport="true">
<RelativeLayout
android:padding="10px"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="@+id/PokerRules"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/poker_rules" />
<Button android:id="@+id/RulesReturn"
android:background="@drawable/btreturn"
android:layout_below="@id/PokerRules"
android:layout_centerHorizontal="true"
android:layout_margin="15px"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</RelativeLayout>
</ScrollView>
</LinearLayout>
Quadro 5 – res/layout/gamevalues.xml
Cada View pode possuir uma imagem ou cor de fundo sendo o arquivo buscado através
do caminho ―@drawable/nome_do_arquivo‖ ou utilizando os valores hexadecimais Alpha,
Red, Green, Blue (ARGB). As Views também podem possuir um id que é utilizado
posteriormente para ser referenciado por outros elementos ou funções. A sintaxe para a
definição de um id é ―@+id/nome_var‖ onde nome_var deve ser um id único no pacote. Para
acessar o id criado anteriormente, utiliza-se ―@id/nome_var‖ como pode ser visto nas
propriedades da View Button do quadro acima.
2.6.3 res.drawable
Por padrão, ao criar um novo projeto Android no Eclipse, são gerados três pastas
dentro da pasta res: drawable-hdpi, drawable-mdpi e drawable-ldpi. Estas três pastas
são geradas a fim de armazenar os componentes gráficos e exibi-los para o usuário
dependendo da resolução disponível no dispositivo, porém caso não se deseje trabalhar desta
32
forma, existe a opção de armazenar arquivos numa única pasta chamada drawable-nodpi
onde para todas as resoluções, será utilizada a mesma proporção para as imagens. Além das
proporções citadas antes, também estão disponíveis os valores xhdpi e tvdpi (ANDROID
DEVELOPERS, 2011).
2.6.4 res.values
A pasta values criada por padrão na geração do projeto contém o arquivo
strings.xml que por padrão armazena os valores literais que são utilizados na aplicação. O
Quadro 6 ilustra o conjunto de strings que são utilizadas na aplicação. Estas strings podem
ser acessadas a qualquer momento pelo desenvolvedor utilizando a referência
―R.string.string_name‖.
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="showRules">Rules</string>
<string name="showScore">Scoreboard</string>
<string name="exitGame">Quit Game</string>
<string name="colorChooser">Select a color:</string>
<string-array name="colors">
<item>black</item>
<item>blue</item>
<item>red</item>
<item>green</item>
<item>yellow</item>
<item>silver</item>
</string-array>
<string name="poker_rules">(…)</string>
</resources>
Quadro 6 – res/values/strings.xml
Dentro desta pasta também é permitido criar outros arquivos de propriedades como o
attrs.xml descrito no Quadro 7 que possui as propriedades do protótipo de pôquer.
33
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- deck type 0 = default, type 1 = spanish style -->
<item type="PokerVars" format="integer" name="deck_type">0</item>
<item type="PokerVars" format="integer" name="player_money">50</item>
<item type="PokerVars" format="integer" name="hand_limit">5</item>
<item type="PokerVars" format="integer" name="initialBet">5</item>
<item type="PokerVars" format="boolean" name="bet">true</item>
<item type="PokerVars" format="boolean" name="joker">false</item>
<item type="PokerVars" format="boolean" name="auto_draw">true</item>
<item type="PokerVars" format="boolean" name="upcard">false</item>
<!-- Multiplayer setup -->
<item type="PokerVars" format="integer" name="min_players">2</item>
<item type="PokerVars" format="integer" name="max_players">4</item>
<item type="PokerVars" format="boolean" name="discard">false</item>
<!-- Allows to shuffle the discard pile back to the deck -->
<item type="PokerVars" format="boolean"
name="restart_on_end">false</item>
</resources>
Quadro 7 – res/values/attrs.xml
Os itens do quadro anterior possuem uma variável type que permite que o
desenvolvedor insira um tipo qualquer, nativo ou mesmo inexistente, que auxilia ao mesmo
que encontre com maior facilidade para acessar as variáveis desejadas. Definir tipos diferentes
permite inserir outros nós com o mesmo nome, como pode ser visto no Quadro 8.
<item type="PokerVars" format="boolean" name="joker">false</item>
<item type="CanastraVars" format="boolean" name="joker">true</item>
Quadro 8 – Múltipla tipagem de constantes
Para o desenvolvedor acessar esta variável, deve-se buscar o tipo definido no
parâmetro format através dos Resources da aplicação passando como parâmetro o caminho
através da classe R (Quadro 9).
Resources res = getResources(); this.joker = res.getBoolean(R.PokerVars.joker);
Quadro 9 – Acessando constantes tipadas
2.6.5 Personalizando menus do sistema
Para criar um menu de opções do Android são necessários três procedimentos:
elaborar um layout, sobrescrever a função onCreateOptionsMenu() e atribuir as ações
quando um dos botões for selecionado utilizando a função onOptionsItemSelected().
34
O menu foi gerado a partir do arquivo res/menu/game_menu.xml (Quadro 10) onde
cada item possui um id para permitir mapear seu evento posteriormente, o ícone que deve ser
exibido na tela e o nome do botão para auxiliar o usuário a entender a utilidade dele.
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/showRules"
android:icon="@drawable/icocards"
android:title="@string/showRules" />
<item android:id="@+id/showScore"
android:icon="@drawable/icocoins"
android:title="@string/showScore" />
<item android:id="@+id/exitGame"
android:icon="@drawable/icoexit"
android:title="@string/exitGame" />
</menu>
Quadro 10 – res/menu/game_menu.xml
Após elaborado o layout, a View deve sobre-escrever as funções citadas
anteriormente (Quadro 11). Quando um item do menu é pressionado, a função
onOptionsItemSelected() é chamada recebendo como parâmetro o item selecionado
permitindo assim efetuar o tratamento do clique a partir do id definido no XML pela função
item.getItemId().
@Override
public boolean onCreateOptionsMenu(Menu menu){
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.game_menu, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle item selection
switch (item.getItemId()) {
case R.id.showRules:
showRules();
return true;
case R.id.showScore:
showScore();
return true;
case R.id.exitGame:
exitGame();
return true;
default: return super.onOptionsItemSelected(item);
}
}
Quadro 11 – Instância de menus e tratamento dos botões
35
2.7 TRABALHOS CORRELATOS
Nesta seção são descritos quatro trabalhos correlatos relacionados ao desenvolvimento
de motores de jogos para Android, quais sejam: Motor de Jogos 3D para o iPhone OS (MJ3I)
apresentado por Takano (2009), Mobile 3D Game Engine (M3GE), desenvolvido por
Pamplona (2005), Blender Game Engine, disponibilizado pela Blender Foundation
(VELDHUIZEN, 2010a) e a Havok, criado pela empresa de mesmo nome (HAVOK INC,
2011a).
2.7.1 MJ3I
Este projeto desenvolvido por Takano (2009) teve como foco o desenvolvimento de
um motor de jogos para o iPhone com foco na construção de um ambiente 3D utilizando o
OpenGL ES (Figura 7), gerenciamento de grafos de cena, importação de arquivos 3D e
tratamento de colisões para abstrair a implementação de rotinas básicas necessárias para um
jogo em 3D. A linguagem de programação utilizada no projeto foi o Objective-C no ambiente
de desenvolvimento xCode.
Fonte: Takano ( 2009).
Figura 7 – Exemplo de aplicação do MJ3I
36
2.7.2 M3GE
Pamplona (2005) descreve o protótipo de um motor de jogos 3D para dispositivos
móveis utilizando um algoritmo leitor de arquivos de modelo 3D e montagem visual do
cenário ao jogador, implementação de câmeras de visualização e movimentação do
personagem do cenário. Para o desenvolvimento do projeto foi utilizada a plataforma Java 2
Micro Edition (J2ME), uma das extensões do Java para sistemas embarcados, com o intuito
de além de criar uma ferramenta de desenvolvimento para jogos, testar a portabilidade e
velocidade de processamento onde o jogo gerado seria instalado.
2.7.3 Blender Game Engine (BGE)
O BGE, desenvolvido pela Blender Foundation, é uma ferramenta gratuita open
source, escrita na linguagem C++. Parte da ferramenta gráfica Blender sob a licença da GNU
GPL5, este motor contém ferramentas de produção gráfica em 2 e 3D, manipulação de grafos
de cena, entre outros recursos e permite a utilização da linguagem Python para a geração de
scripts (VELDHUIZEN, 2010a). Segundo Veldhuizen (2010a), o BGE utiliza um plugin de
um motor de física para jogos 3D chamado Bullet para gerenciar a detecção de colisão, a
dinâmica de corpos rígidos e maleáveis de objetos 3D criados na ferramenta. Este motor,
também utiliza a biblioteca Open Audio Library (OpenAL) para gerenciar os efeitos sonoros
do ambiente. A Figura 8 ilustra a interface do BGE.
5 GNU General Public Licence (GNU GPL), criada em 1989, foi a primeira licença para softwares livres e
atualmente a mais utilizada pelos desenvolvedores de softwares de código aberto.
37
Fonte: Veldhuizen (2010b).
Figura 8 - Interface do BGE
2.7.4 Havok
Este motor proprietário é considerado pela sua utilização multiplataforma em títulos de
sucesso como Soul Callibur IV (PlayStation 3, Xbox 360), Spore (Personal Computer - PC) e
StarCraft II (PC) entre outros (HAVOK INC, 2011a).
O motor é composto por sete módulos de ferramentas (HAVOK INC, 2011b), sendo
eles a Havok Artificial Intelligence (AI), Animation, Behavior, Cloth, Destruction, Physics e
Script, sendo o Havok Animation e o Physics os mais utilizados pelos jogos. Cada módulo foi
projetado para atender a uma necessidade específica de um jogo 3D, abstraindo cálculos
complexos necessários para seu desenvolvimento. A Figura 9 ilustra uma das ferramentas da
empresa, o Havok Physics.
38
Fonte: Havok Inc (2011c).
Figura 9 - Interface do Havok Physics
Embora esta ferramenta seja paga, ela também é distribuída gratuitamente com os
módulos Physics e Animation em parceria com a Intel, para desenvolvedores Windows sem
fins comerciais.
39
3 DESENVOLVIMENTO
Neste capítulo são abordadas as etapas de desenvolvimento do trabalho. São ilustrados
os requisitos, a especificação, implementação e por fim são listados resultados e discussão.
3.1 REQUISITOS PRINCIPAIS DO PROBLEMA A SER TRABALHADO
O motor para o desenvolvimento de jogos para Android deve:
a) permitir alterar regras de jogo durante a implementação de cada jogo (Requisito
Funcional - RF);
b) permitir alterar a velocidade dos movimentos das cartas na cena (RF);
c) permitir configurar a pontuação do jogo (RF);
d) permitir definir o conjunto de cartas a ser utilizado no protótipo de jogo (RF);
e) permitir definir a ordem das cartas do protótipo (RF);
f) permitir definir o modo de distribuição inicial e durante a partida (RF);
g) permitir a movimentação e manipulação das cartas pelo jogador (RF);
h) utilizar a linguagem de programação Java para Android (Requisito Não-Funcional
– RNF);
i) utilizar o ambiente Eclipse Integrated Development Environment (IDE) como
ambiente de desenvolvimento (RNF);
j) utilizar um dispositivo Android e o Android SDK como ambiente de testes (RNF).
O protótipo de jogo de pôquer deve:
a) permitir ao jogador iniciar ou continuar uma partida (RF);
b) permitir ao jogador visualizar sua pontuação e de seus adversários (RF);
c) permitir reorganizar as cartas de sua mão (RF);
d) permitir ao jogador modificar o verso das cartas (RNF).
40
3.2 ESPECIFICAÇÃO
A especificação deste trabalho foi elaborada utilizando a ferramenta Enterprise
Architect, utilizando conceitos de orientação a objetos baseando nos diagramas da Unified
Modeling Language (UML), produzindo os diagramas utilizados nas próximas seções.
3.2.1 Casos de uso
Nesta seção são descritos os casos de uso deste projeto. Um ator foi identificado: o
Usuário, que irá interagir com o jogo resultante do projeto. A Figura 10 ilustra o diagrama de
casos de uso deste ator.
uc Use Case Model
Usuário
UC01 - iniciar a
partida
UC02 - Alterar as
configurações do
jogo
UC04 -
Distribuir as
cartas
UC03 - Selecionar
opções do jogo
UC05 - Mov er
cartas da mão
UC06 - Modificar o
v alor da aposta
Figura 10 – Diagrama de casos de uso
O detalhamento de cada caso de uso do diagrama encontra-se nos anexos A até F.
3.2.2 Diagramas de classe
Nesta seção são apresentadas as classes que compõem a estrutura do motor e as classes
do protótipo, seus relacionamentos e propriedades. Para facilitar o entendimento da estrutura,
41
foi adotado o modelo de padrões de projetos chamado Model-View-Controller (MVC) para
dividir cada grupo de classes baseado em suas funções. A Figura 11 representa a estrutura do
motor de jogos o pacote GameMode está contido no pacote Control, pois as classes derivadas
da classe Game determinam as rotinas de gerenciamento do jogo.
class Simple Model
engine.Model
engine.Control
engine.GameMode
engine.View
DeckFPS
PlayerCardConstants
Poker
GameDisplayGameView Home
OptionGameRules
ScoreBoard
Game
Figura 11 – Diagrama de classes do motor de jogos
3.2.2.1 Pacote engine.Model
O pacote engine.Model constitui as classes de base do projeto ilustradas na Figura 12.
Neste pacote estão incluídas as classes Card, Player e Constants que são utilizadas na
maioria das classes dos outros pacotes do projeto.
42
class Class Model
engine.Model
Player
+ hand: ArrayList<Card>
+ name: String
+ points: int
+ tierBreaker: ArrayList<Card>
+ coins: int
+ betStatus: int
+ Player(String) : void
+ sortHand() : void
Card
+ angle: float
+ toX: float
+ id: int
+ suit: int
+ value: int
+ visible: boolean
+ toY: float
+ x: float
+ y: float
- toAngle: float
+ Card(int, int) : void
+ getImage() : String
+ getSuit() : String
+ getValue() : String
Constants
+ BGNONE: int = R.drawable.card_none {readOnly}
+ BOARDMARGIN: int = 20 {readOnly}
+ CARDMOVE: int = 5 {readOnly}
+ DEFAULT_SEQUENCE: int = 0 {readOnly}
+ DRAWTIME: int = 900 {readOnly}
+ SPANISH_DECK: int = 1 {readOnly}
+ CARD_HEIGHT: int = 113 {readOnly}
+ CARD_WIDTH: int = 78 {readOnly}
+ PREF_FILE: String = PreferencesFile {readOnly}
- TOP_LEFT: int = 0 {readOnly}
- TOP: int = 1 {readOnly}
- TOP_RIGHT: int = 2 {readOnly}
- LEFT: int = 3 {readOnly}
- CENTER: int = 4 {readOnly}
- RIGHT: int = 5 {readOnly}
- BOTTOM_LEFT: int = 6 {readOnly}
- BOTTOM: int = 7 {readOnly}
- BOTTOM_RIGHT: int = 8 {readOnly}
Figura 12 – Pacote engine.Model
A classe Card contém os atributos básicos de uma carta, seu valor e naipe, posição x e
y, margem e ângulo. O construtor da classe recebe como parâmetro o valor da carta e naipe,
que depois quando chamadas as funções getSuit e getValue retornam a versão literal destes
valores para se obter o nome do arquivo de imagem correto a ser retornado pela função
getImage. A classe possui também o atributo visible que define se será exibido a frente ou
o verso da carta e as variáveis de movimentação toX, toY e toAngle. Card é utilizada na
classe Player para compor as cartas da mão do jogador e armazenar as cartas para desempate
(tierBreaker). Na classe Deck, ela é utilizada para a composição das cartas do baralho que
são distribuídas para os jogadores e disponibilizadas na mesa caso necessário. As classes Game
e GameView também utilizam esta classe e serão abortados mais a diante.
A classe Player contém em seus atributos o nome do jogador, seus pontos da rodada,
dinheiro da partida e status da aposta na rodada, além de uma lista de cartas que possui na
mão e uma lista adicional para auxiliar no critério de desempate. Esta classe é utilizada na
classe Game e GameView para o gerenciamento do número de jogadores dentro do jogo, sua
manipulação e gerenciamento de suas cartas.
A classe estática Constants armazena valores e propriedades relacionadas ao desenho
da tela e utilizadas na maioria das classes do projeto, sendo mais utilizadas as variáveis de
posicionamento dos elementos, como TOP_LEFT, TOP_RIGHT e BOTTOM, definição do tamanho
da imagem das cartas e o espaço das margens da tela.
43
3.2.2.2 Pacote View
O pacote engine.View (Figura 13 e Figura 14) é responsável pelo gerenciamento de
layouts do sistema sendo cada classe, excluindo a classe GameView, responsável por uma tela
do programa.
class Class Model
GameDisplay
- gameView: GameView
- settings: SharedPreferences
- game: Game
- rulesIntent: Intent
- scoreIntent: Intent
- requestCode: int
+ raise: Button
+ fold: Button
+ call: Button
+ back: Button
+ coin5: Button
+ coin10: Button
+ coin25: Button
+ coin50: Button
+ coin100: Button
+ onCreate(Bundle) : void
+ showRules() : void
+ exitGame() : void
+ onCreateOptionsMenu(Menu) : void
+ onOptionsItemSelected(MenuItem) : void
+ onPause() : void
+ showScore() : void
+ onBackPressed() : void
+ onActivityResult(int, int, Intent) : void
GameView
- mPaint: Paint
- card: Bitmap
- fps: FPS
- game: Game
- time: long
+ roundBet: int
+ drawMode: int
+ card_bg: int
- GAME_MODE: int = 0 {readOnly}
- BET_MODE: int = 1 {readOnly}
- status: String
- currentBet: String
- betView: RelativeLayout
- betText: TextView
+ setGame(Game) : void
- initDrawView() : void
+ saveState() : Bundle
+ restoreState(Bundle) : void
+ onDraw(Canvas) : void
+ onTouch(View, MotionEvent) : void
+ update() : void
+ onSizeChanged(int, int, int, int) : void
Figura 13 – Pacote engine.View
class Class Model
Home
- gameIntent: Intent
- optionsIntent: Intent
- settings: SharedPreferences
- gameName: String
+ onCreate(Bundle) : void
+ startGame(String, boolean, int) : void
Option
- settings: SharedPreferences
- spinner: Spinner
- playerName: EditText
+ onCreate(Bundle) : void
+ onSaveInstanceState(Bundle) : void
ScoreBoard
+ onBackPressed() : void
+ onCreate(Bundle) : void
GameRules
+ onCreate(Bundle) : void
+ onBackPressed() : void
Figura 14 - Pacote engine.View (cont.)
44
A classe Home é responsável por exibir os menus iniciais do aplicativo como pode ser
visto nas figuras 15, 16 e 17. Home, assim como as outras classes que estendem da classe
Activity no projeto, possui uma View e utiliza um XML para definir seu layout, editado
parcialmente no Quadro 12 a partir do arquivo main.xml para corresponder à figura 15, que
descreve a estrutura de componentes e propriedades da tela.
Figura 15 – Tela principal Figura 16 – Tela de
seleção de jogo
Figura 17 – Tela de
seleção de jogadores
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/bghome">
<Button android:id="@+id/ButtonStart"
android:background="@drawable/btnewgame"
android:layout_centerHorizontal="true"
android:layout_marginTop="50px"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
(...)
<Button android:id="@+id/ButtonQuit"
android:background="@drawable/btquit"
android:layout_below="@id/ButtonOptions"
android:layout_centerHorizontal="true"
android:layout_marginTop="50px"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</RelativeLayout>
Quadro 12 – XML parcial da tela de entrada
A partir da tela inicial, o jogador tem a opção de acessar as configurações do
aplicativo pelo botão de opções do menu, criando uma instância da classe Option que abre
uma nova View utilizando o layout definido em options.xml. Por outro lado, o jogador pode
45
optar por iniciar uma nova partida selecionando o jogo desejado e o número de adversários ou
continuando uma partida já existente, criando assim uma nova instância de GameDisplay.
A classe GameDisplay utiliza o arquivo game.xml para definir seu layout e,
diferentemente das Views citadas anteriormente, possui uma View personalizada chamada
GameView. GameView é a principal responsável por desenhar e interagir com o usuário durante
o jogo através dos métodos onDraw e onTouch estendidos das classes View e
onTouchListener respectivamente.
As classes GameRules e ScoreBoard estão disponíveis durante a execução do jogo.
Ao abrir o menu (Figura 18) e o jogador selecionar o botão de regras ou de pontuação. Os
arquivos de layout relacionados a esses dois arquivos são o gamerules.xml e
scoreboard.xml respectivamente.
Figura 18 – Menu de exibição
3.2.2.3 Pacote engine.Control
O pacote engine.Control está dividido em dois grupos, o primeiro composto pelas
classes FPS e Deck (Figura 19), e a segunda composta pelas classes contidas no pacote
engine.GameMode (Figura 20).
Figura 19 – engine.Control
46
A classe FPS é responsável pela visualização da taxa de quadros por segundo que a
aplicação está executando. Ela é criada e manipulada pela classe engine.View.GameView.
A classe Deck é a responsável por gerenciar todas as cartas que estão no baralho,
mesa, pilha de descartes ou em movimento no jogo, além de ser responsável por produzir o
conjunto de cartas do baralho e efetuar o embaralhamento das cartas.
3.2.2.4 Pacote engine.GameMode
Este pacote faz parte do pacote engine.Control no diagrama de classes por
compartilharem a função de controle e gerenciamento da aplicação, porém dentro do projeto
estão separados para fins de organização.
Neste pacote (Figura 20) estão as classes Game, e Poker que herda as propriedades de
Game. A classe Game é a principal gerenciadora da manipulação das cartas em função das
propriedades do jogo além de efetuar um tratamento específico dos eventos de toque da
aplicação, é a partir desta classe que o desenvolvedor deverá se focar em modificar, pois nela
que as variáveis de jogo são definidas e manipuladas.
A classe Poker herda as propriedades de Game, permitindo personalizar as
propriedades do jogo. Alguns métodos de Game necessitam ser sobrepostos, como o
setValues() que a partir dos valores definidos para o jogo no arquivo attrs.xml, define as
configurações de uso de curingas, quantidade de cartas na mão do jogador ou o uso da pilha
de descartes por exemplo.
47
Figura 20 – Pacote engine.GameMode
48
3.2.3 Diagrama de Sequência
O diagrama de sequência da Figura 21 ilustra a interação do Usuário com o protótipo
de pôquer na rotina de jogo descrito no caso de uso UC01.
sd Sequence Model
Usuário
Home GameDisplay GameView Game
onCreate()
startGame(Game,boolean,int)
findViewById(int)
setGame(Game)
onDraw()
onTouch()
betView.setVisibil ity()
initRound()
playerBet(int)
addBet(Player) :boolean
roundWinner()
tierBreaker()
endRound()
Figura 21 – Diagrama de sequência do caso de uso UC01
3.3 IMPLEMENTAÇÃO
A seguir são mostradas as técnicas, as principais rotinas e ferramentas utilizadas na
implementação do motor e no desenvolvimento do protótipo.
49
3.3.1 Técnicas e ferramentas utilizadas
O desenvolvimento do projeto foi efetuado na linguagem Java utilizando a versão 2.3
da API do Android conhecida como Gingerbread. O ambiente utilizado para o
desenvolvimento da aplicação foi o IDE Eclipse versão Helios junto com o Android SDK e o
Android Development Tools (ADT) for Eclipse. Para efetuar os testes e depuração da
aplicação foi utilizado um simulador disponível no Android SDK em um computador
utilizando as configurações de versão 2.3.3 e também um dispositivo real da marca Samsung
modelo GT-I9100 conhecido como Galaxy S2, que também utiliza a versão 2.3.3 do Android.
O simulador foi testado em dois dispositivos, o primeiro, um desktop com processador
Intel Core 2 Quad Q8300 de 2.5 Giga Hertz (GHz) cada processador, Graphics Processing
Unit (GPU) de 1 Giga Byte (GB), memória Random Access Memory (RAM) de 4GB e
resolução de 1360x768 pixels. Enquanto o segundo foi um notebook Intel i5 M480 com 2
processadores de 2.66GHz, GPU de 1.1GB, 4GB de memória RAM e resolução de 1366x768
pixels.
O dispositivo Samsung GT-I9100 possui um processador modelo ARM Cortex-A9
Dual Core 1.2GHz, GPU Mali-400MP e 1GB de memória RAM e resolução de 480x800
pixels.
3.3.2 Inicialização da partida
As configurações do jogo se iniciam nos menus de seleção do tipo de jogo (Figura 16)
e quantidade de adversários (Figura 17) definidos na função onCreate() da atividade Home e
iniciando com a função startGame(). Esta última função (Quadro 13) recebe como
parâmetros o nome do jogo, se o jogo será continuado ou iniciado novamente e a quantidade
de jogadores que serão inseridos no jogo.
Ao iniciar uma nova partida, as informações do jogo anterior são excluídas da classe
SharedPreferences do aplicativo e grava-se o nome do jogo atual que será inicializado e a
quantidade de jogadores da partida durante a função commit().
50
Antes de iniciar uma nova instância de jogo, verifica-se se uma instância anterior já
não foi criada, pois caso exista um Intent existente na memória, o aplicativo reaproveita o
espaço e cria uma nova instância por cima da antiga.
public void startGame(String gameName, boolean cont, int players){
if (!cont){
SharedPreferences.Editor editor = settings.edit();
editor.putInt("playerCount",players);
editor.putString("gameName", gameName);
editor.remove("continue");
editor.remove("gameScore");
editor.remove("playersHand");
editor.remove("roundBet");
editor.remove("currentPlayer");
// Commit the edits!
editor.commit();
}
if (gameIntent == null)
gameIntent = new Intent(getWindow().getContext(), GameDisplay.class);
startActivity(gameIntent);
}
Quadro 13 – Inicialização do GameDisplay
Durante a inicialização do GameDisplay, as informações do jogo são recuperados
utilizando a função getSharedPreferences(). A variável settings (Quadro 14) utilizada
na aplicação permite obter os dados armazenados na aplicação utilizando getters dos tipos dos
dados. Cada getter recebe dois valores como parâmetro, o nome da variável a ser recuperada e
o valor padrão caso a variável não exista.
Ainda no Quadro 14, após o nome do jogo ser obtido, através de reflexão cria-se uma
nova instância do jogo referenciado que será inserido dentro do contexto do GameView.
51
@Override
public void onCreate(Bundle state) {
super.onCreate(state);
settings = getSharedPreferences(Constants.PREF_FILE, 0);
// Recovers the values defined by the user, or get default values
int color = settings.getInt("CardBg", 0);
String playerName = settings.getString("PlayerName","Player 1");
String gameName = settings.getString("gameName", "");
boolean isContinue = settings.getBoolean("continue", false);
int count = settings.getInt("playerCount",1);
(...)
if (gameName != ""){
try {
Class<?> c = Class.forName("engine.GameMode."+gameName);
game = (Game) c.newInstance();
} catch (Exception e){
e.printStackTrace();
}
}
setContentView(R.layout.game);
gameView = (GameView) findViewById(R.id.gameView);
if (count > 1) game.players.add(new Player("CPU"));
if (count > 2) game.players.add(new Player("CPU2"));
if (count > 3) game.players.add(new Player("CPU3"));
gameView.game = game;
gameView.setGame();
}
Quadro 14 – Inicialização de variáveis do jogo
Após a inicialização do GameView através da função findViewById(), esta nova View
recebe através da função setGame() (Quadro 15) o jogo criado pela atividade e a partir desta
função o jogo é inicializado. Nesta função primeiramente o jogo consulta as configurações
definidas no arquivo attrs.xml e inicializando a pontuação do jogador e criando um novo
baralho como pode ser visto no Quadro 16.
public void setGame(){
game.setValues(getResources());
// On creating view, the size is not set yet
Display display = ((WindowManager)
getContext().getSystemService(Context.WINDOW_SERVICE))
.getDefaultDisplay();
if (display.getWidth() > display.getHeight()){
game.HEIGHT = display.getHeight();
game.WIDTH = display.getWidth();
} else {
game.HEIGHT = display.getWidth();
game.WIDTH = display.getHeight();
}
if (game.autoDraw){
time = SystemClock.uptimeMillis();
}
game.initRound();
}
Quadro 15 – setGame()
52
Após as configurações do jogo serem definidas, é necessário obter as dimensões da
tela do dispositivo para permitir que os elementos do jogo sejam posicionados corretamente
na tela. Neste momento ainda é necessário criar um objeto da classe Display para obter as
dimensões de tela, pois a View somente terá um tamanho após a chamada da função
onDraw(). Após esta chamada, as dimensões da tela podem ser obtidas através dos métodos
getHeight() e getWidth(), devido na especificação do layout, esta View está definida com
as propriedades layout_width=”match_parent” e layout_height=”match_parent” que
determinam que a View ocupe todo o espaço da tela.
Caso o jogo esteja configurado com o valor autoDraw igual a true, a variável time é
inicializado para começar a contagem de tempo entre a distribuição de cartas, este tempo
definido na constante DRAWTIME inserido na classe Constants.
Por fim, a View chama a função initRound(), responsável por efetuar a inicialização
das variáveis utilizadas em cada rodada do jogo, verificando a disponibilidade dos jogadores e
iniciar a distribuição de cartas caso a variável autoDraw estiver ativa.
@Override
public void setValues(Resources res){
// Obtain values from res/values/attrs.xml
super.deckType = res.getInteger(R.PokerVars.deck_type);
super.playerMoney= res.getInteger(R.PokerVars.player_money);
super.joker = res.getBoolean(R.PokerVars.joker);
super.bet = res.getBoolean(R.PokerVars.bet);
super.autoDraw = res.getBoolean(R.PokerVars.auto_draw);
super.upcard = res.getBoolean(R.PokerVars.upcard);
super.minPlayers = res.getInteger(R.PokerVars.min_players);
super.maxPlayers = res.getInteger(R.PokerVars.max_players);
super.handLimit = res.getInteger(R.PokerVars.hand_limit);
super.restart_on_end = res.getBoolean(R.PokerVars.restart_on_end);
for (Player p : players){
p.coins = super.playerMoney;
}
deck = new Deck(joker,deckType);
for(Card card : deck.deck){
card.id=res.getIdentifier(card.getImage(),"drawable","engine.View");
}
deck.sort();
}
Quadro 16 – Definindo as propriedades do jogo
A função setGame() descrita na classe Game, deve ser sobrescrita nas classes
específicas que herdam desta classe como efetuado na classe Poker exibida no Quadro 16.
Nesta função, atribui-se para suas variáveis os valores do tipo PokerVars, descrito na seção
anterior, inicializa-se o baralho, define-se o id da imagem que cada carta possui e por fim
embaralha-se o baralho.
53
Ao distribuir as cartas para os jogadores, a rotina descrita no Quadro 17 se encarrega
de contar a quantidade de cartas que devem ser distribuídas para cada jogador e multiplicadas
pela quantidade de jogadores presentes. Com este valor, cada carta é retirada do baralho é
inserida em uma fila. Utilizando-se sua posição na fila, determina-se o destino para qual esta
carta deverá se movimentar e qual o ângulo final que a carta deverá rotacionar. Após
concluída a geração da fila, a primeira carta é retirada da fila e inserida no vetor de cartas em
movimento.
int limit = handLimit * players.size();
for (i = 0; i < limit; i++){
Card card = deck.push();
if (card == null)break;
setCardPosition(card);
toMove.add(card);
players.get(i%players.size()).hand.add(card);
}
deck.moving.add(toMove.remove(0));
Quadro 17 – Rotina de distribuição das cartas
3.3.3 Algoritmos gráficos
Devido à disponibilidade de se utilizar imagens pré-desenvolvidas para serem
desenhadas na aplicação, decidiu-se por utilizar os métodos de desenho na tela a partir de
Bitmaps.
Esta seção trata das classes Game e GameView que são as responsáveis pelo processo de
animação das cartas durante o jogo, sendo a primeira responsável pela alteração na transição e
rotação das cartas e a segunda efetua o desenho do Bitmap na tela a partir dos parâmetros
configurados no jogo.
Quando uma nova instância da classe GameView é chamada na atividade GameDisplay, a
partir das variáveis disponíveis no momento, a classe chama a função onDraw() (Quadro 18) para
desenhar os elementos na tela. Porém por padrão as Views somente redesenham a tela caso seja
necessário (ao recuperar o foco na aplicação por exemplo). Então para garantir que a tela se mantenha
sendo redesenhada constantemente, foi inserido a função update() no final do onDraw() que por
sua vez ao finalizar sua execução invoca a função invalidate() que informa ao sistema que a tela
precisa ser redesenhada, criando assim um ciclo de desenho e atualização constante enquanto a
atividade estiver em foco.
54
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (drawMode == GAME_MODE){
game.update();
}
(...)
update();
}
public void update() {
if (game.toMove.size() > 0){
long now = SystemClock.uptimeMillis();
if (now - time > Constants.DRAWTIME){
time = SystemClock.uptimeMillis();
game.deck.moving.add(game.toMove.remove(0));
}
}
fps.update();
invalidate();
}
Quadro 18 – Ciclo de desenho
O valor drawMode por padrão inicia com o valor GAME_MODE que significa que o jogo
está em execução e as cartas precisam ser atualizadas caso necessário. Enquanto estiver em
execução, é chamada a função update() do jogo que é responsável por alterar a posição x e
y e ângulo das cartas em movimento. No Quadro 19, parte da rotina update() é exibida onde
inicialmente, as variáveis moveX e moveY calculam a distância entre o ponto de origem das
cartas, no caso o baralho, até o seu destino, valores toX e toY. Com base na maior distância,
os movimentos de rotação e translação serão escalados para proporcionar uma animação mais
suave, tendo como base o valor de movimento em pixels definido na constante
Constants.CARDMOVE. Ao movimentar a carta, caso o próximo movimento x,y ou angular for
menor que a distância até o destino, a posição da variável será substituída pelo valor de
destino, evitando assim que os valores passem do objetivo.
55
for (Card current : deck.moving){
moveY = Math.abs(current.toY-startPos.top);
moveX = Math.abs(current.toX-startPos.left);
if (moveX < moveY){
move = (float) Math.floor(moveY/Constants.CARDMOVE);
} else {
move = (float) Math.floor(moveX/Constants.CARDMOVE);
}
if (current.toAngle != current.angle){
if (current.angle < current.toAngle){
current.angle += current.toAngle/move;
} else {
current.angle -= Math.abs(current.toAngle)/move;
}
if (Math.abs(current.toAngle-current.angle) <= Constants.CARDMOVE){
current.angle = current.toAngle;
}
}
if (moveX > moveY){
move = Constants.CARDMOVE/(moveX/moveY);
} else {
move = Constants.CARDMOVE;
}
if (current.y != current.toY){
if (current.toY < 0 && current.y - move > current.toY)
current.y -= move;
else if (current.toY > 0 && current.y + move< current.toY)
current.y += move;
if (Math.abs(current.toY-current.y) <= Constants.CARDMOVE)
current.y = current.toY;
}
(...)
Quadro 19 – Atualização da posição das cartas
Após cada carta em movimento ser reposicionada, é feita uma verificação (Quadro 20)
se a posição atual da carta é a posição de destino. Caso afirmativo, a carta é exibida se ela
veio do baralho, ou é escondida caso ela venha da mão do jogador. Ao chegar à posição final,
a carta é inserida num vetor de cartas a serem removidas da lista de movimentações que ao
final do ciclo de verificação de cartas a serem movimentadas, excluirá da lista as que já
chegaram a seu destino.
if ((current.y == current.toY) && (current.x == current.toX)){
current.visible = (!current.visible);
removed.add(current);
}
(...)
if (removed.size() > 0)
deck.moving.removeAll(removed);
Quadro 20 – Verificação de posição
Com as cartas devidamente posicionadas, cabe à View desenhá-las na tela na função
onDraw(). O trecho descrito no Quadro 21 descreve como cada carta em movimento é
56
desenhada na tela durante a partida. Para cada carta, primeiramente é verificado se ela já
possui um id de desenho, caso negativo, busca-se nos recursos do sistema, o id relacionado ao
nome da imagem que a carta necessita6. Após essa verificação, caso o valor de i seja igual a
zero e a carta estiver visível, cria-se um Bitmap baseado no id da carta, caso contrário, o verso
da carta, que foi previamente definido, será exibido.
// Draw the moving cards
for(Card c : game.deck.moving){
if (c != game.selected && c != null){
if (c.id == 0){
c.id = getResources().getIdentifier(c.getImage(), "drawable",
"engine.View");
}
if (c.visible && i == 0){
card = BitmapFactory.decodeResource(getResources(),c.id);
} else {
card = bg;
}
if (c.angle != 0){
Matrix matrix = new Matrix();
matrix.preTranslate(c.x, c.y);
matrix.postRotate(c.angle, c.x + (Constants.CARD_WIDTH/2), c.y +
(Constants.CARD_HEIGHT/2));
canvas.drawBitmap(card, matrix, mPaint);
} else {
canvas.drawBitmap(card, c.x, c.y, mPaint);
}
}
i++;
}
Quadro 21 – Ciclo de desenho de cartas em movimento
Para desenhar a carta, após o Bitmap desta ser definido, primeiro é verificado se esta
carta está rotacionada ou não. Caso esteja, é necessário criar uma matriz definindo sua
posição x e y com a função preTranslate(), e rotacionando-a com a função postRotate()
que recebe como parâmetros o ângulo da imagem e sua posição x e y de centro da imagem.
Caso os valores de centro da imagem não forem informados, a imagem irá rotacionar em
relação ao ponto (0,0) da tela, proporcionando um resultado indesejável para a situação. Com
a matriz configurada, a função canvas.drawBitmap(), se encarregará de desenhar a imagem
baseado nas informações da matriz.
Se a carta não necessitar ser rotacionada, a função drawBitmap() receberá nos
parâmetros, o Bitmap a ser desenhado e sua posição na tela sem necessidade de mais
tratamentos.
6 c.getImage()
busca na classe Card o nome do arquivo a ser utilizado baseando no valor e
naipe desta carta.
57
3.3.4 Algoritmos de controle
Além da inicialização e rotinas de desenho, algumas rotinas de controle são
fundamentais para o funcionamento do aplicativo sendo elas a função onBackPressed(), que
sobrescreve o comportamento do aplicativo ao pressionar o botão ―back” do dispositivo, a
função onTouch(), disparada quando o usuário interage com a tela, onPause(), disparado
quando a aplicação por algum motivo perde o foco, as rotinas de apostas e a verificação do
vencedor da rodada roundWinner(), definida na classe Poker.
3.3.4.1 onBackPressed()
O botão back, por padrão, permite ao usuário navegar entre atividades de forma rápida.
Porém, como explicado na seção 2.4, ao pressionar o botão back, a atividade atual é destruída
e a próxima atividade da pilha é carregada. Dependendo da forma que a atividade foi
construída, este comportamento não é desejado em algumas ocasiões, por exemplo, quando
em uma atividade múltiplas Views são utilizadas, ao exibir uma nova View por cima da inicial
é natural que o usuário deseje retornar à View anterior ao pressionar o botão back. Em
situações normais, isso não acontecerá e em vez disso, a atividade será destruída.
No projeto desenvolvido neste trabalho, a atividade GameDisplay possui duas camadas
de telas, sendo a primeira a tela de jogo, onde as cartas são distribuídas e desenhadas, e uma
segunda quando o botão de apostas é pressionado e uma nova camada é desenhada por cima
exibindo os botões de seleção de apostas. Para evitar que o jogo termine quando o usuário
pressiona o botão back durante a tela de apostas está sendo desenhada, a atividade sobrescreve
essa função (Quadro 22) permitindo que ao pressionar o botão, a tela de apostas seja
escondida e retorne ao jogo.
@Override
public void onBackPressed (){
if (gameView.drawMode == GameView.BET_MODE){
gameView.drawMode = GameView.GAME_MODE;
gameView.betView.setVisibility(View.GONE);
} else {
super.onBackPressed();
}
}
Quadro 22 – Função onBackPressed()
58
3.3.4.2 onTouch()
Ao implementar a classe OnTouchListener na classe GameView, é necessário
sobrescrever a função onTouch() que responde pelos eventos de toque do usuário na tela. A
função recebe como parâmetro o evento MotionEvent que permite extrair os valores x e y da
tela onde o evento foi disparado e o tipo de ação efetuado, sendo as ações utilizadas na
implementação a ACTION_DOWN, ACTION_UP e ACTION_MOVE.
Quando a ação ACTION_DOWN é disparada (Quadro 23), uma área retangular relativa ao
ícone de apostas é criada, e utilizando a função contains() da classe Rect, é verificado se as
coordenadas x e y do evento de toque estão dentro da área do ícone. Esta função retorna true
caso as coordenadas estejam dentro da área ou acima da fronteira lateral esquerda ou superior,
enquanto retorna false caso estejam fora da área ou acima das fronteiras inferior e lateral
direito.
int actionCode = event.getAction() & MotionEvent.ACTION_MASK;
if (actionCode == MotionEvent.ACTION_DOWN) {
int posX = (int) event.getX();
int posY = (int) event.getY();
if (game.bet){
Bitmap icon =
BitmapFactory.decodeResource(getResources(),R.drawable.icocoins);
Rect betIcon = game.position(game.betPos, icon.getHeight(),
icon.getWidth());
if (betIcon.contains(posX, posY)){
this.drawMode = BET_MODE;
for (int i = game.current; i > 0; i = (i+1)%game.players.size()){
if (game.addBet(game.players.get(i))){
currentBet = game.result + currentBet;
}
}
}
betText.setText(currentBet);
betView.setVisibility(View.VISIBLE);
}
} (...)
Quadro 23 – Verificação do botão de apostas
Caso o botão de apostas for pressionado e não seja a vez do jogador, as apostas dos
jogadores anteriores a ele serão efetuadas e a tela de apostas se torna visível.
Ao disparar o evento de toque, as classes de jogo também são notificadas, sendo estas
responsáveis por verificar se a coordenada onde o usuário pressionou consta alguma de suas
cartas na mão permitindo assim que a carta seja selecionada e enquanto não for solta, o
usuário poderá movê-la na tela. Ao disparar a ação ACTION_UP, caso a classe de jogo tenha
59
regiões definidas, o desenvolvedor poderá configurar para a carta ser inserida e posicionada
neste lugar, a mesa de jogo ou a pilha de descartes por exemplo.
3.3.4.3 onPause()
Quando a atividade ou aplicação é destruída, movida de posição na pilha de atividades
ou perca o foco por algum outro motivo como o dispositivo entrar em modo de repouso, antes
disso o sistema chama a função onPause() junto com uma série de outros comandos padrões
do sistema operacional. Neste momento é interessante que o aplicativo salve as informações
do jogo de forma que quando a atividade seja restaurada, seja possível continuar o jogo do
ponto onde ele foi interrompido.
O Quadro 24 demonstra como a aplicação armazena as variáveis do jogo na classe
SharedPreferences que serão carregadas novamente na inicialização da atividade. O editor
somente permite armazenar um conjunto limitado de tipos de dados (boolean, integer, String,
float e long), sendo assim necessário armazenar os dados de forma que seja possível recuperar
e tratar os dados posteriormente.
60
@Override
public void onPause(){
// Save game status
SharedPreferences.Editor editor = settings.edit();
String score = "";
String hands = "";
String names = "";
String handCards = "";
for (Player p : game.players){
handCards = "";
score += (score != "") ? ",":"";
score += p.coins;
for (Card c : p.hand){
handCards += (handCards != "") ? ",":"";
handCards += c.value + "-" + c.suit;
}
hands += (hands != "") ? "@":"";
hands += handCards;
names += (names != "") ? ",":"";
names += p.name;
}
editor.putBoolean("continue",true);
editor.putString("gameScore", score);
editor.putString("playersName",names);
editor.putString("playersHand",hands);
editor.putString("gameName", game.getClass().getSimpleName());
editor.putInt("roundBet", game.currentBet);
editor.putInt("currentPlayer", game.current);
editor.commit();
super.onPause();
}
Quadro 24 – onPause()
3.3.4.4 roundWinner()
Na partida de pôquer desenvolvida no protótipo, foi elaborada uma rotina de avaliação
das cartas da mão de cada jogador permitindo assim estimar o peso que suas cartas têm na
rodada. Para se obter o resultado esperado, a função pode ser separada em várias etapas que
permitem filtrar as possíveis combinações de cartas do jogo.
Inicialmente as cartas do jogador são ordenadas pelo valor de suas cartas utilizando a
função sort() (Quadro 25) da classe Collections que segundo Android Developers (2011),
utiliza um algoritmo similar ao MergeSort obtendo assim no pior caso, um gasto proporcional
a n Log n interações.
61
(...)
ArrayList<Card> cards = (ArrayList<Card>) p.hand.clone();
// Optimized ArrayList sorter using java MergeSort
Collections.sort(cards,new Comparator<Card>(){
public int compare(Card card, Card card2) {
return (card.value > card2.value) ? 1:-1;
}
}
);
(...)
Quadro 25 – MergeSort de cartas
Após as cartas estarem ordenadas utiliza-se um laço for para verificar se as cartas da
mão do jogador estão na sequência e se possuem o mesmo naipe. Com esta verificação, o jogo
identifica se o jogador possui a sequência Royal Flush, Straight ou Flush concluindo a
verificação nestes casos.
Caso o jogador não possua nenhuma das sequências anteriores, um vetor de 14
posições é criado para armazenar quantas cartas de cada valor foram encontradas. Após
armazenar os valores no vetor, é verificado se algum dos índices do vetor possui dois ou mais
valores armazenados nele, determinando assim caso o jogador possua uma quadra, trinca ou
dupla de cartas. Se a mão do jogador não se enquadrar em nenhuma das regras anteriores,
serão consideradas as cartas de maior valor que o jogador possui.
Os valores definidos na aplicação para distinguir o peso da sequência de cartas do
jogador podem ser visualizados no Quadro 26. Caso haja empate na pontuação de dois ou
mais jogadores, a função tierBreaker() analisa o valor das cartas dos jogadores empatados
e define o jogador baseado em quem tiver as cartas com maior valor. Em caso de empate
técnico, os jogadores tiverem as cartas de mesmo valor, o jogo considera empate e divide o
valor da aposta entre os empatados.
private static final int PAIR = 200;
private static final int TWO_PAIRS = 300;
private static final int THREE_OF_A_KIND = 400;
private static final int STRAIGHT = 500;
private static final int FLUSH = 600;
private static final int FULL_HOUSE = 700;
private static final int FOUR_OF_A_KIND = 800;
private static final int STRAIGHT_FLUSH = 900;
private static final int ROYAL_FLUSH = 1000;
Quadro 26 – Lista de pontuações do jogo de pôquer
62
3.3.5 Operacionalidade da implementação
A operacionalidade da aplicação desenvolvida a partir do motor será apresentada
através de imagens na forma de funcionalidades e casos de uso. As imagens utilizadas foram
retiradas diretamente do dispositivo GT-I9100 através do aplicativo ddms.bat disponível
junto com o conjunto de ferramentas do Android SDK.
A aplicação possui uma tela inicial apresentada anteriormente na Figura 15 onde há
quatro opções disponíveis, sendo a primeira a de início de novo jogo que ao clicá-la, a tela é
alterada para a Figura 16 onde é possível selecionar um dos jogos disponíveis no aplicativo.
Ao selecionar um jogo da listagem, caso este jogo permita alterar a quantidade de jogadores,
será exibida a tela de seleção de jogadores como pode ser visto na Figura 17.
Na tela de entrada ainda há a opção de continuar um jogo já existente caso este não
tenha sido finalizado com sucesso (em caso de vitória ou derrota do jogador), a opção de
modificar as configurações do jogo como pode ser visto na Figura 22 onde o jogador poderá
alterar seu nome de jogo e a cor do verso das cartas (Figura 23). Por fim o último botão na
tela de entrada finaliza o aplicativo.
Figura 22 – Tela de opções Figura 23 – Listagem de cores
Ao iniciar uma nova partida ou continuar uma partida existente, uma tela parecida com
a da Figura 24 será apresentada (no caso do jogo de pôquer). Nesta tela o jogador poderá abrir
o menu do jogo utilizando o mesmo botão de menu do dispositivo (Figura 18), ou clicar nas
fichas no canto inferior direito da tela onde será exibida a tela de apostas do jogo (Figura 25).
63
Figura 24 – Tela de jogo
Figura 25 – Opções de apostas
Na Figura 25 a opção ―Raise‖ aumenta o valor da aposta, levando para a tela ao lado
onde é possível selecionar os valores disponíveis para o aumento da aposta. Caso o jogador
não possua dinheiro suficiente para aumentar a aposta, este botão estará indisponível. A opção
―Call‖ cobre o valor do ultimo aumento da aposta e somente está disponível caso algum
jogador antes tenha aumentado a aposta. Já a opção ―Check‖ mantém o valor da aposta como
está desde que o valor da aposta não tenha sido modificado na rodada atual. A opção ―Fold‖
efetua a desistência do jogador da rodada, não permitindo assim que este participe da
avaliação do vencedor da rodada. O botão ―Back‖ tem dois comportamentos diferentes
dependendo de quais botões estão visíveis, caso os botões de valores da aposta estiverem
visíveis, retorna aos botões anteriores, caso contrário a tela de apostas será fechada.
Ao aumentar aposta serão verificadas as ações dos outros jogadores e caso não haja
mais aumentos, a rodada será finalizada, e o jogador com o melhor conjunto de cartas
receberá o valor das apostas na mesa.
A partir do menu (Figura 18), o jogador poderá selecionar uma das três opções
disponíveis, a primeira de regulamento, irá abrir a tela mostrada na Figura 26. A segunda
opção de pontuação irá exibir a tela da Figura 27, enquanto a terceira opção de sair do jogo
finalizará o jogo e retornará para a tela de seleção de jogadores (Figura 17).
64
Figura 26 – Regulamentos do jogo
Figura 27 – Pontuação do jogo
3.4 RESULTADOS E DISCUSSÃO
Este trabalho trouxe como principal desafio, o entendimento das diferenças de
abordagem entre a linguagem Java tradicional para o desenvolvido na plataforma Android.
Devido à limitação de hardware e a preocupação com o desempenho da aplicação, por várias
vezes foram encontradas dificuldades em manter o funcionamento da aplicação a uma taxa de
atualização de quadros viável, sendo os conselhos sugeridos por Android Developers (2011),
muito úteis durante o desenvolvimento da aplicação.
Uma das dificuldades encontradas na elaboração do trabalho foi em relação à
explicação formal de parte dos desenvolvedores da plataforma em relação a alguns conceitos,
sendo eles mais focados em passar uma visão geral dos recursos e aprofundando na
documentação da linguagem. Ocorreram algumas dificuldades ao utilizar alguns métodos e
atributos devido à utilização de rotinas desaconselhadas pelos desenvolvedores, tais como o
problema de falta de memória utilizando o laço for utilizando uma variável de tamanho
sendo solucionado trocando estes for pelo laço for-each em todas situações disponíveis. Um
detalhe importante que durante a implementação não foi lembrado foi que ao utilizar a rotação
das cartas na tela, por padrão as matrizes são rotacionadas utilizando o ponto zero da View,
efetuando um efeito diferente do esperado quando inserido junto a um efeito de translação da
matriz. A especificação não é clara neste aspecto, mas é possível resolver este problema
passando parâmetros adicionais de x e y para definir o centro de rotação da matriz. Porém em
65
contra partida, quando a documentação não parece clara o suficiente, é possível encontrar
através da internet a solução muitas vezes fornecida para outros desenvolvedores com a
mesma dificuldade, sendo assim possível resolver problemas com certa facilidade.
Mesmo que o simulador seja uma boa saída para testar o aplicativo, sua taxa de
desempenho gráfico é muito inferior a de um dispositivo real. A Tabela 1 ilustra a diferença
da taxa de quadros entre o dispositivo e os simuladores utilizados.
Tabela 1 – Média de quadros por segundo (fps)
2 Jogadores 3 Jogadores 4 Jogadores
GT-I9100 35 33 33
Desktop 6.5 6 5.5
Notebook 5 3.5 3.5
A utilização de arquivos XML permite ao desenvolvedor uma maior transparência no
desenvolvimento de um projeto, permitindo assim que seja possível alterar as configurações
do jogo sem a necessidade de se preocupar tanto com a programação em si.
Mesmo com o grande avanço na tecnologia para os dispositivos móveis, a mesma
preocupação devido à limitação de recursos do aparelho encontrada por Pamplona (2005)
ainda existe. Não há dúvidas que os avanços dos últimos anos permitiram ao desenvolvedor
uma maior liberdade para utilizar mais recursos computacionais devido às melhorias dos
dispositivos permitindo assim um melhor resultado para o usuário final.
Assim como o trabalho de Takano (2009), este trabalho se focou no resultado gráfico
da aplicação, não sendo utilizadas rotinas complexas para tratamentos de física dos objetos,
porém, assim como o trabalho dele, houve uma preocupação em relação à interação do
usuário com a interface do aplicativo através de toques.
Apesar de utilizar a linguagem Java assim como Pamplona (2005), há algumas
diferenças em relação ao Java para o Android, sendo este último otimizado para essa
plataforma de forma que haja um melhor resultado de desempenho dado os recursos
disponíveis em cada aparelho. Porém, a preocupação com o consumo de recursos, como a
memória e processamento, ainda existe e afeta diretamente na durabilidade da bateria do
dispositivo.
O protótipo ocupa aproximadamente 1.13 MB de memória sendo dessas sua maior
parte utilizada para armazenar arquivos de imagem e de mídia. Os dados armazenados na
memória através da classe SharedPreferences ocupam cerca de 4KB.
66
4 CONCLUSÕES
Este trabalho apresentou a implementação de um motor de jogos de cartas na forma de
uma biblioteca de funções dividida em várias atividades com funções específicas. As classes
implementadas permitiram manipular os recursos gráficos da aplicação e a configuração do
jogo de forma mais simplificada utilizando recursos provenientes de arquivos XML. Desta
forma, foi possível concluir todos os objetivos propostos no trabalho.
Este trabalho disponibiliza um conjunto de funções e conceitos que permitem ao
desenvolvedor entender melhor o desenvolvimento de aplicativos na plataforma e a facilitar o
desenvolvimento de outros jogos de cartas reaproveitando a lógica das funções aqui
apresentadas.
O aplicativo desenvolvido apresentou algumas semelhanças com alguns dos trabalhos
correlatos em relação ao tratamento de controle de cena ao receber um evento de toque na
tela, no modelo de estrutura utilizado (MVC) e no conceito de auxiliar o desenvolvedor no
entendimento do ambiente e disponibilizando recursos para o desenvolvimento de um novo
aplicativo.
A plataforma Android disponibiliza através de sua API uma série de classes e funções
que permitem ao desenvolvedor interagir de muitas formas com os dispositivos onde suas
aplicações irão rodar, permitindo tanto a interação com hardware como com outros softwares
se estes permitirem o acesso para tal.
As ferramentas disponíveis pelo Android SDK facilitam muito para o desenvolvedor
efetuar seus testes utilizando um dispositivo virtual, utilizar o modo debug tanto no simulador
como no dispositivo ou salvar imagens da tela do dispositivo.
67
4.1 EXTENSÕES
O protótipo desenvolvido foi projetado de forma que somente um usuário possa jogar o
jogo por vez. Explorando as técnicas de redes e protocolos, sugere-se que o aplicativo seja
estendido para o modo multiplayer, permitindo assim que vários jogadores com seus
dispositivos em rede local ou pela internet possam participar de uma mesma partida. Com esta
implementação, sugere-se que seja implementado também, uma pontuação online e ranking,
permitindo uma maior competição entre os jogadores.
Este trabalho poderia ter explorado ainda mais a fundo a utilização de XMLs para
tornar o aplicativo mais dinâmico, pois há muitos outros recursos disponíveis na plataforma
através deles, porém este estudo fica como sugestão para trabalhos futuros.
68
REFERÊNCIAS BIBLIOGRÁFICAS
ANDROID DEVELOPERS. The developer’s guide, [S.l.], 2011. Disponível em:
<http://developer.android.com/guide/ >. Acesso em: 15 mar. 2011.
______. Dalvik JIT. [S.l.], 2010. Disponível em: <http://android-
developers.blogspot.com/2010/05/dalvik-jit.html>. Acesso em: 17 mar. 2011.
GENKISTAR. Poker gratuit, [S.l.], 2011. Disponível em: <http://www.genkistar.com/>.
Acesso em: 26 set. 2011.
GREGORY, Jason; LANDER, Jeff. Game engine architecture. Wellesley: A. K. Peters,
2009.
HAVOK INC. Available games. [S.l.], 2011a. Disponível em:
<http://www.havok.com/ndex.php?page=available-games>. Acesso em: 22 mar. 2011.
______. Havok product family. [S.l.], 2011b. Disponível em:
<http://www.havok.com/index.php?page=products>. Acesso em: 26 mar. 2011.
______. Product screenshots. [S.l.], 2011c. Disponível em:
<http://www.havok.com/índex.php?page=physics-screenshots>. Acesso em: 26 mar. 2011.
JOGATINA. A história do baralho – os dias de hoje. [S.l.], 2009. Disponível em:
<http://blog.jogatina.com/archives/a-historia-do-baralho-os-dias-de-hoje/>. Acesso em: 27
set. 2011.
JOGOS DE CARTAS. Jogos de cartas - o mundo das cartas em suas mãos. [S.l.], 2007.
Disponível em: <http://jogosdecartas.hut.com.br/>. Acesso em: 27 set. 2011.
PAMPLONA, Vitor F. Um protótipo de motor de jogos 3D para dispositivos móveis com
suporte a especificação móbile 3D Graphics API for J2ME. 2005. 83 f. Trabalho de
Conclusão de Curso (Bacharelado em Ciências da Computação) – Centro de Ciências Exatas
e Naturais, Universidade Regional de Blumenau, Blumenau.
SCHUYTEMA, Paul; MANYEN, Mark. Game development with LUA. Hingham: Charles
River Media, 2005.
TAKANO, Rafael H. MJ3I: um motor de jogos 3D para o iPhone OS. 2009. 52 f. Trabalho
de Conclusão de Curso (Bacharelado em Ciências da Computação) – Centro de Ciências
Exatas e Naturais, Universidade Regional de Blumenau, Blumenau.
69
UOL TECNOLOGIA. Venda de smartphones deve superar a de computadores no Brasil
em 2011, diz IDC. [S.l.], 2011. Disponível em: <http://tecnologia.uol.com.br/ultimas-
noticias/redacao/2011/02/17/venda-de-smartphones-deve-superar-a-de-computadores-no-
brasil-em-2011-diz-idc.jhtm>. Acesso em: 11 mar. 2011.
VELDHUIZEN, Bart. Game engine. Amsterdam, [2010a?]. Disponível em:
<http://www.blender.org/education-help/tutorials/game-engine/>. Acesso em: 22 mar. 2011.
______. Features. Amsterdam, [2010b?]. Disponível em: <http://www.blender.org/features-
gallery/features/>. Acesso em: 26 mar. 2011.
WARD, Jeff. What is a game engine? [S.l.], abr. 2008. Disponível em:
<http://gamecareerguide.com/features/529/what_is_a_game_.php>. Acesso em: 20 mar. 2011.
70
ANEXO A – UC01 - Iniciar a partida
O caso de uso ilustrado no Quadro 27 descreve como o Usuário interage com o jogo
selecionado no protótipo em seu dispositivo real.
UC01 – Iniciar a partida
Descrição Este caso de uso descreve os procedimentos que o Usuário
deve seguir para iniciar uma partida de pôquer e finalizar uma
rodada.
Pré-condição Possuir o aplicativo instalado no dispositivo.
Cenário principal 1. O Usuário seleciona a opção de novo jogo (Figura 15).
2. A aplicação lista os jogos disponíveis (Figura 16).
3. O Usuário seleciona um modo de jogo.
4. A aplicação exibe a quantidade de adversários disponíveis
(Figura 17).
5. O Usuário seleciona quantos adversários deseja enfrentar.
6. A partida é iniciada e as cartas são distribuídas para os
jogadores.
7. O Usuário seleciona o valor da aposta.
8. Os adversários selecionam manter o valor da aposta.
9. A aplicação avalia a maior pontuação da rodada.
10. O jogador que ganhar a rodada recebe o valor da aposta.
11. Nova rodada se inicia.
Cenário alternativo 1 No passo 4, caso a partida não permita adversários, pular para o
passo 6.
Cenário alternativo 2 No passo 7, caso o usuário selecione aumentar o valor da
aposta, os adversários podem manter o valor da aposta ou aumentar novamente.
2.1. Se todos os adversários selecionarem manter o valor da
aposta, pular para o passo 9.
2.2. Caso algum dos adversários selecione aumentar a aposta, a
rodada de apostas continua enquanto não chegar a quatro
aumentos nas apostas ou concluir a rodada de apostas. Voltar para o passo 7.
Cenário alternativo 3 No passo 10, caso haja empate entre dois ou mais jogadores, o
valor da aposta é dividido entre os vencedores.
Pós-condições O Usuário pode finalizar o jogo ganhando ou perdendo de seus
adversários, encerrando a partida ou saindo da aplicação e iniciando nova partida.
Quadro 27 – Caso de uso UC01
71
ANEXO B – UC02 - Alterar as configurações de jogo
Este caso de uso ilustrado no Quadro 28 descreve como o Usuário pode interagir com
as opções disponíveis na aplicação, opções estas que não interferem no funcionamento do
jogo.
UC02 – Alterar as configurações do jogo
Descrição Algumas propriedades dos jogos podem ser alteradas, como o nome
do jogador ou o verso das cartas, este caso de uso descreve os passos desta alteração.
Pré-condição Possuir o aplicativo instalado no dispositivo.
Cenário principal 1. O Usuário seleciona a opção de opções (Figura 22).
2. A aplicação lista as opções disponíveis (Figura 23).
3. O Usuário altera seu nome de exibição.
4. O Usuário finaliza as alterações e pressiona o botão de retornar.
Cenário alternativo 1 O passo 3 é opcional, por padrão o aplicativo inicia com o nome
―Player 1‖.
Cenário alternativo 2 No passo 3 o Usuário pode optar por alterar a cor do verso das
cartas que será utilizada (Figura 23) que por padrão inicia-se com a
cor preta.
Pós-condições As configurações do aplicativo serão salvas e carregadas nas
partidas posteriores.
Quadro 28 – Caso de uso UC02
72
ANEXO C – UC03 - Selecionar as opções de jogo
Neste caso de uso detalhado no Quadro 29, o Usuário interage com o menu do jogo
(Figura 24) ao pressionar o botão de menu do dispositivo durante a partida.
UC03 – Selecionar as opções do jogo
Descrição Este caso de uso descreve as opções padrões disponíveis no motor
que são acessadas quando o jogador pressiona o botão menu do dispositivo.
Pré-condição A tela de jogo estar visível e o menu do dispositivo ser pressionado.
Cenário principal 1. O jogador pressiona o botão de regras.
2. A aplicação exibe em nova tela os regulamentos do jogo (Figura
26).
3. O jogador pressiona o botão de retornar ou o botão back.
4. A aplicação retorna para a tela anterior e exibe novamente o
menu.
Cenário alternativo 1 No passo 1 o Usuário pode optar por pressionar o botão de
pontuação da partida.
1.1. A aplicação exibe nova tela contendo em ordem de pontos o
nome dos jogadores participantes e sua pontuação atual (Figura 27).
Cenário alternativo 2 No passo 6 o Usuário pode optar por pressionar o botão de sair da partida.
2.1. A partida se encerra e a aplicação retorna para a tela de seleção
de jogos.
Pós-condições Após a navegação no menu, o Usuário pode por pressionar o botão
back ou o botão menu para retornar à partida.
Quadro 29 – Caso de uso UC03
73
ANEXO D – UC04 - Distribuir as cartas
Este caso de uso é necessário para os jogos onde a distribuição das cartas não é
automática sendo esta decisão dependente do desenvolvedor, e descreve a reação do sistema
quando a imagem do baralho é pressionada durante o jogo, como pode ser visto no Quadro
30.
UC04 – Distribuir as cartas
Descrição Este caso de uso descreve as possíveis reações do aplicativo quando
o baralho é pressionado. Este caso de uso é opcional dependendo da
definição das propriedades do jogo elaborado.
Pré-condição A tela de jogo estar visível, haver cartas disponíveis no baralho e a
imagem do baralho ser pressionado.
Cenário principal Caso as cartas ainda não foram distribuídas, o aplicativo verifica os
jogadores disponíveis e distribui as cartas para todos.
Cenário alternativo 1 Caso não haja mais cartas no baralho, as cartas do ―cemitério‖ são
embaralhadas novamente e recolocadas no baralho.
Cenário alternativo 2 Caso as cartas já tenham sido distribuídas, o jogo não será alterado.
Pós-condições As cartas são distribuídas conforme especificação do jogo.
Quadro 30 – Caso de uso UC04
74
ANEXO E – UC05 - Mover as cartas da mão
Muitos jogos permitem uma maior ou menor interação do jogador com a partida, este
caso de uso detalhado no Quadro 31 descreve como o sistema interage quando o jogador
seleciona uma carta e movimenta-a na tela.
UC05 – Mover as cartas da mão
Descrição Este caso de uso descreve como as cartas do jogador podem ser
manipuladas durante a partida.
Pré-condição O jogo deve permitir cartas na mão do jogador e o jogador
selecionar uma das cartas em sua mão.
Cenário principal 1. O aplicativo verifica qual é a carta que deve ser selecionada
quando o jogador pressiona a tela.
2. Enquanto o jogador manter a tela pressionada, a carta poderá ser
movida por toda a tela. O sistema irá redesenhar a carta
acompanhando o movimento de toque do jogador.
3. Quando a tela parar de ser pressionada, o aplicativo verifica a
posição onde a carta será solta.
4. Caso a carta for posicionada em uma das áreas pré-definidas para
o jogo, a carta será retirada da mão do jogador e inserida no contexto atual.
Cenário alternativo 1 No passo 4, caso o jogador solte a carta fora de uma das áreas pré-
definidas, a carta irá retornar para a mão do jogador.
Pós-condições As cartas são reposicionadas conforme especificação do jogo.
Quadro 31 – Caso de uso UC05
75
ANEXO F – UC06 - Modificar o valor da aposta
Em vários jogos de cartas o sistema de apostas é utilizado para deixar a partida mais
interessante. Este caso de uso (Quadro 32) descreve os passos de como o Usuário interage
com as apostas no aplicativo.
UC06 – Modificar o valor da aposta
Descrição Este caso de uso estará disponível em partidas onde ocorrem apostas
entre os jogadores e descreve as opções de apostas possíveis.
Pré-condição O jogo deve conter mais de um jogador e permitir apostas entre si, e
após a distribuição das cartas, o botão de apostas ser pressionado.
Cenário principal 1. A aplicação exibe uma tela com as opções de apostas disponíveis
(Figura 25).
2. O jogador escolhe a melhor aposta para a rodada e aguarda a
decisão dos outros jogadores.
3. A janela de apostas se fecha e a partida continua.
Cenário alternativo 1 No passo 2, o jogador decide aumentar a aposta. Caso os outros
jogadores se mantiverem na rodada e se algum jogador aumentar a aposta, voltar para o passo 1.
1.1 Caso o valor da aposta for aumentado quatro vezes e a rodada de
apostas for finalizada, não será mais possível aumentar o valor da
aposta e os jogadores poderão optar por continuar na rodada ou desistir da mesma.
Cenário alternativo 2 No passo 2, o jogador opta por desistir da rodada. As cartas do
jogador serão retiradas e ele não poderá competir nesta rodada.
Pós-condições Ao finalizar a rodada do jogo, o jogador com a melhor pontuação
das cartas receberá o valor total da aposta. Em caso de empate, o valor será dividido igualmente entre os vencedores.
Quadro 32 – Caso de uso UC06