1. Serviço Nacional de Aprendizagem Comercial do Rio Grande do Sul
Faculdade Senac Porto Alegre
Curso de Tecnologia em Análise e Desenvolvimento de Sistemas
RELATÓRIO FINAL DE PROJETO
Blinded Walker
Desenvolvimento de um jogo para a plataforma Android
Tiago Rodrigues Mattos da Cunha
Prof. MSc. Luciano Zanuz (Orientador)
Porto Alegre
2012
3. RESUMO
O desenvolvimento de jogos é um assunto que atrai muito interesse, em
especial nos dias de hoje. É fácil ter acesso a recursos computacionais, kits de
desenvolvimento, documentação e aos diversos dispositivos clientes existentes no
mercado. Entretanto, muitas vezes não se tem certeza por onde começar, nem se
tem ideia de que tipo de desafios podem estar presentes. Este trabalho tem o intuito
de documentar o desenvolvimento de um jogo, trazendo consigo uma apresentação,
e uma possível solução, para as diversas dificuldades encontradas no processo.
Como resultado são apresentadas diversas soluções para dificuldades comuns no
desenvolvimento de jogos na plataforma Android.
PALAVRAS-CHAVE: Desenvolvimento de jogos. Jogos. Android.
4. SUMÁRIO
1
Apresentação Geral do Projeto
.........................................................................................
6
2
Definição do Problema
..........................................................................................................
7
3
Objetivos
.....................................................................................................................................
8
4
Análise de Tecnologias/Ferramentas
..............................................................................
9
4.1
Android SDK
....................................................................................................................................
9
4.2
Linguagem Java
.............................................................................................................................
9
4.3
IDE Eclipse
.......................................................................................................................................
9
4.4
Fireworks
..........................................................................................................................................
9
4.5
GarageBand
..................................................................................................................................
10
4.6
Audacity
..........................................................................................................................................
10
4.7
Astah*
..............................................................................................................................................
10
4.8
ObjectAid
.......................................................................................................................................
10
4.9
OpenGL
...........................................................................................................................................
10
5
Descrição da Solução
.........................................................................................................
11
6
Abordagem de Desenvolvimento
...................................................................................
12
7
Arquitetura do Sistema
......................................................................................................
14
7.1
Game Design Document
..........................................................................................................
15
7.1.1
Título
...........................................................................................................................................................
15
7.1.2
Descrição Curta
....................................................................................................................................
15
7.1.3
Tipo/Gênero do Jogo
.........................................................................................................................
15
7.1.4
Cenário
......................................................................................................................................................
15
7.1.5
Descrição Longa
..................................................................................................................................
15
7.1.6
Sistema de Jogo
...................................................................................................................................
16
7.1.7
Game Play
...............................................................................................................................................
16
7.1.8
Tela de título e telas de informações
........................................................................................
17
7.1.9
Requisitos de Áudio
............................................................................................................................
17
7.2
Modelagem Funcional
...............................................................................................................
19
7.2.1
Casos de Uso
........................................................................................................................................
19
7.2.2
Mapa de Transição
.............................................................................................................................
20
7.2.3
Backlogs
...................................................................................................................................................
21
7.2.4
Diagramas de Classes
......................................................................................................................
23
7.3
Modelagem dos Dados
.............................................................................................................
24
7.4
Modelagem de Interface Gráfica do Usuário
...................................................................
25
8
Funcionamento do Sistema
.............................................................................................
26
9
Descrição dos desafios e soluções encontrados
...................................................
27
9.1
O mundo como uma coleção de lista de vetores (ArrayList)
...................................
27
9.2
Renderizando de forma eficiente: aceleração na GPU
................................................
27
9.3
Sentindo você: um sistema para entrada de dados
.....................................................
30
9.4
Estou tocando em você, ou não?
........................................................................................
31
9.5
Definindo Níveis
..........................................................................................................................
32
9.6
Colisão: o que acontece entre nós
......................................................................................
33
9.7
O mundo é mais que imagens estáticas: um sistema de animação
.....................
33
9.7.1
Sprites, o inicio do movimento
......................................................................................................
34
9.7.2
Uma breve história no tempo: Uma explicação do OpenGL e os estados
...........
35
9.7.3
Detalhes tão pequenos de nós dois, ou mais: como funciona a animação
..........
36
9.8
Produzindo os gráficos e uma descoberta
......................................................................
37
9.8.1
Benchmarking
........................................................................................................................................
38
9.9
Eu posso ouvir você: um sistema sonoro
........................................................................
39
5. 9.9.1
Produzindo sons
...................................................................................................................................
39
9.10
Sistema de menus
....................................................................................................................
39
9.11
Montando níveis: mais desafios
........................................................................................
47
9.12
Pontuação
...................................................................................................................................
47
9.13
Persistindo dados
....................................................................................................................
48
10
Validação
...............................................................................................................................
49
10.1
Estratégia
.....................................................................................................................................
49
10.2
Consolidação dos Dados Coletados
................................................................................
51
11
Considerações
....................................................................................................................
60
12
Referências Bibliográficas
.............................................................................................
61
...........................................................................................
62
13
Componentes Re-utilizados
14
APÊNDICE A - Formulário de validação
...................................................................
63
15
APÊNDICE B – Formulários de validação preenchidos
.....................................
64
6. 6
1 Apresentação Geral do Projeto
O projeto é centrado no desenvolvimento de um jogo e os desafios
encontrados ao longo do mesmo. Este tema foi escolhido uma vez que o mercado
de entretenimento está cada vez maior, apontando novas possibilidades. Dentro
deste mercado, os jogos se colocam como uma das formas mais interativa,
possibilitando que um consumidor possa, de forma controlada e limitada, decidir o
que irá acontecer, como e quando. No ano de 2011, o mercado de jogos para
dispositivos móveis movimentou mais de 12 bilhões de dólares americanos e
empresas como a Rovio, do sucesso Angry Birds, fazem cerca de 6 milhões de
dólares americanos por mês apenas com propagandas em seus jogos (Bussiness
Degree, 2012).
Outro ponto importante que temos nos últimos anos é o crescimento
acelerado das plataformas móveis. Em especial, os smartphones têm atingido uma
enorme fatia da população, independente de cultura, sexo e idade. O crescimento de
mercado dos smartphones foi de 42,5% ano a ano, com um total de 144,9 milhões
de dispositivos colocados no mercado no primeiro trimestre deste ano, comparado
com os 101,7 milhões no mesmo período do ano passado (IDC, 2012). Dentro
desses dispositivos, existem algumas opções de Sistema Operacional (SO), cada
qual com suas ferramentas, forças e fraquezas. Destas opções, foi escolhido o
Android (Android, 2011), sistema mais aberto e de fácil acesso para
desenvolvimento. O Android está presente em diversos aparelhos no mercado e
possui um kit de desenvolvimento de software, Software Development Kit (SDK)
(Android SDK, 2011), disponível a diversos sistemas operacionais. Outra vantagem
do Android é ser baseado na linguagem Java, cuja vasta documentação é de suma
importância para o trabalho.
Sendo assim, esse trabalho se propõe a juntar essas duas vertentes:
entretenimento e dispositivos móveis. Neste caso específico, um jogo para Android.
Ambos os campos possuem possibilidades interessantes, e desafios ainda mais
interessantes. Este trabalho é exatamente sobre esses desafios.
7. 7
2 Definição do Problema
Um software de negócio costuma nascer da necessidade de resolver um
problema. Como organizar certos dados a ponto de gerar a informação necessária.
Um jogo, com algumas exceções, não se propõem a resolver um problema. Sendo
assim, a definição do problema leva em conta os problemas encontrados ao longo
do desenvolvimento.
• Como gerenciar os recursos de forma a evitar gargalos?
• Como capturar e tratar o evento de toque?
• Como gerenciar um nível carregado e repassar a seus componentes
uma requisição de atualização e como capturar os resultados dessa
requisição?
• Os diversos sons devem ser sincronizados a seus eventos. Como
manter o tempo correto?
• Como gerenciar um sistema de animação, que faça sentido com os
eventos e movimentos existentes?
• Para um jogo, manter certo nível de performance é essencial. Essa
performance se apresenta ao usuário como fluidez no jogo. Como
manter a performance necessária para não comprometer o projeto?
8. 8
3 Objetivos
O objetivo é a criação de um jogo no estilo plataforma/puzzle para a
plataforma Android que terá sua interação baseada em toque. Ao final do projeto é
esperado:
• 10 fases diferentes, com complexidade crescentes;
• Sistema de renderização que reutilize diversas texturas;
• Sistema de controle de toque do usuário, identificando o toque da tela
a um ponto do jogo;
• Sistema de animação;
• Sistema de som, com capacidade para músicas de fundo e efeitos
sonoros;
• Verificar se a forma escolhida para apresentação da dinâmica de jogo
sem tutoriais funciona.
9. 9
4 Análise de Tecnologias/Ferramentas
Para o desenvolvimento deste projeto, diversas ferramentas e tecnologias
foram utilizadas. A seguir temos uma breve apresentação de cada uma delas e
como a mesma se encaixa no projeto em questão.
4.1 Android SDK
Para o desenvolvimento foi utilizado o SDK oficial da plataforma Android
(Android SDK, 2011). O mesmo possui todas as bibliotecas básicas para o
desenvolvimento Android, incluindo um depurador e um simulador (permitindo testes
em ambientes controlados), entre outros.
Existem outros SDKs que geram código para Android, mas a escolha foi
pela SDK oficial para que fosse possível trabalhar com a plataforma nativamente.
4.2 Linguagem Java
A linguagem utilizada pela plataforma Android é Java. Portanto, ao utilizar o
SDK oficial, a escolha pela linguagem Java é um pré-requisito. Entretanto, a
máquina virtual não é a padrão, e sim uma máquina criada especialmente para a
plataforma, conhecida como Dalvik. Sendo assim, existe um namespace específico
do Android, com classes especialmente criadas para este ambiente.
Outra grande vantagem é a ampla documentação existente para Java
disponível na Internet. A linguagem Java foi criada pela Sun Microsystems, que foi
comprada pela Oracle, tendo como base o paradigma de orientação a objetos. Mais
detalhes sobre a linguagem podem ser obtidos no site oficial da linguagem em
<http://www.oracle.com/technetwork/java/index.html>.
4.3 IDE Eclipse
O ambiente de desenvolvimento, Integrated Development Environment
(IDE), utilizado é o Eclipse (Eclipse, 2011). É o IDE recomendado pela
documentação oficial do SDK da plataforma Android, além de ser um IDE bastante
completo e com variados recursos úteis ao desenvolvimento (tais como: integração
com controles de versão e suporte a refatoração).
4.4 Fireworks
A criação dos gráficos (imagens) foi feita utilizando o Fireworks (Fireworks,
2011). O Fireworks é um software proprietário da Adobe. O mesmo possui um
período de experiência onde todas as suas funcionalidades estão disponíveis. É um
editor fácil de usar e com recursos suficientes ao trabalho aqui realizado.
10. 10
4.5 GarageBand
A ferramenta que foi utilizada para a produção sonora é o GarageBand
(GarageBand, 2011). O mesmo faz parte de um pacote de softwares da Apple
conhecido como iLife. Ele está disponível na instalação padrão do Mac OS X Snow
Leopard e possui os recursos necessários para a confecção dos sons necessários.
O mesmo possui uma biblioteca com efeitos sonoros e trechos de som que
facilitam a criação de diversos conteúdos sonoros sem, necessariamente, precisar
tocar algum instrumento.
4.6 Audacity
Como software sonoro auxiliar, o Audacity (Audacity, 2012) foi utilizado, uma
vez que o mesmo permite a conversão de um formato de áudio para outro. O
Audacity é um software open source.
4.7 Astah*
Para a modelagem dos casos de uso foi utilizado o Astah* Community
(Astah, 2011). O mesmo dá suporte a diagramas do UML 2.0 e, na versão
Community, é totalmente gratuito.
4.8 ObjectAid
O ObjectAid (ObjectAid, 2011) foi utilizado para a geração dos diagramas de
classe, uma vez que permite que os mesmos sejam gerados a partir do código
existente.
4.9 OpenGL
O OpenGL é uma especificação padrão para escrever aplicações e simular
física, que produzem gráficos 2D e 3D. O padrão foi criado em 1992 pela Silicon
Graphics Inc. (SGI) com o objetivo de ser uma Interface de Programação de
Aplicativos, Application Programming Interface (API) ,independente.
O OpenGL possui diversas versões, cada qual especificando um conjunto de
funções que devem ser suportadas para que um hardware possa ser certificado para
uma versão específica.
Isso permite que tenhamos o processamento paralelo de informações em
um hardware específico para este fim. No caso deste projeto, os gráficos são
processados via OpenGL.
11. 11
5 Descrição da Solução
Dada a natureza e complexidade de um jogo, o mesmo foi dividido em
partes menores, possibilitando o seu desenvolvimento. Neste caso, há alguns
módulos básicos dentro da arquitetura proposta: renderização, detecção de colisão,
animação, som e entrada de dados. Esses módulos fazem parte de um todo
conhecido como máquina de jogo (ou game engine). Cada um dos módulos foi
desmembrado em suas funções menores e implementado dentro de um sprint. Mais
detalhes sobre o que é um sprint estão presentes na Abordagem de
Desenvolvimento.
Entretanto, dentro do abordagem de desenvolvimento adotada, é importante
a entrega de valor ao final de cada sprint. Como algumas tarefas podem ser
divididas e abortadas em mais de um sprint, isso faz com que nem sempre, no
desenvolvimento de jogos, se possa ter algo jogável ao final de um sprint. O que
ocorre, neste caso, é uma diminuição da incerteza. Essa diminuição vem com o fato
de que pequenas partes podem ser desenvolvidas e testadas, garantindo não só
que o código funciona, mas que a ideia sendo trabalhada é aceita como esperado.
Ao diminuir as incertezas de um jogo rumo ao mercado, estamos garantindo que o
mesmo terá mais chances de dar certo, o que, por sua vez, agregará valor ao
produto. Ou, de acordo com KEITH (2010, p. 18) “Um projeto ágil diminui a incerteza
em pequenas iterações que incluem todas as partes do desenvolvimento”.
Dentro deste relatório, encontra-se o Documento de Design de Jogo (Game
Design Document – GDD). No mesmo temos detalhado o que será realizado em
termos de jogo, com todos os detalhes sobre o mesmo. Entretanto, o GDD não dá
uma visão macro de como o sistema é em relação a sua implementação. Essa visão
ficará clara nos capítulos posteriores, por hora a figura 1 abaixo demonstra uma
visão geral do sistema. Maiores detalhes são encontrados no capítulo 9.
Figura 1 - Visão geral do sistema
12. 12
6 Abordagem de Desenvolvimento
A estratégia escolhida é o Scrum, na sua variante Scrum Solo. O motivo
desta escolha está na descrição da solução, a capacidade de diminuir as incertezas
ao longo do tempo. Dado o caráter exploratório do projeto, gerenciar as incertezas é
fundamental.
De acordo com Keith (2010, p. 36), o Scrum é um framework para a criação
de produtos complexos. Ao invés de desenvolver toda a documentação
anteriormente a qualquer implementação, o Scrum encoraja ciclos menores de
desenvolvimento, onde cada característica do produto é estudada, implementada,
testada e entregue, para que o cliente possa avaliar o produto.
Dentro do Scrum, em especial para desenvolvimento de jogos, existem,
segundo Keith (2010, p. 44) os seguintes papéis:
• Clientes e Stakeholders: Apesar de o cliente final de um jogo ser o
jogador (ou gamer), este não influi diretamente sobre o projeto,
entretanto os stakeholders os representam. Eles podem ser diversas
pessoas envolvidas externamente, tal como publishers. Eles
costumam definir vários itens do Product Backlog e prioriza-los;
• Product Owner: É o membro do Scrum Team responsável pela
intermediação entre os clientes e stakeholders e o Scrum Team. Ele
representa os primeiros dentro do ciclo do Scrum, ao mesmo tempo
em que facilita a comunicação entre as partes. É, também,
responsável pelo Product Backlog;
• Scrum Team: “É composto por um Scrum Master, um Product Owner
e um time de desenvolvedores.” (Keith, 2010, p. 44);
• Scrum Master: Responsável por facilitar o desenvolvimento,
garantindo que o time tenha acesso aos recursos necessários e
eliminando distrações;
• O Time: O time de desenvolvimento, englobando profissionais de
diversas áreas de conhecimento, de acordo com a necessidade do
projeto.
O Scrum também é formado pelos seguintes artefatos:
• Product Backlog: É a lista de requisitos ou características necessárias
ao projeto;
• Sprint Backlog: É a lista de PBIs (Product Backlog Item) que serão
realizados durante um Sprint;
• Sprint: Uma iteração do processo de Scrum. Um sprint tem uma
duração fixa entre duas a quatro semanas (o tempo exato depende
de time para time).
O Scrum também possui os seguintes encontros na sua prática:
• Sprint Planning: é o encontro que define o que será trabalhado no
próximo sprint;
• Daily Scrum/Meeting: encontro diário do time de desenvolvimento;
• Sprint review: encontro onde o resultado do Sprint é demonstrado;
• Sprint retrospective: encontro onde o time de desenvolvimento avalia
o sprint finalizado.
13. 13
Entretanto, o Scrum, da forma demonstrada, é de difícil aplicação quando há
apenas uma pessoa no desenvolvimento. Para isso, o Scrum Solo é a solução
encontrada. Nesta modalidade, os diversos papéis são descartados, mas os
princípios e os artefatos do Scrum são respeitados, seguindo-se, então, as boas
práticas do Scrum.
14. 14
7 Arquitetura do Sistema
O Scrum, apesar de valorizar mais software funcionando do que
documentação completa (Beck et al, 2001), não exclui a documentação que faz
parte do processo de desenvolvimento, principalmente em um trabalho como um
TCC.
O desenvolvimento de jogos apresenta um documento próprio para o
mesmo, conhecido como documento de design de jogo. O documento de design de
jogo (GDD) é o principal documento no desenvolvimento de um jogo. É nele que se
concentram todas as informações sobre o jogo e suas regras. É com ele que os
diferentes envolvidos no processo irão basear suas decisões, pois é este documento
que deve deixar claro o que é o jogo, numa linguagem acessível a todo o grupo.
Fazendo um paralelo com um software de negócios, é no GDD que se
encontram as regras de negócio, requisitos funcionais e não funcionais e outros
necessários.
O modelo utilizado é baseado no disponível em Digital Worlds (2010). O
modelo foi produzido durante a preparação para um curso na The Open University
(The Open University, 2011).
Acrescenta-se a este documento, alguns outros documentos normalmente
utilizados no desenvolvimento de software. Sendo assim, os mesmos se encontram
presentes nas próximas subseções.
15. 15
7.1 Game Design Document
7.1.1 Título
Blinded Walker
7.1.2 Descrição Curta
O jogo consiste em guiar um protagonista vendado através de um cenário
com diversas armadilhas.
7.1.3 Tipo/Gênero do Jogo
O jogo é um misto de plataforma e puzzle.
7.1.4 Cenário
O jogo passa em um mundo de fantasia. Este mundo não possui uma
história específica ou especial. Ele apenas serve como fundo ao jogo e justificativa
para a forma que o jogo se constitui. Neste mundo existem perigos aos seus
habitantes: buracos sem fundo e afins.
A gravidade neste mundo, também, é diferente, pois existem plataformas
flutuantes. Essas plataformas fazem parte dos caminhos e rotas possíveis para se
andar. Diversas escadas permitem que seus habitantes possam facilmente andar
entre elas.
7.1.5 Descrição Longa
O jogo é sobre um personagem (Bob) que gosta de se arriscar. Ele é
conhecido como Blinded Walker, pois ele tem a mania de andar vendado. Ele diz
que a sorte sempre está ao seu lado, e que não vai acontecer nada demais com ele.
Ele possui uma equipe de uma pessoa (Arthur), que está sempre estressado com o
seu colega. Bob sempre se venda e sai caminhando até encontrar com Arthur, que
retira a sua venda e acaba com o desafio (assim, vencendo-se uma fase).
Ao jogador cabe ‘guiar’ Bob através das fases, retirando os obstáculos e
criando os caminhos necessários. O jogador, portanto, faz o papel da ‘sorte’ ao
longo do jogo. O jogador só pode agir sobre regiões pré-definidas do cenário e sobre
o personagem principal. As regiões pré-definidas são as regiões de blocos. Existem
alguns tipos de blocos: pedra rachada, que pode ser destruída somente, espaço
pontilhado sem identificação, pode ser criado e apagado, espaço pontilhado com
relógio, cria bloco que dura por pouco tempo (tempo a ser verificado/ajustado) e
espaço pontilhado com um ‘x’, bloco que pode ser criado e não mais apagado.
Ao tocar sobre o personagem, ele muda o sentido do seu caminhar, ele
também muda o sentido ao bater em algum obstáculo, mas cai em um buraco se o
mesmo existir. O personagem, também, sobe escadas automaticamente e cabe ao
jogador derrubá-lo caso não queira que o mesmo suba.
16. 16
A câmera se movimenta automaticamente, sempre mantendo o foco no
personagem principal. O jogador não pode movimentar a câmera em momento
algum do jogo.
7.1.6 Sistema de Jogo
O sistema comporta os seguintes elementos:
• Personagem do Jogador (Bob): caminha automaticamente durante
todo o tempo de jogo, tendo o seu movimento modificado pelos
seguintes eventos:
o Blocos na altura do personagem ou paredes: muda o sentido
do movimento para seu oposto;
o Escadas: o movimento se torna vertical, sempre no sentido de
baixo para cima (subida);
o Toque do jogador: muda o sentido do movimento, seja o
horizontal (forçando o personagem a caminhar no sentido
oposto), seja o vertical (este só existente quando o
personagem estiver subindo uma escada, fazendo o
personagem cair da escada);
o Ponto de chegada (Arthur): para o movimento e finaliza a fase
(vitória);
o Queda em armadilhas: para o movimento e finaliza a fase (fim
de jogo).
• Blocos: existem quatro tipos de blocos que reagem ao toque, sendo
eles:
o Rachado: bloco que poderá ser destruído através do toque do
jogador, não pode ser construído;
o Pontilhado: bloco que, através do toque do jogador, pode ser
criado e destruído tantas vezes quanto forem requeridas;
o Pontilhado com relógio: bloco que pode ser criado, mas que
dura um curto período de tempo (especificar o tempo);
o Pontilhado com um ‘x’: bloco que pode ser criado e não mais
destruído.
• Escadas: servem para subir, e somente subir, para um nível acima de
plataformas. A escada não reage ao toque do jogador. Ativa a ação
subir do personagem principal;
• Ponto de chegada (Arthur): define o destino final de uma fase. Ativa o
evento de vitória;
• Armadilhas: as armadilhas são locais que ativam o evento de fim de
jogo (o jogador perde). São representadas por diversos buracos
(falhas) nas plataformas.
7.1.7 Game Play
O jogo é jogado apenas através do toque. A mecânica é simples, o jogador
apenas pode tocar em certos elementos que se destacam na tela e, a partir desse
toque, um evento ocorre. Este evento pode disparar uma mudança no cenário (ao
tocar em um bloco) ou mudar o sentido do movimento do personagem principal.
17. 17
Toques em outras regiões não causam nenhum evento. O jogador não
poderá aplicar efeitos de zoom, nem arrastar a tela para visualizar outras partes do
cenário.
7.1.8 Tela de título e telas de informações
Devem existir as seguintes telas que não pertencem ao gameplay:
• Tela de identificação do desenvolvedor: Uma tela simples, não
necessariamente animada, onde o logo do desenvolvedor é
claramente identificável;
• Tela Título: Tela contendo o título do jogo e a opção de iniciar o jogo,
indo para a tela de menu inicial. Esta tela deve aparecer após a tela
de identificação do desenvolvedor. É esperado o título do jogo e um
tema musical;
• Tela de menu inicial: deve conter o mesmo background da tela título.
Devem existir as opções básicas de inicio de jogo: [jogar],
[pontuações], [opções] e [sair]. O item de menu [sair] será adicionado,
prevendo uma possível adaptação a dispositivos sem botão físico de
voltar;
• Tela de pontuações: listará as fases já vencidas pelo usuário e a
pontuação obtida em cada uma delas;
• Tela de opções: o usuário poderá definir o nível sonoro para as
músicas de fundo e para os efeitos sonoros. Devem ser utilizados
sliders para este fim;
• Tela jogar: na primeira inicialização dessa tela, aparecerá uma
introdução aos personagens e a história do jogo (com a opção de
pular). Na segunda utilização em diante, o jogador irá direto para a
tela que lista os níveis existentes. Nessa listagem, o primeiro item
será a possibilidade de rever a introdução, o segundo item em diante
serão as fases em sua sequencia de jogo, sendo que uma fase só se
torna disponível após a anterior ser vencida;
• Tela de vitória de fase: a tela apresentará a pontuação obtida e irá
parabenizar o jogador pela vitória, possibilitando ir diretamente para a
próxima fase, ou voltar para a lista de fases;
• Tela de fim de jogo: a tela apresentará uma mensagem de
encorajamento e as opções de tentar novamente a fase ou ir para a
lista de fases;
• Menu in-game: durante o gameplay, o jogador poderá chamar um
sistema de menu ao pressionar o botão de [pausar] na tela de jogo.
Este menu será composto das opções de voltar para a fase, reiniciar
a fase, voltar para a listagem de fases ou ir diretamente para a tela de
menu inicial (facilitando o jogador sair do jogo).
7.1.9 Requisitos de Áudio
As seguintes características são esperadas na parte sonora:
• Deve existir a possibilidade de desabilitar os sons do jogo;
18. 18
• Música de background: tema musical que acompanhe o jogador
durante o game play de uma fase;
• Sons de alerta: devem existir diversos sons de alerta, vinculados a
certos eventos do jogo:
o Seleção de item de menu;
o Seleção de nível para jogar;
o Toque no protagonista;
o Destruição do bloco ’rachado’;
o Criação de bloco;
o Temporizador do bloco temporário;
o Inicio de fase;
o Final de fase;
o Queda em armadilha;
o Troca de folha ao introduzir os personagens/história.
19. 19
7.2 Modelagem Funcional
Estão presentes os Casos de Uso (para modelagem do sistema de menus e
uso geral do jogo), mapa de transição (demonstrando a relação entre as telas do
jogo), diagramas de classes e backlogs (Sprint).
7.2.1 Casos de Uso
Figura 2 - Diagrama de caso de uso
20. 20
7.2.2 Mapa de Transição
O mapa de transição demonstra a transição existente de uma tela para outra
no jogo. A figura 3 abaixo apresenta o mapa de transição deste projeto.
Figura 3 - Mapa de Transição
21. 21
7.2.3 Backlogs
Aqui são apresentados os backlogs dos sprints realizados ao longo do
projeto.
• Sprint 01
o Sistema de Renderização
§ Criação da Atividade
§ Criação da superfície
§ Criação do buffer1 de renderização
§ Criação do sistema de câmeras
§ Criação do laço básico de jogo
§ Criação do thread2 de jogo
o Ajustes GDD
§ Título
§ Descrição Curta
§ Tipo/Gênero do Jogo
§ Cenário
§ Descrição Longa
§ Sistema de Jogo
§ Game Play
§ Tela de título e telas de informações
§ Requisitos de Áudio
• Sprint 02
o Sistema de Input
§ Captura do evento de toque
§ Transformação do ponto de toque de espaço em tela
para espaço em jogo
§ Criação do mecanismo de tratamento do evento de
toque
o Sistema de carregamento e armazenamento de níveis
§ Definição do formato a ser utilizado
§ Criação dos elementos dentro do formato
§ Carregamento e gerenciamento dos recursos
• Sprint 03
o Sistema de Colisão
§ Adaptação do BBox3
§ Sistema de gravidade
§ Tratamento do evento de queda do personagem jogável
• Sprint XX
o Rescrita do sistema
§ Sistema gráfico (render) utilizando OpenGL
§ Colisão
§ Armazenamento de níveis (utilizando TXT)
§ Input
• Sprint 04
o Sistema de animação
1
Memória
de
uso
temporário.
2
Unidade
de
trabalho.
3
Classe
inicialmente
utilizada
para
tratamento
de
colisões.
22. 22
§ Definição dos sprites4
§ Definição de vértices
§ Tratamento de animações
§ Sistema para desenhar os sprites
§ Sistema de gerenciamento de recursos
• Sprint 05
o Assets gráficos
§ Plano de fundo (background)
§ Personagem principal (Bob)
§ Personagem secundário (Arthur)
§ Blocos diversos
§ Mensagem de vitória
§ Mensagem de derrota (game over)
o Ajustes na documentação
• Sprint 06
o Sistema de som
§ Música
§ Efeitos sonoros
o Ajustes na documentação
• Sprint 07
o Assets sonoros
§ Música de jogo
§ Efeitos sonoros diversos
• Ação em blocos
• Ação em personagem principal
o Ajustes na documentação
• Sprint 08
o Sistema de menus
§ Tela de logo
§ Tela de menu principal
§ Seleção de níveis
§ Menu durante jogo (nível)
• Sprint 09
o Níveis de jogo
• Sprint 10
o Validação
§ Criação dos formulários
§ Aplicação da validação
§ Análise dos resultados
4
Maiores
detalhes
no
capítulo
9.7.1
23. 23
7.2.4 Diagramas de Classes
7.2.4.1 Renderização
A figura 4 demonstra o diagrama de classes vinculado ao sistema de
renderização.
Figura 4 - Diagrama de classes do sistema de renderização
7.2.4.2 Input
A figura 5 demonstra o diagrama de classes vinculado ao sistema de input.
Figura 5 - Diagrama de classes do sistema de input
24. 24
7.2.4.3 Colisão
A figura 6 demonstra o diagrama de classes vinculado ao sistema de
colisão.
Figura 6 - Diagrama de classes do sistema de colisão
7.2.4.4 Uma visão geral das classes restantes
A figura 7 demonstra uma visão geral das outras classes existentes neste
projeto.
Figura 7 - Uma visão geral de outras classes do sistema
7.3 Modelagem dos Dados
Inicialmente foi pensado que existiria a necessidade de um armazenamento
utilizando o SQLite, padrão na plataforma. Entretanto uma outra solução foi
encontrada com o Shared Preferences, conforme item 9.13 deste trabalho.
25. 25
7.4 Modelagem de Interface Gráfica do Usuário
A figura 8 demonstra o protótipo inicial para a interface gráfica do usuário
durante o desenvolver de um nível.
Figura 8 - Protótipo de interface com o usuário
Bloco fixo
Personagem não jogável
(chegada)
Escada
Personagem jogável
Bloco
interativo
26. 26
8 Funcionamento do Sistema
Com os trabalhos encerrados, o comportamento esperado do sistema é
aquele previsto no nosso GDD. O jogo inicializa com uma tela de logo, sendo a
mesma seguida de uma tela de menu principal, onde o nome do jogo esta presente.
Os níveis são selecionáveis a partir de uma interface própria. O som pode ser
ativado, ou desativado, a partir do menu principal.
Uma exceção está presente em relação ao conteúdo do GDD: os slides
contando a história. Para tais slides, é importante um bom desenho, que possa
traduzir o que pretendemos passar de forma visual. Infelizmente não possuímos,
ainda, um profissional que possa produzir tal material.
O jogo inicia com uma tela que apresenta o logotipo do desenvolvedor,
seguida por outra tela com o título do jogo. Essa segunda tela possui três ícones: um
para a tela de créditos, outro para ativar/desativar o som e o terceiro para a tela de
seleção de níveis. Para escolhe uma opção, basta tocar sobre o ícone desejado.
A seleção de níveis tem o formato de um rolo de filme onde, tocando uma
das pontas do rolo, é possível escolher um nível. Existe um ícone para retornar a
tela de título e para entrar em um nível basta tocar sobre o mesmo.
Durante o jogo existe a opção de pausar, representada por um ícone.
Estando o jogo pausado, é possível voltar a seleção de níveis, reiniciar o nível ou
continuar jogando. Essas opções são, também, representadas por ícones. Existe,
ainda, alguns ícones nas telas de fim de jogo (quando o jogador perde) e vitória.
Esses ícones permitem que, na tela de fim de jogo, voltar a seleção de níveis ou
tentar novamente o nível e, na tela de vitória, voltar a seleção de níveis, ir para o
próximo nível ou tentar novamente o nível atual.
Maiores detalhes de como o jogo funciona podem ser verificados no GDD e
no item 9.10 deste mesmo documento.
27. 27
9 Descrição dos desafios e soluções encontrados
Dado que este formato de relatório favorece um projeto de negócio mais
clássico, iremos abrir este espaço, onde os diversos questionamentos que
aparecerem durante o desenvolvimento serão colocados, juntos com suas possíveis
soluções e razões que levaram a certas escolhas.
9.1 O mundo como uma coleção de lista de vetores (ArrayList)
O primeiro desafio encontrado foi como organizar o mundo do jogo. O
mundo do jogo, dentro deste jogo específico, é cada nível disponível. Os níveis são
compostos de diversos objetos, que descrevem o cenário de fundo, os blocos fixos,
os blocos interativos, escadas, personagem jogável e não jogável. Há diversas
formas que podem ser utilizadas: uma matriz, um vetor... etc.
Inicialmente foi pensado em um modelo de grafo, pois o mesmo facilita a
propagação de um evento, dada a sua estrutura. Um grafo permite que, a partir do
nó raiz, seja simples enviar um evento de atualização aos nós existentes, uma vez
que existe uma ligação direta entre eles.
A figura 9 demonstra como um evento pode se propagar em um grafo.
Figura 9 – Um evento se propagando em um grafo
Um evento disparado na raiz
facilmente se propaga.
Esse modelo foi descartado após o TCC I, pois para lidar com os diversos
tipos de elementos e eventos relacionados ao jogo havia uma complexidade acima
do esperado. Houve uma falha em prever o quão complexo o sistema se mostraria.
A solução encontrada para tanto foi a de trabalhar com lista de vetores.
Essas listas são utilizadas para definir todos os blocos existentes no cenário,
existindo uma lista, portanto, para cada tipo específico de bloco. As escadas
também são definidas desta forma. Outros elementos (personagem principal,
background, chegada, menus) são definidos individualmente.
Isso facilita trabalhar com esses elementos e ainda mantem a facilidade para
propagar um evento para todos estes elementos e parar esta propagação, se
necessário.
Em termos de código a estrutura a ser utilizada foi o ArrayList, um tipo de
estrutura padrão do Java, o que nos garante uma boa documentação.
9.2 Renderizando de forma eficiente: aceleração na GPU
Ainda em TCC I, a ideia era utilizar o SurfaceView (um dos elementos para
trabalhar com interface padrão no Android) para a apresentação das diversas
imagens ao usuário. Este view, apesar de ser otimizado para performance, ainda
assim poderia causar situações de queda de performance. A solução na época foi a
28. 28
de restringir o que seria desenhado em cada chamada do método de desenhar,
limitando a quantidade de trabalho realizada para este fim.
Pensando nisso, foi criado um elemento responsável por representar o
espaço nos eixos x e y que um objeto ocupasse. Com este intuito foi criado o BBox,
que nada mais era do que uma classe para guardar os pontos que demarcavam o
canto inferior esquerdo e o canto superior direito de um objeto, permitindo facilmente
definir sua área para os testes necessários.
Entretanto, mesmo com esse cuidado, alguns problemas de performance
ocorriam. Outro ponto importante é a complexidade que este sistema agregava ao
jogo. Refatorar o código, seja para reorganizar, ou por causa de novas
características, estava sendo muito custoso.
Dada essa realidade, uma outra solução foi procurada. Esta solução acabou
sendo o uso de um outro elemento padrão para trabalhar com gráficos no Android: o
GLSurfaceView (GLSurfaceView, 2011).
Em relação ao SurfaceView, a mudança mais importante para o
GLSurfaceView é a utilização de OpenGL. O OpenGL é um padrão amplamente
utilizado no mercado para trabalhar com gráficos que são processados por um
dispositivo gráfico especializado, a unidade de processamento de gráficos (Graphics
Processing Unit – GPU). Este processamento especializado oferece um outro nível
de performance, não somente por trabalhar com um processador especializado em
gráficos, mas também por não precisar compartilhar a unidade central de
processamento (Central Processing Unit – CPU) com outros processos do próprio
jogo. O padrão específico utilizado neste projeto é o OpenGL ES 1.0 (OpenGL ES
1.0, 2011), que é o padrão com maior suporte entre os dispositivos com Android,
uma vez que o mesmo é suportado pela plataforma desde a versão 1.0.
Apesar de apresentar um novo padrão de performance para o projeto, onde,
até agora, não foi necessário controlar o que deve ou não ser apresentado ao
usuário, essa mudança apresentou um novo grupo de desafios.
O OpenGL trabalha, por padrão, com um plano cartesiano que possui três
eixos, sendo eles denominados x, y e z. O eixo x é um eixo horizontal, com o sentido
de crescimento em direção para a direita. O eixo y é vertical, com o sentido
crescimento em direção para cima. O eixo z é perpendicular ao plano formado pelos
eixos x e y, e seu sentido de crescimento é em direção ao observador. Claro que é
possível observar esse plano de outros ângulos, mas esta é a forma padrão de
trabalhar com os mesmos.
Para exibir imagens a uma saída, no OpenGL trabalha-se com o conceito de
câmeras e cenas. Uma cena é um conjunto de objetos gráficos que são trabalhados
para serem exibidos. Uma câmera define que espaço dentro do plano que o
observador está vendo, definindo limites dentro do plano cartesiano onde os gráficos
gerados são retornados ao dispositivo de saída. Essa câmera pode trabalhar com
mais de um tipo de projeção, que deve ser escolhida também de acordo com o que
é necessário. Em uma das formas, as imagens são geradas levando em
consideração a posição do observador em relação a cena, criando um efeito de
perspectiva, na outra a posição do observador não é levada em conta, criando uma
imagem sem perspectiva. Esta segunda forma é a escolhida para trabalhar neste
jogo que é em duas dimensões, e a aplicação de perspectiva poderia deformar as
imagens.
É importante lembrar que até é possível apresentar todo o espaço do
OpenGL em uma tela, mas essa representação dificilmente teria algum valor prático,
o mesmo pode ser dito em relação a apresentação de um nível de uma só vez. Para
29. 29
isso o sistema de câmeras deve definir qual o volume onde os gráficos gerados
serão levados a tela do dispositivo. É válido lembrar que, apesar de que não vamos
trabalhar com o eixo z, este volume definido na câmera deve contem algum valor em
z e não zero. Por isso citamos volume, e não área. Este volume onde a câmera
considera válido e, portanto, ‘filma’ o nosso conteúdo é chamado de view frustum.
Outro ponto importante é que o OpenGL trabalha com vértices e superfícies.
Um vértice é um ponto, dentro do espaço definido pelos três planos, que define uma
borda de uma superfície. Uma superfície é um conjunto de vértices, a partir dos
quais é definido o seu formato. Geralmente são usados três vértices, o que resulta
em um triangulo.
Vértices e superfícies não possuem informação de imagens, no máximo
alguma cor. Para possibilitar o uso de imagens diversas, tais como as dos blocos
que compõem um nível, é necessário passar ao OpenGL uma imagem e ligar uma,
ou mais, superfícies a mesma. Esta imagem dentro do OpenGL é conhecida como
textura.
O que acontece é que neste projeto são trabalhadas imagens retangulares
(os blocos, escadas, personagem e etc se baseiam em quadrados e retângulos,
lembrando que o quadrado nada mais é do que um caso específico do retângulo),
logo um triangulo não é a superfície mais recomendada as nossas necessidades.
Para resolver esse problema, trabalhamos com dois triângulos encostados, o que
nos permite ter a superfície adequada de forma simples e rápida. Mas nem tudo é
tão simples. Ao trabalhar com dois triângulos, e tentar movimenta-los ao mesmo
tempo, pequenas falhas na linha onde os dois se tocam podem ocorrer, gerando
erros gráficos. Logo, simplesmente gerenciar dois triângulos lado-a-lado não é a
melhor opção.
A solução para este caso está dentro do próprio OpenGL. Existe como
definir um certo número de vértices e utilizar índices que indicam ao OpenGL quais
vértices utilizar para cada triângulo, nós apenas trabalhamos com esses vértices e
índices. Desta forma, criamos os quatro vértices e os seis índices, e deixamos o
OpenGL cuidar deste ‘retângulo’. Para isso ser possível, além dos quatro vértices e
dos seis índices é preciso passar outra informação ao OpenGL, qual a forma básica
que ele irá utilizar para aproveitar esses vértices. Com isso, o problema de existir um
espaço entre os triângulos deixa de existir.
Tendo a superfície pronta, só falta aplicar a textura a mesma. Isso é feito
associando os vértices anteriormente citados com as coordenadas da textura. Logo,
cada ‘ponta’ da textura é associada a um vértice, o que faz com que a textura seja
aplicada ao longo de toda a superfície. Uma nota importante aqui, podemos utilizar
quaisquer pontos das coordenadas da textura com os vértices que definimos para a
superfície. Essa informação será de grande valia quando formos ver o sistema de
animação.
Finalizando o novo sistema de renderização encontra-se o fato de que o
sistema foi pensado para ser independente da resolução física do dispositivo. Esse é
um cuidado já prevendo a utilização do jogo em telas de diferentes resoluções e
proporções. Este sistema é feito aproveitando que o sistema de eixos do OpenGL é
independente do dispositivo utilizado para output. Neste projeto isso funciona da
seguinte forma: uma câmera possui a informação da resolução física do aparelho e
qual o tamanho nos eixos x e y do nosso view frustum. A partir dessas duas
informações, passamos ao OpenGL que nosso viewport (a visão final para a qual a
nossa câmera irá desenhar) vai da posição (0,0) até (resolução x da tela, resolução
y da tela), garantindo que o que a câmera capturar será automaticamente
30. 30
escalonado pelo OpenGL para a nossa tela. Ou seja, o OpenGL captura a imagem
utilizando nosso view frustum (que tem um tamanho arbitrário qualquer) e escalona
para nosso viewport, de forma automática e sem perdas de performance. Temos
que levar em conta que, ao fazer isso, o OpenGL pode escalonar nossa imagem
para uma tela maior, ou menor, do que nosso espaço virtual do view frustum. Ao
escalonar, algum algoritmo deve ser aplicado para permitir este trabalho. O
algoritmo aqui escolhido é o que utiliza a cor mais próxima, o que garante que não
haverá um efeito de perda de definição da imagem, caso a diferença entre os dois
views seja muito grande. Por outro lado, isso pode levar aos gráficos apresentarem
um fenômeno conhecido como pixelização, exemplificado na figura abaixo.
A figura 10 demonstra o efeito de pixelização. A partir da imagem da
esquerda a da direita é gerada e, ao escalonar a imagem, os pontos (pixels) que a
compõe se tornam mais visíveis.
Figura 10 – Exemplo de pixelização
No jogo em questão o efeito não é um problema, na verdade é o que é
esperado. Todo caso, a escolha por essa forma de trabalhar tem implicações em
como a identificação de um toque foi feita, o que veremos a seguir.
9.3 Sentindo você: um sistema para entrada de dados
Um jogo, para garantir a sua interatividade, precisa prover uma forma de o
usuário participar ativamente do que está ocorrendo. A plataforma Android permite
diversas formas de interação com o usuário: teclado, acelerômetros e toque. Dentro
da dinâmica esperada neste jogo, o toque é a opção que supre todas as nossas
necessidades.
Para tanto, uma pequena coleção de classes foram criadas.
• TouchEvent guarda um evento de toque. Basicamente possui o tipo
de toque e a posição nos eixos x e y em que este toque ocorreu.
• TouchEventPool é uma classe para pooling 5 de TouchEvent. Isso
serve para tentar reutilizar os objetos de TouchEvent, diminuindo a
instanciação e destruição de objetos durante a execução do jogo.
• InputHandler é a classe que gerencia a entrada de dados, buscando
objetos do pool, guardando-os de volta no pool e passando os objetos
necessários as classes que os pedirem.
5
Uma
classe
utilizada
para
pooling
é
uma
classe
responsável
por
instanciar
outra
classe
e
manter
a
referência
desses
objetos,
deixando-‐os
disponíveis
para
quando
necessário.
31. 31
9.4 Estou tocando em você, ou não?
Outro problema que é encontrado no desenvolvimento de um jogo é a
questão de para onde no jogo o input do jogador foi direcionado. No caso de um
jogo baseado em toque, onde existir algum tipo de scrolling (movimento de câmera a
outras regiões do nível), o tratamento dessa entrada de dados passa por converter o
ponto de tela em um ponto de jogo.
O ponto de jogo é onde, efetivamente para o sistema, o jogador interagiu. A
partir desse ponto é que deve ser calculado se o jogador solicitou que o personagem
jogável se movimente em outra direção, ou se um bloco deve ser acionado, ou se
nada deve ocorrer.
Na figura 11 temos uma representação da diferença entre a área de tela e a
área de toque em um jogo onde existam scrolling horizontal e vertical. Notamos que
um toque em área de tela equivalente as coordenadas (X, Y) = (10, 10), não seria
necessariamente o mesmo ponto em área de jogo.
Figura 11 – Diferença entre a área de tela e a área de jogo
Área de jogo
Área de tela
Para descobrir qual é o ponto em área de jogo, para então saber onde de
verdade foi o toque, devemos aplicar um cálculo que permita essa conversão. Aqui
temos o impacto direto de trabalhar com um sistema independente de resolução do
dispositivo que rodará o jogo. Para facilmente identificar este ponto dentro do
espaço de jogo, começamos normalizando o vetor do toque e transformando o
mesmo para uma região com a mesma área do nosso view frustum. Após isso,
adicionamos a posição da tela a este vetor. Como a posição da tela está sendo,
neste projeto, definida pelo ponto central da mesma, descontamos metade do
tamanho do view frustum de cada eixo.
Após essas operações, temos o ponto dentro do espaço de jogo definido de
forma correta, e já podemos trabalhar com ele.
Ter o ponto correto é o primeiro passo, o segundo é definir em qual objeto
este toque foi dado. Para tanto, temos que testar esse toque contra uma região
definida para cada objeto que compõem o nível e que pode reagir a um toque. Para
este fim, a classe que era conhecida como BBox no TCC I foi rescrita e se tornou o
nosso BoundingBox. A ideia por trás de um BoundingBox é definir as fronteiras de
um objeto dentro do espaço de jogo, permitindo verificar se um ponto qualquer está
contido dentro de sua área.
32. 32
Para o toque no personagem principal foi verificado que a proporção
escolhida inicialmente entre os dois views existentes criava uma área muito
pequena, o que dificultava acertar o personagem. Este problema foi corrigido ao
mudar essa proporção, fazendo com que todos os elementos do jogo tenham
representações maiores em tela. Esta sugestão veio do professor orientador e,
temos de admitir, fez uma diferença considerável.
9.5 Definindo Níveis
Um dos objetivos do jogo é possuir 10 níveis diferentes. Para tanto, deve
existir uma forma de armazenar estes níveis de forma que seja possível edita-los de
maneira simples (para permitir ajustes) e que não ocupem muito espaço (para não
onerar demais o dispositivo do usuário).
Algumas possibilidades para tanto seria o armazenamento em banco de
dados, em um arquivo binário pré-compilado, um XML ou um TXT. O banco de
dados poderia se tornar um pouco complexo, e nada amigável a uma rápida
modificação. O binário pré-compilado seria a forma com maior desempenho para
carregamento, mas seria necessário uma ferramenta que gerasse esse compilado
integrada ao desenvolvimento. O XML, escolhido como opção em TCC I, foi
descartado em pró do TXT, uma vez que, para o design desse jogo, o XML não
apresentava vantagens sobre o TXT, além de que o TXT é um formato mais simples
de trabalhar.
Sendo assim, o TXT foi o caminho escolhido, por oferecer um meio termo
entre complexidade, facilidade de edição e desempenho. Este TXT é posicional,
onde cada posição em cada linha representa um espaço no nível. Como
trabalhamos basicamente com retângulos, é fácil definir um mapa em TXT ao
mesmo tempo que se tem ideia de como o mesmo está ficando em relação ao que
será apresentado ao usuário, vide exemplo abaixo.
A figura 12 apresenta um exemplo de TXT com a definição de um nível para
testes.
Figura 12 – Exemplo de um TXT com um nível para testes
Para o carregamento deste TXT para um nível de jogo, algumas regras
devem ser respeitadas:
• Um espaço em branco representa um local sem item no jogo, mas o
espaço vazio deve ser respeitado e levado em conta no
posicionamento dos demais elementos;
• Um ‘S’ representa um bloco sólido sem possibilidade de interação;
• Um ‘B’ representa a posição inicial do personagem jogável. Na
possibilidade de mais de um ‘B’ presente no TXT, apenas o último
será considerado;
• Um ‘C’ representa um bloco rachado, que pode apenas ser removido
do cenário;
33. 33
• Um ‘W’ representa um bloco que pode ser ativado apenas uma vez;
• Um ‘A’ representa um bloco que pode ser ativado e desativado
quantas vezes o jogador julgar necessário, que inicializa desativado;
• Um ‘a’ representa um bloco que pode ser ativado e desativado
quantas vezes o jogador julgar necessário, que inicializa ativado;
• Um ‘T’ representa um bloco que, após ativado, se mantem ativo por
um intervalo de tempo;
• Um ‘G’ representa o ponto de chegada (Arthur);
• Um número natural entre 1 e 9 representa o índice do número de
escadas a ser empilhadas. Este índice é multiplicado por quatro para
se definir o número de escadas até o próximo andar.
9.6 Colisão: o que acontece entre nós
Colisão é o que ocorre quando dois objetos dentro do jogo se tocam. Desde
que, claro, eles possam se tocar. É comum trabalhar com mais de um plano, o que
faz que existam objetos que não colidam, assim como alguns tipos de objetos que
não colidem de forma alguma. Neste jogo, a colisão entre objetos é toda centrada no
personagem principal. É ele que se movimenta podendo colidir com um bloco em
seu caminho, é ele que pode colidir com uma escada, com o ponto de chegada e,
até, com o chão, o que permite controlar se ele deve cair ou não.
Para trabalhar com colisões, o BoundingBox criado para tratar eventos de
toque foi modificado para testar a colisão com outro BoundingBox, uma vez que o
mesmo já define a área de um objeto.
Mesmo podendo reaproveitar uma classe já existente, isso não resolvia
todos os casos de colisão do personagem principal com o espaço de jogo. Para
resolver todos os casos, o personagem principal possui quatro BoundingBox, sendo
eles:
• BoundingBox padrão, com o nome de bounds: define a área exata do
personagem. Utilizado para testes de toque do jogador e para
verificar se, em uma queda, o personagem chegou ao chão;
• BoundingBox groundBounds: utilizado para testar se o personagem
ainda está caminhando sobre uma superfície. Quando o mesmo não
está, ele começa a cair. Este BoundingBox está levemente deslocado
para baixo no eixo y em relação ao padrão;
• BoundingBox directionBounds: utilizado para verificar se o
personagem bateu contra uma parede. É menor no eixo y em relação
ao padrão, mas um pouco maior no eixo x. Isso evita que o
personagem entre em uma parede antes de detectar a colisão;
• BoundingBox ladderBounds: utilizado para testes contra escadas,
para verificar se o personagem deve subir. É menor no eixo x em
relação ao padrão, para evitar que o personagem suba pela escada
sem estar na frente dela.
Com essas quatro regiões é possível atendar a todos os casos existentes no
jogo em questão.
9.7 O mundo é mais que imagens estáticas: um sistema de animação
34. 34
Até agora o jogo trabalhava com imagens estáticas para todos os seus
objetos. Não que isso seja necessariamente ruim, é comum trabalhar com material
temporário enquanto testamos um conceito ou outro ponto do sistema. Entretanto,
ao entregar um jogo pronto, é esperado que os personagens se movimentem de
alguma forma, refletindo o seu movimento em tela, por exemplo.
Para este ponto o sistema de animação foi desenvolvido, buscando
acrescentar de forma fácil esses movimentos aos elementos do jogo. Entretanto,
antes de entrar em detalhes sobre como foi feito em código a solução, vamos ver
como, em um jogo 2D, definimos os quadros de animação de um objeto.
9.7.1 Sprites, o inicio do movimento
Jogos 2D costumam ter suas imagens desenhadas em 2D (sem surpresas
aqui). O que acontece é que para ter um personagem caminhando, por exemplo,
alguém deve desenhar todos os quadros dessa animação. Por quadro de animação
se entende uma imagem estática que representa o movimento de um objeto em
determinado tempo.
Um movimento, logo, é composto de vários quadros que, ao serem trocados
de forma ordenada, passam a impressão de movimento. Esses quadros, e aqueles
que representam objetos sem animação também, possuem o nome de sprites
quando trabalhamos com jogos 2D.
Sendo assim, para cada movimento temos um conjunto de sprites, o que faz
com que a técnica normalmente utilizada para trabalhar com essa animação se
basear em utilizar um arquivo de imagem que agrupe todos esses sprites, arquivo
este conhecido como sprite sheet. A partir desse agrupamento, criamos uma região
de tamanho padrão e, então, percorremos este arquivo, utilizando um sprite por vez.
Abaixo temos uma ilustração desta técnica.
A figura 13 demonstra um sprite sheet com o sprite que representa o quadro
de animação atual selecionado.
Figura 13 – Exemplo de sprite sheet com um sprite selecionado
Quando o sistema de renderização estava sendo apresentado, foi citado que
quaisquer pontos nas coordenadas da textura podem ser utilizados para serem
ligados a um conjunto de vértices. Como também já citado, para o OpenGL as
imagens são texturas, logo com o sprite sheet não seria diferente. Sendo assim, é
possível criar uma forma de vincular um conjunto de vértices a um pedaço específico
de uma textura, permitindo o uso da técnica de animação anteriormente explicada. O
sistema de animação é todo construído em torno dessa ideia, possuindo os
seguintes pontos importantes:
• Uma animação possui o tempo que a mesma deve levar em conta
para mudar a imagem que deverá ser apresentada ao jogador;
35. 35
• Uma animação deve controlar se a mesma ocorre em loop, ou seja,
se após o último quadro voltamos ao primeiro, ou se não existe loop;
• Uma animação deve conter os diversos quadros utilizados para a sua
necessidade.
Definido como o sistema de animação deve se portar, a classe Animation foi
criada, mas falta ainda definir como seriam definidos os quadros da animação.
Como os quadros nada mais seriam do que regiões de uma textura, a classe
TextureRegion foi criada, com o objetivo de guardar as informações necessárias
sobre os diversos sprites contidos em uma textura, e poder passa-los a uma
animação. Esta classe guarda a textura que é utilizada e em quais pontos dentro das
coordenadas de textura a imagem que queremos se encontra. Essas informações
são posteriormente utilizadas para ligar aos vértices necessários para apresentar a
imagem ao jogador.
Partindo desse principio, em que todas as imagem de uma animação podem
estar presentes em uma única textura, por que não centralizar todos os objetos
possíveis em uma só textura?
Essa abordagem trás uma vantagem importante, utilizando uma textura ao
invés de várias, temos menos chamadas para ligar a textura ao OpenGL e, portanto,
menos mudança de estados, o que resulta em melhor performance. Mas o que
exatamente queremos dizer com isso?
9.7.2 Uma breve história no tempo: Uma explicação do OpenGL e os estados
O OpenGL define uma forma padrão e eficiente de trabalhar com
dispositivos gráficos especializados, mas isso já sabemos. O que vamos ver tem
haver mais com a forma de como ele funciona internamente.
Iremos traçar um paralelo com uma fábrica, o que é um bom exemplo. Com
OpenGL configuramos um estado, que é como se fosse configurada uma fábrica
para produzir um determinado produto. Uma vez feita essa configuração, basta
iniciar o processo e fabricar várias peças iguais, mas em alta velocidade. Quando
ocorre de precisar fabricar um produto diferente, a fábrica precisa parar e ser
reconfigurada, o que faz com que ela não possa produzir neste meio tempo, sem
falar que é uma operação demorada. O OpenGL funciona de forma análoga. Ao
setar uma configuração com um viewport, um view frustum, um padrão para
escalonar, uma textura e etc, basta apresentar a imagem resultante ao dispositivo de
saída em alta velocidade (o que resulta em vários quadros por segundo, também
conhecido como frames per second – FPS). Se precisarmos trocar de textura, cada
troca força um novo setup, o que gera uma mudança de estado, operação que é
muito custosa dado a velocidade em que o OpenGL trabalha.
Voltando a animação e a apresentação de imagens ao jogador, vamos,
então, colocar todos os objetos possíveis no menor número de texturas, evitando
quedas de performance por mudança de estados. Para possibilitar o trabalho com
essas (utilizando uma expressão comum em fabricação em fábricas) bateladas, a
classe SpriteBatcher foi criada. Ela é a ponte entre o sistema de animação, e as
outras imagens apresentadas ao jogador, para o OpenGL.
36. 36
9.7.3 Detalhes tão pequenos de nós dois, ou mais: como funciona a animação
Agora que temos o sistema de animação definido, podemos explicar como
todo o sistema de animação e renderização funciona em sua versão final.
Tudo inicia com a classe Game, que é responsável pelo bootstrap6 do jogo.
Essa classe estende a classe Activity, que é uma classe padrão Android para uma
atividade, e implementa a interface Renderer, que é a interface que devemos
implementar quando queremos que uma classe possa ser usada como um
renderizador para a classe GLSurfaceView (mais sobre essa classe adiante).
Nesta classe Game é configurado que o jogo utilizará a tela cheia do
dispositivo, escondendo a barra de status do sistema, também é configurado que
não haverá a barra de título, com o nome do aplicativo. Como estendemos Activity,
passamos a instância de Game para o construtor do GLSurfaceView e após, por
implementarmos Renderer, setamos a instancia de Game como renderizador da
nossa instância do GLSurfaceView.
O GLSurfaceView é uma classe padrão do Android que representa uma
superfície OpenGL para trabalharmos. O Android roda seus processos em um
thread a parte, sem que precisemos fazer toda essa configuração. Outra
responsabilidade do Android é fazer o loop que garante que o método onDrawFrame
(que é utilizado para desenhar no dispositivo de saída) seja chamado o maior
número de vezes possível. Esse método, junto com alguns outros utilizados pelo
GLSurfaceView, estão presentes na classe Game, pois são os métodos que
precisamos implementar da interface Renderer.
O motivo de concentrar Activity e Renderer na classe Game é facilitar o
gerenciamento do jogo, outras implementações podem separar esses elementos se
assim acharem necessário.
Tendo essa inicialização feita, guardamos a instancia do GLSurfaceView em
um objeto auxiliar: Graphics. A partir deste, podemos facilmente acessar o próprio
GLSurfaceView e algumas de suas propriedades mais utilizadas (largura e altura).
Claro, uma classe somente com esse intuito não teria o por que de existir. A classe
Graphics vai um pouco mais longe, guardando a instancia de OpenGL ES 1.0
(GL10) que utilizaremos em nosso processo. Assim, as classes relacionadas aos
gráficos ficam agrupadas em um mesmo objeto.
Uma observação importante é que recebemos uma nova instancia da classe
GL10 toda vez que a superfície é criada (método onSurfaceCreated). Isso significa
que toda a vez que nosso jogo perde o foco do dispositivo (quando, por exemplo,
uma ligação é recebida), perdemos a superfície que temos (nossa instância de GL10
é invalidada). Logo, devemos sempre trabalhar com a instancia recebida por esse
método. Esse comportamento se dá por uma escolha da plataforma, e que faz
sentido: quando perdemos o foco, nossa aplicação não se apresenta na tela do
dispositivo, logo não existe porque um processo que só serve para gerar imagens se
manter rodando, consumindo recursos de forma desnecessária.
Todo caso, essa instancia de OpenGL é que será utilizada no nosso
processo de renderização. Ela é nossa instancia do OpenGL, e é ela responsável
por receber todas as informações que resultarão em um belo jogo animado na tela
de um smartphone.
6
No
desenvolvimento
de
software,
uma
classe
responsável
pelo
bootstrap
é
a
classe
que
inicializa
as
outras
necessárias
e
garante
que
o
sistema
seja
inicializado
de
forma
correta.
37. 37
Garantindo o acesso ao OpenGL, podemos começar a criar nossa
representação gráfica, para isso duas outras classes servem de base: Stage e
StageRenderer.
A classe Stage representa uma abstração padrão de um nível de jogo. Ela
guarda as instancias de todos os objetos num nível, e é responsável por passar o
evento de atualização a todos eles. O Stage, também, executa os testes de colisão e
notifica os outros objetos conforme necessário. Por fim, o Stage é responsável por
receber um StageFile, que é um estágio carregado a partir de um arquivo TXT, e
popular o nível com os objetos de acordo com o definido.
O StageRenderer recebe a instancia de Stage, de Graphics e do
SpriteBatcher (mais sobre este último mais adiante). Ele é responsável posicionar a
câmera dentro do espaço de jogo, capturar o OpenGL e setar algumas opções,
iniciar a batelada, passando a textura a ser utilizada pelo SpriteBatcher, passar os
objetos a serem desenhados ao SpriteBatcher e chamar o método do SpriteBatcher
que desenha a nossa tela. No processo de buscar os objetos ao SpriteBatcher, o
StageRenderer busca as regiões de textura (TextureRegion) a serem utilizados,
fazendo chamadas aos diversos objetos e suas respectivas classes de animação
(Animation).
Para facilitar o trabalho com as diversas texturas e regiões de texturas a
classe AssetCollection foi criada. A mesma possui uma coleção estática de objetos a
serem utilizados dentro do jogo. Atualmente a coleção inclui Animation, Texture e
TextureRegion. Em breve teremos a inclusão das classes relativas aos efeitos
sonoros e músicas.
9.8 Produzindo os gráficos e uma descoberta
Após a definição do sistema de animação, chegou o momento de produzir
as imagens que são utilizadas no jogo. Essas imagens deveriam conter um mínimo
de elementos para que fosse possível representar todos os elementos do jogo
conforme o GDD.
Durante algum tempo, existiu um bom número de tentativas de produzir uma
arte original, mas foi algo que não se provou produtivo. Apesar de todo o esforço,
uma verdade ficou: faltou talento para desenhar. Para que o jogo não ficasse sem as
imagens necessárias, o que poderia comprometer o trabalho, a solução foi buscar
essas imagens em algum lugar na internet.
Foram encontrados dois conjuntos de imagens, um proveniente do
‘WidgetWorx’ (WidgetWorx, 2012), disponíveis com uma licença ‘Common Public
License’ versão 1.0 (CPL, 2012), o outro conjunto proveniente de ‘The Spriters
Resource’ (TSR, 2012), disponíveis sem uma licença específica. É importante frisar
que o jogo não será publicado com nenhuma imagem pertencente a esses
conjuntos. O que será realizado, como trabalhos futuros, é a contratação de um
profissional para este fim: produzir os diversos elementos gráficos do jogo.
Sendo assim, o que temos atualmente pode ser visto como imagens do tipo
place holder em versão de luxo. Em especial por conta do segundo conjunto de
imagens, o qual se mostrou uma surpresa.
38. 38
9.8.1 Benchmarking
Como citado anteriormente, o segundo conjunto de imagens foi uma
interessante surpresa. Talvez, até mais do que isso. É muito comum, em termos de
trabalhos diversos, que existam sistemas com funcionalidades próximas a um novo
sendo produzido, ou até mesmo um concorrente para o mesmo segmento que
desejamos atender. Neste caso, é comum que certas similaridades ocorram. O
mesmo pode ocorrer com jogos.
Jogos eletrônicos existem a mais de 30 anos, conforme podemos verificar
pela data de lançamento do Atari, por exemplo. O Atari (Atari, 2008) foi lançado em
1977 e é considerado o primeiro console de grande sucesso. Isso faz com que
muitas ideias já tenham sido convertidas em jogos. Algumas vezes, esses jogos
usam hardware adicional em relação a aquele lançado inicialmente, e o mouse do
SuperNES (SNES), um console da Nintendo, é um exemplo desse tipo de hardware.
O mouse do SNES foi lançado em 1992 e recebeu uma coleção de jogos
que utilizavam o dispositivo. Entre eles um conhecido como ‘Mario & Wario’, lançado
oficialmente somente no Japão em 1993. O jogo em questão, publicado pela
Nintendo, possui premissas praticamente idênticas a do jogo deste trabalho: um
personagem que não pode enxergar deve ser guiado por um cenário. Entretanto, o
trabalho da Nintendo possui algumas diferenças:
• Um personagem vilão (Wario) cobre a cabeça do personagem jogável
com um objeto (balde, casca de ovo, etc);
• Como o jogo não é para telas de toque, e sim para ser jogado com
um mouse, um ponto de referencia é necessário, neste jogo o ponto é
uma fada, que é controlada pelo jogador;
• Existem 3 personagens selecionáveis, cada um representando um
nível de dificuldade (existe uma diferença na velocidade de caminhar
entre eles);
• Existem blocos com tamanhos variados;
• Existem blocos com outros efeitos (andar mais rápido, escorregar);
• Existem combates com o personagem vilão em batalhas;
• Existem moedas a serem coletadas ao longo das fases;
• Existem inimigos que devem ser impedidos de tocar o personagem
principal ao longo das fases.
O jogo da Nintendo em questão foi jogado utilizando-se de emulação, o que
permitiu a coleta dessas informações.
Tendo esse conhecimento em mãos, a decisão foi em tornar o ‘Mario &
Wario’ um benchmarking para o jogo desenvolvido neste trabalho. Ter uma base
para comparações permite explorar onde há possibilidades de melhorias e não
cometer os mesmos erros que em outras plataformas.
Todo caso, uma dúvida legítima ficou neste caso. Ao longo desses 31 anos,
muitos jogos foram jogados. Alguns com mais dedicação, outros apenas para
conhecer. É possível que tenha existido um contato anterior com o ‘Mario & Wario’, e
isso tenha influenciado o conceito desenvolvido aqui. Entretanto, não existem
recordações sobre o mesmo.
39. 39
9.9 Eu posso ouvir você: um sistema sonoro
Sons fazem parte do nosso dia-a-dia, e estão associados aos mais diversos
eventos. Uma corda que vibra (violão), uma borracha sendo utilizada para
desacelerar uma grande massa (um carro freando), e etc. Nada mais natural de ter
sons associados a eventos em nosso jogo, mais uma música de fundo para animar o
clima.
A plataforma Android disponibiliza duas classes para trabalhar com sons,
uma mais adequada para músicas, MediaPlayer (MediaPlayer, 2011), e outra mais
adequada para efeitos sonoros, SoundPool (SoundPool, 2011). Ambas são fáceis de
lidar, mas para simplificar o seu uso neste jogo, duas classes foram criadas Music e
SoundFX.
A classe Music serve para controlar as músicas que tocarão no jogo. Ela
encapsula MediaPlayer, adicionando algumas facilidades para o tratamento com o
som. Já SoundFX serve para os efeitos sonoros do jogo. Ela recebe uma instância
de SoundPool e o identificador de um efeito sonoro presente no SoundPool, assim
agrupando os pré-requisitos para executar um efeito sonoro.
9.9.1 Produzindo sons
Os sons que devem acompanhar os diversos acontecimentos no jogo, seja a
música de fundo, que não teve distrair o jogador, ou os diversos efeitos sonoros,
devem possuir um mesmo tema. Isso significa que os sons não podem serem
discrepantes num sentido de que um som seja típico de desenhos animados, e outro
de um disparo extremamente realista. E, claro, os sons devem acompanhar o estilo
do jogo, sendo tão alegres, ou sinistros, quanto o ambiente a ser criado.
O programa utilizado, GarageBand, possui um grande número de sons para
possibilitar a montagem de músicas em diversos estilos. Alguns desses sons
possuem a denominação de cartoon, ou seja, desenho. Estes sons de desenho
foram a base para as montagens e cortes utilizados no jogo. O Audacity auxiliou
nesta tarefa permitindo um corte mais fácil de segmentos de sons e a conversão
para um formato melhor para trabalhar com a plataforma Android.
9.10 Sistema de menus
O sistema de menus deve atender o diagrama de caso de uso presente no
item 7.2.1 deste trabalho. Para tanto, algumas imagens tiveram de ser criadas
especificamente para este fim. Estas imagens representam a possibilidade de ir a
próxima tela, retornar, tentar novamente um nível, ativar e desativar o som e ver os
créditos.
O sistema de menus funciona com o toque, entretanto não utiliza a dinâmica
de arrastar para mostrar outros conteúdos, como poderia ter sido realizado na tela
de seleção de fases. Mas vamos entrar em mais detalhes sobre as telas,
comentando uma a uma.
A primeira tela é a tela de logo. Nela apresentamos o nosso logotipo, para
que o jogador possa identificar quem é o responsável pelo jogo. Não há nenhum tipo
de interação nesta tela, a mesma fica presente durante um tempo pré-determinado e
depois dispara a próxima tela. Na figura 14 abaixo, a tela em questão.
40. 40
Figura 14 - Tela de logotipo (logo)
Logo após transcorrido o tempo da tela de logo, a tela que é apresentada ao
jogador é a tela de título. Esta tela mostra o título do jogo, possibilitando facilmente
identificar o jogo a ser jogado. Neste tela é possível acessar os créditos, pelo item
de menu que se assemelha a um ‘i’, ativar e desativar o som, pelo ícone que se
assemelha a uma caixa de som, e entrar na tela de seleção de fase, no ícone que se
assemelha a uma seta apontando para a direita. Ainda sobre o som, o retorno visual
de que o som está ativo ou não acontece no próprio ícone que representa o som:
linhas que representam o som saindo da caixa significa que o som está ativo, um ‘x’
ao lado da caixa representa que o som não está ativo. A figura 15 abaixo demonstra
a tela de título.
41. 41
Figura 15 - Tela de título
A próxima tela que abordaremos é a de seleção de nível (figura 16). Nesta
tela é possível voltar a tela de título, utilizando a seta apontando para a esquerda, ou
selecionar e entrar em um dos níveis disponíveis. Os níveis estão disponibilizados
em um formato que lembra um rolo de filme. É uma metáfora que ainda funciona,
mas que no futuro, com a falta de filmes físicos neste formato, pode não dar tão
certo. Para selecionar uma fase é necessário tocar nos cantos do rolo, e a próxima
fase rola até a visão do usuário. Uma vez centralizada na tela, basta tocar no nível
para inicia-lo. Entretanto, existe um mecanismo que limita o acesso a um próximo
nível: um novo nível só se torna disponível após o nível imediatamente anterior ao
mesmo for vencido.
Centralizado sobre o quadro que representa o nível existe o número do nível
e sua melhor pontuação. Pelo número do nível o jogador sabe onde está, e a
pontuação permite que ele possa rapidamente verificar qual foi o seu melhor
desempenho em um nível qualquer. No caso de ser um novo nível, ainda não
jogado, a pontuação aparece zerada (um zero), indicando que o jogador ainda não
possui pontuação na mesma.
42. 42
Figura 16 - Tela de seleção de nível
Após a seleção de um nível, chegamos a tela de jogo (figura 17). O jogo
inicia em um estado de espera. Assim que o jogador realiza o primeiro toque em
tela, o jogo começa a acontecer (Bob começa a caminhar) e, então, o jogador deve
fazer o possível para conquistar esse nível. A tela de jogo possui mais um de
estado: a espera inicial, onde o jogo aguarda o toque inicial do jogador, o estado de
jogando, onde Bob caminha pelo cenário e deve ser guiado, o estado de pausado,
onde o jogador pode dar um tempo para descansar, o estado de nível conquistado e
o estado de perder o jogo, quando Bob cai em algum buraco.
A tela com o estado de jogo rodando é igual a de estado de jogo em espera
inicial (figura 17). A diferença para a espera inicial é que Bob está ativamente se
movimentando e o jogador deve guia-lo. Durante esse estado, um ícone de pausa
fica disponível na parte superior esquerda da tela para que o jogador possa pausar o
jogo assim que necessário.
43. 43
Figura 17 - Tela de jogo
Ao tocar o ícone de pausa, a tela de pausa é apresentada ao jogador (figura
18). Nesta tela existe uma mensagem avisando que o jogo está pausado e três
ícones para ações: uma seta apontando para a esquerda, que serve para voltar a
tela de seleção de fase, uma seta apontando para a direita, que serve para voltar ao
jogo atual, e uma flecha girando, que serve para reiniciar o nível. Esta tela também
aparece automaticamente se o jogo perder o foco do dispositivo, seja por uma
ligação ou outro evento. Desta forma, ao voltar ao jogo, o jogador tem tempo para
focar novamente na tarefa que estava realizando no nível em questão.
44. 44
Figura 18 - Tela de pausa
Existem duas alternativas de final de nível: conquistar o nível ou perder o
nível. Para cada condição uma tela diferente é apresentada ao jogador.
A tela de fim de jogo (figura 19) é apresentada quando o jogador perde o
nível. Esta condição é alcançada quando Bob cai em algum buraco existente em um
nível. Nesta tela, junto com a mensagem de fim de jogo, dois ícones estão
disponíveis: uma seta apontando para a esquerda, para retornar a tela de seleção
de nível, e uma flecha girando, para tentar novamente o nível atual.
45. 45
Figura 19 - Tela de fim de jogo
A tela de vitória, ou de conquista de nível, é demonstrada na figura 20. A
mesma ocorre quando o jogador consegue guiar Bob com sucesso até Arthur. Nesta
tela existe a mensagem de que o jogador venceu e três ícones: seta apontando para
a esquerda, voltar a tela de seleção de nível, seta apontando para a direita, ir para
próximo nível, e flecha girando, tentar o nível novamente. O ícone que leva a
próxima fase só aparece se existe uma próxima fase. Logo abaixo dos ícones é
apresentada a pontuação obtida pelo jogador naquela partida.
46. 46
Figura 20 - Tela de vitória
A tela de créditos (figura 21) é uma tela simples. Ela é composta das
informações de crédito e de um ícone que representa voltar a tela de título.
Figura 21 - Tela de créditos