Você está na página 1de 37

Daniel Ricardo dos Santos

Diogo de Campos
Mauricio Oliveira Haensch

Desenvolvendo Jogos com Java ME


Daniel Ricardo dos Santos
Diogo de Campos
Mauricio Oliveira Haensch

Desenvolvendo Jogos com Java ME

PET Computação

U NIVERSIDADE F EDERAL DE S ANTA C ATARINA


C ENTRO T ECNOL ÓGICO
D EPARTAMENTO DE I NFORM ÁTICA E E STAT ÍSTICA
P ROGRAMA DE E DUCAÇ ÃO T UTORIAL
Licença:
Atribuição-Uso Não-Comercial-Compartilhamento pela mesma Licença 2.5 Brasil

• Para cada novo uso ou distribuição, você deve deixar claro para outros os
termos da licença desta obra.
• Qualquer uma destas condições podem ser renunciadas, desde que Você obte-
nha permissão do autor.
• Nada nesta licença prejudica ou restringe os direitos morais dos autores.
Sumário

1 Introdução p. 5

1.1 A plataforma Java . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 5

1.2 Java Card . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 5

1.3 Java Micro Edition (ME) . . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 6

1.4 Java Standard Edition (SE) . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 6

1.5 Java Enterprise Edition (EE) . . . . . . . . . . . . . . . . . . . . . . . . . . p. 6

2 Java ME p. 7

2.1 O que é . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 7

2.1.1 Aplicações . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 7

2.1.2 Vantagens . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 7

2.2 Principais diferenças entre Java SE e Java ME . . . . . . . . . . . . . . . . . p. 8

2.3 Configurações e perfis . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 9

2.4 JCP e JSRs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 11

3 Ferramentas de desenvolvimento p. 12

3.1 Sun Wireless Toolkit . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 12

3.2 IDEs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 12

3.2.1 NetBeans . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 12

3.2.2 Eclipse . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 13

4 Bibliotecas Java ME p. 14

4.1 Java SE x Java ME . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 14


4.2 Bibliotecas disponı́veis . . . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 14

5 Desenvolvendo para Dispositivos Móveis p. 16

5.1 Uso de memória . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 16

5.2 Resolução . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 16

5.3 Configurações e perfis . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 17

6 Desenvolvendo Jogos p. 18

6.1 A classe MIDLet . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 18

6.2 Commands . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 19

6.3 Displays e Displayables . . . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 20

6.4 Canvas . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 20

6.5 Sprites e Tiles . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 21

6.5.1 Sprites . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 21

6.5.2 TiledLayer . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 22

6.6 Sons . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 23

7 Estudo de Caso - Liga Quatro p. 26

7.1 Introdução . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 26

7.2 Estrutura do código . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 26

7.2.1 Commands . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 26

7.2.2 Eventos do menu principal . . . . . . . . . . . . . . . . . . . . . . . p. 29

7.2.3 A classe TelaDoJogo . . . . . . . . . . . . . . . . . . . . . . . . . . p. 30

7.2.4 A classe Bola . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 33

7.2.5 PlayTone . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . p. 35

8 Bibliografia p. 36
5

1 Introdução

1.1 A plataforma Java

Java é uma tecnologia que se estende muito além da própria linguagem de programação.
Trata-se, na verdade, de uma plataforma de desenvolvimento.

A plataforma Java compreende um conjunto de softwares e especificações da Sun Microsys-


tems para o desenvolvimento de aplicativos. A grande vantagem desta plataforma é a sua com-
patibilidade não apenas entre sistemas operacionais, mas também entre diversas arquiteturas,
variando de PDAs e set-top boxes até servidores. As aplicações desenvolvidas nesta plataforma
também são diversas, variando de aplicativos embarcados a jogos e aplicativos web.

Devido a essa grande variedade a plataforma é dividida em quatro diferentes ”edições”.


Cada edição é composta de um pacote de softwares que permitem o desenvolvimento e a
execução de programas escritos na linguagem Java.

As plataformas são desenhadas não para um processador especı́fico, mas para uma Java Vir-
tual Machine (JVM) e um compilador com um conjunto de bibliotecas. A principal vantagem
do uso da máquina virtual é aumentar a portabilidade do código.

1.2 Java Card

Java Card é a edição mais limitada da plataforma Java e é destinada a smart cards ou dispo-
sitivos similares com pouquı́ssima memória. Seu principal foco é a segurança, com suporte ao
encapsulamento de dados no aplicativo, firewall entre aplicativos e criptografia.

Atualmente, encontra-se em desenvolvimento a versão 3 da plataforma.


6

1.3 Java Micro Edition (ME)

Voltada para dispositivos com pouca capacidade de processsamento e memória limitada,


principalmente dispositivos móveis. Muito usada em celulares e PDAs, estende-se também
para aparelhos como set-top boxes e Blu-ray players. Apresenta algumas dificuldades no de-
senvolvimento devido a grande variedade de dispositivos e suas especificidades.

A plataforma Java ME é detalhada no próximo capı́tulo.

1.4 Java Standard Edition (SE)

É a edição usada para o desenvolvimento de aplicações portáveis de propósito geral. Contém


o que necessário para o desenvolvimento de aplicações desktop e applets, como pacotes de in-
terface gráfica, entrada e saı́da, comunicação com bancos de dados, etc.

Atualmente, encontra-se na versão 6.

1.5 Java Enterprise Edition (EE)

Java EE é a maior das edições de Java, compreendendo tudo o que está presente na Standard
Edition além de coisas mais especı́ficas, como o servidor de aplicações GlassFish. Voltada
para aplicações que rodam no lado do servidor e pensada para tornar as aplições robustas,
seguras, escaláveis e portáveis. É usada para o desenvolvimento de aplicações web e baseadas
na arquitetura orientada a serviços (SOA), como web services.

Atualmente, encontra-se na versão 5.


7

2 Java ME

2.1 O que é

Java ME é uma das edições da plataforma Java e, como tal, é composta pela própria lingua-
gem, por uma Máquina Virtual e por uma coleção de APIs.

A linguagem é a mesma usada nas outras edições, portanto é familiar para qualquer pessoa
que já tenha programado Java em qualquer plataforma. Para se entender Java ME, então, é
necessário conhecer a Máquina Virtual e a coleção de APIs.

Java ME aceita diferentes implementações de máquinas virtuais e normalmente a máquina


virtual é definida pela Configuração.

Além desses elementos comuns, esta edição da plataforma Java apresenta alguns elementos
especı́ficos como Profiles e Configurações, que serão estudados adiante.

2.1.1 Aplicações

É usado principalmente para o desenvolvimento em sistemas móveis e embarcados, alguns


exemplos de sistemas que adotam Java ME são:

• Celulares;

• PDAs;

• Set-top boxes;

• Blu-ray players.

2.1.2 Vantagens

Por ter um campo de aplicação bem especificado, Java ME apresenta diversas facilidades
para os desenvolvedores, tais como:
8

Portabilidade: Um aplicativo criado utilizando Java ME pode ser portado para diversos apare-
lhos, desde que sigam as mesmas especificações. Isso é muito importante no desenvolvi-
mento para celulares, pois um mesmo aplicativo desenvolvido poderá rodar em modelos
de diferentes fabricantes.

Boa compatibilidade entre aparelhos: Como já foi citado acima, desde que sigam as mesmas
especificações, não há grandes problemas de compatibilidade entre os aparelhos que irão
rodar um aplicativo.

Constante desenvolvimento: Assim como todo o resto da tecnologia Java, Java ME se encon-
tra em constante desenvolvimento, com novas funções e capacidades sendo adicionadas
a todo momento. Esse processo de desenvolvimento segue a JCP (Java Community Pro-
cess), que será detalhado mais a frente.

Boa documentação: A plataforma conta com uma vasta documentação online, no próprio site
da Sun, além de inúmeros outros exemplos e tutoriais que podem ser encontrados em sites
e fóruns.

Desenvolimento integrado: Uma das principais vantagens de Java ME é o desenvolvimento


integrado, proporcionado por ferramentas como o Wireless Toolkit (WTK) e IDEs como
o NetBeans.

2.2 Principais diferenças entre Java SE e Java ME

As principais diferenças entre Java SE e Java ME são descritas a seguir:

Máquina Virtual: As máquinas virtuais utilizadas em Java ME são desenhadas para serem
mais compactas e eficientes que a máquina virtual utilizada em Java SE, por isso alguns
recursos são perdidos.

Números de ponto flutuante: O suporte a ponto flutuante só foi adicionado na versão 1.1 do
CLDC, por isso ao desenvolver aplicações para dispositivos mais antigos é possı́vel que
o desenvolvedor não encontre suporte para esta representação numérica.

Reflexão: O pacote java.lang.reflect só está presente no CDC. Por isso não é possı́vel utilizar
reflexão em dispositivos de menor capacidade.

APIs de alto nı́vel (Swing e AWT): Algumas APIs de mais alto nı́vel, como as utilizadas para
interface gráfica não estão presentes em Java ME.
9

Acesso ao sistema de arquivos: Para se ter acesso ao sistema de arquivos é necessário utilizar
o PDA Profile, ou uma implementação especı́fica de um fabricante.

Grupos de threads: O suporte a grupos de threads, threads que podem ser manipuladas em
conjunto, também só é provido pelo CDC.

A figura abaixo mostra uma visão geral dos componentes de Java ME e como eles se rela-
cionam as outras tecnologias Java:

2.3 Configurações e perfis

As configurações (Configurations) e perfis (Profiles) representam as configurações e APIs


mı́nimas disponı́veis em um dispositivo móvel.

Cada aplicativo desenvolvido necessita de uma configuração mı́nima para ser rodado, e a
garantia de que o dispositivo alvo terá essa configuração é fornecida pelo sistema de Configu-
rations e Profiles.

As principais Configurations são o CDC e o CLDC e o principal Profile é o MIDP, que são
explicados a seguir.

O MIDP é a definição de uma arquitetura, e define várias APIs que devem estar disponı́veis
em uma plataforma para promover um ambiente de desenvolvimento para as suas MIDlets. A
diferença mais importante entre o MIDP 1.0 e o MIDP 2.0 é a introdução de uma API para jogos,
10

que facilita muito a criação deste tipo de aplicativo. A maioria dos aplicativos desenvolvidos
para dispositivos móveis são capazes de rodar em plataformas MIDP 1.0, que devem ter as
seguintes caracterı́sticas, entre outras:

• Tamanho da tela mı́nimo de 96x54pixels.

• Input através de “One handed keyboard” ou “Two handed keyboard” ou Touch Screen.

• 128Kbytes de memória para os componentes MIDP.

• 8Kbytes de memória para dados das aplicações.

• 32Kbytes de memória dinâmica.

O MIDP 1.0 é baseado em aparelhos que seguem a configuração CLDC, ou seja, os apare-
lhos compatı́veis com MIDP também o serão com CLDC.

Dispositivos CLDC (Connected Limited Device Configuration) são os aparelhos mais sim-
ples, que possuem uma interface menor e pouca memória e velocidade de processamento. De-
vem ter no mı́nimo as seguintes caracterı́sticas:

• 128Kb de memória para rodar a máquina virtual.

• 32Kb de memória dinâmica.

• Comunicação em rede com baixa largura de banda.

• Baixa potência (para diminuir o gasto de energia).

Dispositivos CDC (Connected Device Configuration) são aparelhos mais potentes que os
CLDC e devem ter pelo menos as seguintes caracterı́sticas:

• 512Kb de memória para executar a máquina virtual.

• 256Kb de memória dinâmica.

• Capacidade para comunicação em rede com alta largura de banda.

• Suporte à persistência.
11

2.4 JCP e JSRs

O Java Community Process (JCP) é um processo formalizado para que várias partes inte-
ressadas colaborem no desenvolvimento de especificações e futuras versões da plataforma Java.

Entre os membros da JCP podemos encontrar operadoras de telefonia como a NTT Do-
CoMo, desenvolvedores de dispositivos móveis como a Nokia, a Sony Ericsson e a Motorola,
outras empresas e até mesmo pessoas fı́sicas.

Os documentos descrevendo as especificações e tecnologias propostas são chamados Java


Specification Request (JSR).

Esses documentos são publicamente revisados até que um JSR se torne final e seja votado
pelo Comitê Executivo da JCP.

Um JSR final provê uma implementação de referência, na forma de código fonte e um


Technology Compatibility Kit para verificar a especificação da API.

O próprio JCP é descrito por um JSR, o JSR 215 de 2006.

Os JSRs são especialmente importantes em Java ME, por causa do número de recursos
novos e da velocidade com que mudam em dispositivos móveis.

Alguns exemplos de JSRs em Java ME:

• JSR 30: J2ME CLDC 1.0

• JSR 37: MIDP 1.1

• JSR 68: J2ME Platform Specification

• JSR 82: Bluetooth

• JSR 118: MIDP 2.0

• JSR 133: Java Game Profile

• JSR 139: CLDC 1.1

• JSR 184: Mobile 3D Graphics API for J2ME


12

3 Ferramentas de desenvolvimento

3.1 Sun Wireless Toolkit

O Wireless Toolkit, ou WTK, é uma ferramenta de auxı́lio ao desenvolvimento de aplicações


em Java ME (CLDC e MIDP). O WTK possui exemplos, documentação, funções de otimização
e um emulador para dispositivos móveis.

Há algumas IDEs (Integrated Development Enviroment, ou ambiente de desenvolvimento)


que já vêm com o Sun Wireless ToolKit integrado, outras possuem plugins para integração e
também é possı́vel usar apenas o WTK para desenvolver suas aplicações.

3.2 IDEs

Duas IDEs já bem conhecidas para desenvolvimento em Java (e também em outras lingua-
gens de programação) são o Eclipse e o NetBeans. Ambos os ambientes suportam o desenvol-
vimento em Java ME e serão descritos com um pouco mais de caracterı́sticas a seguir.

3.2.1 NetBeans

O ambiente de desenvolvimento NetBeans se iniciou como um projeto de dois estudantes, e


posteriormente foi comprado pela Sun Microsystems. Por aproximadamente um ano, a empresa
manteve o ambiente como software proprietário, mas após esse perı́odo o código foi aberto e
a partir disso foram surgindo muitas melhoras no ambiente em si e diversos plugins foram
criados. Entre esses avanços, surgiu o NetBeans Mobility Pack, que é uma extensão da IDE
para desenvolvimento com Java ME.

O NetBeans Mobility Pack já vem integrado com o emulador da Sun (WTK), além de su-
portar outros emuladores. Essa extensão também tem algumas ferramentas que auxiliam no
desenvolvimento para dispositivos móveis, como algumas classes que podem ser utilizadas nas
13

suas aplicações fora as padrões da biblioteca Java ME. Um exemplo disso é a SplashScreen,
que estende a classe Canvas do Java ME e pode ser bem útil para as aplicações desenvolvidas.

O Mobility Pack também possui o Game Builder, que auxilia o desenvolvimento de jogos.
Através dessa ferramenta, fica mais fácil criar Sprites e Tiles para seu jogo, e o Game Builder
também permite a construção de cenas (combinações de objetos estáticos e dinâmicos) de uma
maneira mais simples e rápida, ajudando na criação do seu jogo.

Além de todas as vantagens do ambiente para o desenvolvimento de aplicações com Ja-


vaME, é um ambiente gratuito e com boa documentação, além de um forte apoio da comuni-
dade.

3.2.2 Eclipse

Existem plugins para a IDE Eclipse para desenvolvimento com Java ME. A grande van-
tagem de se desenvolver com esse ambiente são todas as facilidades do Eclipse, que é bem
poderoso e versátil para geração de software em Java.

Um dos plugins existentes para desenvolvimento com JavaME no ambiente Eclipse é o cha-
mado EclipseME. Este plugin é um pouco antigo e só funciona com algumas versões também
antigas da IDE. Porém, existem diversos outros plugins que podem ser utilizados para desen-
volvimento de aplicações para a plataforma ME do Java. Esses plugins podem ser encontrados
na própria página da IDE Eclipse, na seção de plugins, mas vários deles são pagos.
14

4 Bibliotecas Java ME

4.1 Java SE x Java ME

O Java Micro Edition possui apenas um subconjunto dos componentes da Standard Edition,
com uma máquina virtual e algumas APIs mais limitadas. As APIs presentes no Java ME são de-
finidas no JCP (Java Community Process). A limitação de memória dos dispositivos que rodam
Java ME acabou resultando na retirada de algumas das bibliotecas presentes no Java Standard
Edition, para manter a plataforma pequena e adequada ao seu objetivo. Porém, o Java Micro
Edition não possui apenas uma especificação. Dependendo da configuração e perfil escolhidos,
você pode utilizar diferentes APIs para desenvolver sua aplicação. Com a evolução do poder de
processamento e capacidade de memória dos dispositivos, surgem novas funcionalidades mais
complexas através dos JSRs. Como exemplo disso, podemos citar a ausência de variáveis do
tipo ponto flutuante (como o double) em versões mais antigas de configurações e perfis do Java
ME. Devido à essa diferença entre as duas edições, nem toda aplicação desenvolvida em Java
SE funcionará num dispositivo que roda Java ME.

4.2 Bibliotecas disponı́veis

Como dito anteriormente, as APIs disponı́veis para desenvolver a sua aplicação resultam
da combinação de configuração e perfil adotados pelo programador. Utilizando como exemplo
a versão 1.1 do CLDC (Connected Limited Device Configuration), os pacotes presentes para o
desenvolvimento da sua aplicação são:

• java.io

• java.lang

• java.lang.ref

• java.util
15

• javax.microedition.io

Outra diferença de bibliotecas entre a Standard Edition e a Micro Edition é em relação à


interface das aplicações. O SWT e Swing da edição padrão de Java não se encontram na versão
ME. A Micro Edition conta com um novo pacote para este fim, chamado java.microedition.lcdui,
que pode ser encontrado em qualquer versão do MIDP. A versão 2.0 do MIDP contém os se-
guintes pacotes:

• java.io

• java.lang

• java.util

• javax.microedition.io

• javax.microedition.lcdui

• javax.microedition.lcdui.game

• javax.microedition.media

• javax.microedition.media.control

• javax.microedition.midlet

• javax.microedition.pki

• javax.microedition.rms

Como podemos notar, existe uma série de pacotes exclusivos do JavaME (os iniciados com
javax.microedition). Assim, com a presença de alguns pacotes exclusivos, também pode-se
afirmar que nem toda aplicação escrita em Java ME funcionará na plataforma Java SE.
16

5 Desenvolvendo para Dispositivos


Móveis

5.1 Uso de memória

Os dispositivos para os quais se desenvolve em Java ME, em geral, possuem pouca memória,
sendo isso um fator de risco para o desenvolvimento de aplicações desse tipo, juntamente com
a capacidade de processamento. Para evitar isso, deve-se ter um cuidado maior para essa classe
de software.

A utilização de estruturas de dados e tipos de dados mais simples é um bom começo para
reduzir o uso de memória. Outra saı́da é reduzir o número de objetos criados, ou reduzir o
tamanho dos objetos que são utilizados na aplicação. As imagens utilizadas também são uma
grande fonte de Out of Memory Error, a exceção que ocorre ao se utilizar memória em excesso.

Se mesmo com esses cuidados ainda existem problemas com memória, você pode usar um
profiler para auxiliar a encontrar usos desnecessários ou ineficientes de memória. Um Profiler
é uma ferramenta que ajuda a encontrar bottlenecks da sua aplicação, achando memory leaks
e rastreando a vida dos objetos. Com essas informações, é mais fácil encontrar problemas na
implementação que ocasionam os erros de uso de memória da sua aplicação.

5.2 Resolução

Os diferentes dispositivos que suportam aplicações em Java ME possuem resoluções de tela


diferentes. Assim, na hora de desenvolver esse tipo de software, é preciso ter em mente alguma
resolução um pouco mais especı́fica para a qual será desenvolvida a aplicação. É muito mais
fácil desenvolver para uma resolução especı́fica do que tentar fazer um software mais geral que
rode em várias resoluções diferentes.

Normalmente são lançadas algumas versões do software, cada uma para uma resolução di-
17

ferente. Isso também ajuda na questão de capacidade do dispositivo. Geralmente dispositivos


com uma mesma resolução da tela possuem uma capacidade de processamento e memória pa-
recidos, facilitando com que uma aplicação funcione em uma quantidade maior de aparelhos
móveis ao ser desenvolvida especificamente para um conjunto de dispositivos similares.

5.3 Configurações e perfis

A escolha da configuração e sua versão, assim como a versão do perfil adotado pelo pro-
gramador, deverá levar em conta o dispositivo para o qual se está desenvolvendo a aplicação,
assim como as funcionalidades da aplicação sendo desenvolvida. Essa escolha acaba definindo
em que tipos de dispositivos a sua aplicação irá rodar. Por esse motivo, é fundamental que se
verifique as configurações e perfis suportados pelo dispositivo de destino da sua aplicação.
18

6 Desenvolvendo Jogos

6.1 A classe MIDLet

A classe MIDlet corresponde às aplicações do MIDP, e deve ser estendida para que o
software que gerencia as aplicações do dispositivo consiga controlar e modificar o estado das
aplicações (rodando ou pausada, por exemplo). A classe principal de sua aplicações deve es-
tender a esta classe e implementar os métodos utilizados para criar, começar, pausar e destruir
a aplicação. A MIDLet funciona como uma interface entre o gerenciador de aplicações e a sua
aplicação. Essa comunicação funciona através de sinais enviados entre o software gerenciador
do dispositivo e a aplicação.

Para começar a sua aplicação, a classe MIDlet possui o método startApp, que faz com que
o estado atual se torne Active, enviando um sinal para o software gerenciador de aplicações.
Caso alguma exceção ocorra durante a execução desse método, a aplicação é destruı́da auto-
maticamente e o método destroyApp é chamado. A aplicação também pode ser pausada com o
pauseApp, entrando no estado Paused. Nesse estado, o MIDlet deve liberar todos os recursos
alocados.

Além dos métodos que enviam um sinal ao MIDlet para a mudança de estado, há também
métodos da classe que enviam um sinal ao gerenciador de aplicações para informar da mudança
de estado da sua aplicação, como o notifyDestroyed e notifyPaused. Ao utilizar o notifyDes-
troyed, por exemplo, o software gerenciador não irá chamar o destroyApp, mas o próprio MI-
Dlet deve ter realizado as mesmas operações que o método de destruição, como a liberação de
recursos.

Através da combinação desses métodos da classe MIDlet monta-se o ciclo de vida de sua
aplicação. É importante manter correta a comunicação entre a aplicação e o software gerenci-
ador de aplicações, estando sempre atento à questão de liberação de recursos e troca de sinais
para mudança de estado.
19

6.2 Commands

Os commands do Java ME são usados para gerar e tratar eventos. Por exemplo, um comando
pode ser associado a um botão, ou imagem, de modo que, quando este for selecionado, ou
clicado, o evento será disparado.

Um command guarda a informação semântica de uma ação, como pode ser visto pelos tipos
de comandos existentes, mostrados mais abaixo. Desse modo, ele é responsável apenas pelo sig-
nificado, e não à ação propriamente dita. A ação fica como responsabilidade da implementação
de um CommandListener associado à tela em o Command se encontra.

Um comando deve ser criado da seguinte maneira:

Command(String shortLabel, String longLabel, int commandType, int priority)

Onde shortLabel é o nome abreviado do comando, longLabel é o nome completo do co-


mando e commandType é o tipo do comando. Há também um outro construtor para objetos da
classe Command, com a ausência do parâmetro longLabel.

Os tipos possı́veis do commandType são os seguintes:

• OK

• ITEM

• SCREEN

• HELP

• BACK

• CANCEL

• EXIT

• STOP

A prioridade de um comando só é importante se algum objeto disparar mais de um evento


de uma vez, neste caso, somente o comando de maior prioridade é avaliado.

Estes comandos devem então ser tratados por uma classe que implementa a interface Com-
mandListener. Nesta classe será definido o método commandAction que vai decidir que ação
deve ser tomada quando cada evento é disparado.
20

Exemplos de implementações de commands e CommandListeners podem ser encontrados


no final desta apostila, na seção Estudo de Caso - Liga Quatro ou neste site: http://pt.
wikibooks.org/wiki/J2ME/Li%C3%A7%C3%B5es/CommandListener

6.3 Displays e Displayables

O Display é único por MIDlet e é o responsável por funções relacionadas à tela do dispo-
sitivo. Para obter uma referência ao Display, a aplicação deve utilizar o método getDisplay.
Através dessa classe, é possı́vel chamar métodos para obter algumas informações importantes
para a sua aplicação, como o número de cores do aparelho, ou até mesmo se ele suporta cores.
A classe Display também possui métodos para controlar funções do dispositivo, como vibrar,
através do método vibrate, e um efeito de luz do aparelho, com o método flashBackLight.

Um objeto só possui a capacidade de ser inserido no Display ao estender a classe Dis-
playable. Porém, um Displayable apenas tem capacidade para ter comandos, listeners, tı́tulos
e tickers associados a ele. Tudo que é mostrado e a sua interação com o usuário é definida
através de subclasse, como é o exemplo do Canvas. A utilização do Displayable é feita com
os métodos setCurrent e getCurrent da classe Display, que troca e retorna o objeto do tipo
Displayable atualmente na tela, respectivamente.

Caso não seja definido nas subclasses do Displayable, a configuração inicial do seu objeto
será:

• Não visı́vel no Display;

• Tı́tulo null;

• Nenhum ticker associado ao Displayable em questão;

• Nenhum command existente;

• Nenhum listener para comandos.

6.4 Canvas

A classe Canvas é uma subclasse de Displayable, e é utilizada para objetos da sua aplicação
que precisam fazer alterações gráficas na tela, ou receber eventos de teclas pressionadas no
dispositivo. Em geral, os jogos utilizam muito essa classe, para poder interagir com o usuário
tanto na recepção de estı́mulos quanto na apresentação dos resultados desses estı́mulos.
21

Esta classe é responsável por tratar eventos de entrada, como teclas pressionadas, soltas
ou mantidas pressionadas (keyPressed, keyReleased e keyRepeated, respectivamente) e também
eventos com um pointer de um PDA (similares aos de tecla). É possı́vel verificar qual das
teclas foi pressionadas com o método getKeyCode, mas em Java ME existe um artifı́cio que é
recomendado para os desenvolvedores de jogos, que é o sistema de Game Actions.

Os Game Actions são definidos para aplicações portáveis que utilizam as teclas de setas de
um dispositivo e eventos relacionados a jogos. O MIDP provê os seguintes Game Actions: UP,
DOWN, LEFT, RIGHT, FIRE, GAME A, GAME B, GAME C e GAME D. Cada Game Action
pode estar a associado a mais de um Key Code, porém cada tecla só pode estar associada à um
Game Action. A aplicação pode traduzir um Key Code para um Game Action através do método
getGameAction e fazer o oposto através de getKeyCode. Apesar de vários Key Codes poderem
estar associados ao mesmo Game Action, apenas um deles é retornado com este método.

Outra função do Canvas é o controle gráfico do que é exibido. Isso é feito através do
setFullScreenMode, que tanto coloca quanto tira do modo de exibição em tela cheia, e o método
repaint, que renderiza o Canvas por completo ou apenas uma região dele, artı́ficio muito útil
para reduzir o tempo gasto com processamento para repintar uma área sem alterações.

6.5 Sprites e Tiles

Entre as bibliotecas do Java ME, existem as classes Sprite e TiledLayer, que são utiliza-
das para fazer os objetos animados e o fundo do seu jogo, respectivamente. Um Sprite, por
sua definição, é uma imagem ou animação em duas ou três dimensões que será utilizada para
representar um objeto, e muitas vezes são usados para detectar colisões ou fazer algum tipo
de interação do jogo. Já os Tiles são usados para representar o fundo da tela, usualmente uma
paisagem, um tabuleiro ou alguma cena de um jogo.

6.5.1 Sprites

Para a classe Sprite, um arquivo de imagem é dividido em vários frames de tamanho igual,
e você pode criar livremente uma sequência dos frames para gerar sua animação. Para animar
sua sequência, são usados métodos como o nextFrame, setFrame e prevFrame. Com esta classe
do Java ME também é possı́vel definir a área de colisão de um sprite, assim como verificar se
houve colisão entre dois sprites, entre um sprite e um Tile ou até mesmo entre um sprite e uma
imagem, utilizando métodos como defineCollisionRectangle e collidesWith.
22

Os Sprites em Java ME são produzidos a partir de uma única imagem com todos os frames
que serão utilizados para renderizar a animação. É possı́vel fazer apenas um frame, mas nos
demais casos a imagem fonte é dividida igualmente em frames de uma determinada largura e
altura. A sequência da animação é criada através de uma Frame Sequence, que nada mais é do
que um array com os ı́ndices dos frames gerados.

Um objeto do tipo Sprite pode ter um pixel de referência. Esse pixel é definido por padrão
como a posição (0,0) do sprite, podendo ser redefinida para qualquer outra posição, até mesmo
fora dos limites do sprite. Esse pixel existe para permitir algumas operações que são realiza-
das baseadas em algum referencial, como transformações de rotação e espelhamento do sprite.
Segue abaixo um exemplo de sprite com um pixel de referência influenciando na rotação.

Figura 6.1: Imagens retiradas de: http://java.sun.com/javame/reference/apis/jsr118/javax/


microedition/lcdui/game/Sprite.html

6.5.2 TiledLayer

Uma TiledLayer se trata de uma imagem composta de uma grade de células, onde cada
célula é preenchida com uma imagem estática chamada tile. Essa composição de imagens
menores permite economia de espaço, e é uma técnica muito usada em diversos jogos de duas
dimensões.

Cada posição na grade da TiledLayer recebe um indı́ce, ao qual é associado um tile. Cada
um desses tiles é considerado estático e fica ligado à imagem que ele representa. Existe também
um animated tile, que é um tile virtual associado dinamicamente à um estático. Com os tiles
animados, ao se utilizar o mesmo animated tile em diversas células da grade, é possı́vel mudá-
las ao mesmo tempo apenas modificando o tile estático ao qual eles estão associados.

A grade da TiledLayer possui um número de células de mesmo tamanho distribuı́das por


23

linhas e colunas, definidas na criação do objeto. As células da grade não podem receber mais
de um tile por vez, mas várias células podem receber o mesmo tile. Para mudar o conteúdo de
uma das células, podem ser utilizados os métodos setCell e fillCells.

6.6 Sons

O JavaME possui uma API responsável por funcionalidades multimı́dia, como gravação de
áudio e vı́deo, além dos controle de sons. A Mobile Media API, ou MMAPI, funciona basica-
mente através das classes Manager, Player e os controladores VolumeControl e ToneControl.

A classe Manager é utilizada para criar os Players. Isso pode ser feito através do método
createPlayer, onde você passa como parâmetros um InputStream do arquivo de som que será
tocado e o tipo (wave, midi, mp3, etc). Assim, você obtém um Player para tocar o som/música
desejada e deverá controlar esse novo objeto para obter o efeito desejado. Alguns exemplos de
formatos e as Strings que devem ser passados como parâmetro para o createPlayer:

Wave - audio/x-wav

AU - audio/basic

MP3 - audio/mpeg

Midi - audio/midi

Tone - audio/x-tone-seq

O Player que foi obtido pode ser utilizado de uma forma simples, apenas chamando o
método start para que ele comece e pare ao chegar ao fim da mı́dia especificada.

InputStream stream = getClass().getResourceAsStream(sound_file);


Player p = Manager.createPlayer(stream, format);
p.start();

O Player permite uma maior variedade de ações, com a combinação dos métodos da classe
e o seu sistema de estados. Os estados da classe Player são: Unrealized, Realized, Started,
Closed e Prefetched. A imagem abaixo mostra como são feitas as transições entre os estados
através dos métodos mostrados nas flechas presentes na figura.
24

O estado Unrealized indica que o Player não possui nem informações nem recursos ne-
cessários para funcionar. Realized indica que apenas os recursos não foram adquiridos ainda. O
estado Prefetched significa que todos os recursos já foram adquiridos. Já o estado Closed mostra
que o Player está fechado e, por fim, Started é o estado de um Player que já foi iniciado.

Além dos estados e dos métodos para se trocar entre eles, a classe Player tem também
métodos para descobrir a duração da mı́dia a ela atribuı́da, fixar um número de repetições para
a mı́dia e descobrir o estado atual, entre outros (getDuration, setLoopCount e getState, respec-
tivamente).

A Mobile Media API também possui o Control, cujos objetos podem ser utilizados para
controlar algumas funções das mı́dias. Por exemplo, o Player suporta um VolumeControl, que
pode ser utilizado para controlar o volume do aúdio desse Player. Existe também o ToneControl,
que é responsável por manipular sequências de tons monotônicos, uma outra forma de produzir
sons para a sua aplicação.

A classe Manager ainda possui o método playTone, que é uma maneira bem mais simples de
se colocar sons no seu jogo. Este método recebe como parâmetros a nota, a duração e o volume,
e produz um tom monotônico como resultado. Este tipo de som pode ser útil em diversos tipos
de jogos ou aplicações, que não necessitam de algo mais complexo do que pequenos tons para
os efeitos sonoros. Há apenas o risco de esse tipo de solução ocupar muitos recursos da CPU,
25

que acontece apenas em casos de dispositivos que não possuem suporte para geração de tons.
26

7 Estudo de Caso - Liga Quatro

7.1 Introdução

Nesta seção vamos falar um pouco sobre o jogo que nós escolhemos para implementar na
plataforma Java ME, o Liga Quatro.

O jogo, também conhecido em inglês como Four in a Row, Four in a Line, ou pelo nome
registrado Connect 4, é um jogo simples de tabuleiro para dois jogadores, que jogam em turnos
alternados colocando suas fichas em colunas com o objetivo de ser o primeiro a conectar quatro
fichas, seja na vertical, na horizontal ou em qualquer diagonal. Cada jogador escolhe uma
coluna por turno e sua ficha é depositada no primeiro espaço livre daquela coluna.

Este jogo foi escolhido por dois motivos principais: a possibilidade de se comparar a
implementação do jogo em Java ME e Java SE, ampliando o aprendizado da tecnologia em
estudo, e a interface simples.

A possibilidade de comparação de implementações se deu porque os bolsistas envolvidos


já estavam desenvolvendo o jogo em Java SE para a disciplina de Estruturas de Dados. Essa
comparação permitiu uma análise detalhada das diferença entre as duas tecnologias e as dificul-
dades de se portar o código para uma plataforma móvel.

Uma das maiores dificuldades foi modificar alguns trechos para se adaptar à baixa capaci-
dade de processamento e de memória dos dispositivos móveis.

7.2 Estrutura do código

7.2.1 Commands

Aqui estão declarados os atributos da classe MidletL4 do tipo Command. O construtor do


Command recebe como primeiro parâmetro o texto que será mostrado ao usuário para represen-
tar o comando. Outro parâmetro é o tipo de comando, que pertence a um Enum (BACK, EXIT,
27

HELP, OK, CANCEL, SCREEN, STOP, ITEM). O terceiro parâmetro corresponde à prioridade
do comando com relação aos outros comandos da mesma tela, sendo o menor número a maior
prioridade. Neste caso, como todos têm a mesma prioridade, os comandos irão aparecer na
ordem em que são adicionados. Com prioridades diferentes, os comandos ficam listados por
ordem de maior prioridade para menor, para facilitar o acesso do usuário aos comandos mais
importantes.

private Command botaoIrParaMenu = new Command("Voltar", Command.BACK, 0);


private Command botaoOK = new Command("OK", Command.OK, 0);
private Command botaoSairDoJogo = new Command("Sair", Command.EXIT, 0);

A nossa classe MidletL4 implementa a interface CommandListener e precisamos implemen-


tar o método commandAction. Assim, ele recebe um comando e a tela (Displayable) em que
o comando foi executado. No código demonstrado, fazemos comparações para saber em qual
tela o jogo se encontra atualmente. Há algumas telas em que só existe uma ação possı́vel, como
por exemplo as telas de Splash. Desse modo, não é preciso saber qual comando foi executado,
já que obrigatoriamente deveremos passar para a tela seguinte.

Sempre que o comando botaoSairDoJogo for executado, a ação é fechar o aplicativo, não
importando em qual tela o jogo se encontra atualmente.

Já com o comando botaoIrParaMenu, podemos notar que existe uma condição que verifica
se o usuário está no meio do jogo. Caso isso seja verdade, o jogo fica pausado e a tela de menu
recebe um novo botão ”Continuar Jogo”.

/**
* Recebe os eventos que ocorreram, e a tela em qual ocorreu e
* realiza a aç~
ao correspondente.
*
* @param comando
* O evento recebido.
* @param tela
* A tela na qual o evento ocorreu.
28

*/
public void commandAction(Command comando, Displayable tela) {

if (tela == telaSplashL4) {
trocarTela(getTelaSplashPET());
} else if (tela == telaSplashPET) {
trocarTela(getTelaMenu());
}

if (tela == telaOpcoes) {
setarOpcoes();
trocarTela(getTelaMenu());
} else if (comando == botaoSairDoJogo) {
fecharMIDlet();
} else if (tela == telaVitoria || tela == telaDerrota
|| tela == telaEmpate) {
trocarTela(telaMenu);
} else if (comando == botaoIrParaMenu) {
if (Controlador.getInstance().jogoNaoAcabou()
&& tela == telaJogo) {
pausado = true;
telaMenu.insert(0, getBotaoDeContinuarJogo());
}
trocarTela(telaMenu);

}
}

As trocas de tela são feitas utilizando o método abaixo:

/**
* Troca a tela atualmente exibida no celular.
*
* @param proximaTela
29

* Tela a ser exibida.


*/
public void trocarTela(Displayable proximaTela) {
display = getDisplay();
display.setCurrent(proximaTela);
}

7.2.2 Eventos do menu principal

Os comandos executados na tela principal do jogo são tratados de forma um pouco diferente.
Os comandos foram associados à botões da telaMenu. Assim, toda vez que um botão é pressi-
onado, emitindo um evento, o comando é disparado e tratado por um ItemCommandListener.

/**
* Cria um Listener para comandos nos itens do menu principal do jogo.
*/
private ItemCommandListener listenerDeItens = new ItemCommandListener() {

public void commandAction(Command comando, Item item) {


if (item == botaoDeNovoJogo) {
Controlador.getInstance().resetar();
comecarNovoJogo();

if (pausado == true) {
pausado = false;
telaMenu.delete(0); //Remove a opcao de continuar o jogo
}

} else if (item == botaoDeContinuarJogo) {


trocarTela(telaJogo);
pausado = false;
telaMenu.delete(0);

} else if (item == botaoDeOpcoes) {


30

trocarTela(getTelaOpcoes());

} else if (item == botaoDeAjuda) {


trocarTela(getTelaAjuda());

} else if (item == botaoDeSobre) {


trocarTela(getTelaSobre());

} else if (item == botaoDeSair) {


fecharMIDlet();
}
}
};

Como cada Item (botão) possui somente um comando, o método só precisa avaliar qual foi
o item pressionado.

Abaixo, temos um exemplo de associação de um comando a um botão. Em seguida, associ-


amos ao botão o ItemCommandListener mostrado no código acima.

botaoDeNovoJogo.addCommand(botaoOK);
botaoDeNovoJogo.setItemCommandListener(listenerDeItens);

7.2.3 A classe TelaDoJogo

A classe TelaDoJogo estende GameCanvas e é responsável pela tela principal do jogo,


incluindo detecção das teclas pressionadas, atualização do que é mostrado na tela e efeitos
sonoros.

O método abaixo é responsável pela atualização da tela. Os parâmetros recebidos servem


para delimitar a região que será redesenhada na tela, para questões de minimização de esforço
em certos momentos da execução do programa. O método flushGraphics chamado é o que
atualiza na tela aquilo que estava no buffer dentro de área desejada.
31

/**
* Atualiza uma parte da tela do jogo.
* @param x
* Posiç~
ao horizontal em pixels onde começa a área a ser atualizada.
* @param y
* Posiç~
ao vertical em pixels onde começa a área a ser atualizada.
* @param largura
* Largura em pixels da área a ser atualizada.
* @param altura
* Altura em pixels da área a ser atualizada.
*/
public void refresh(int x, int y, int largura, int altura) {
layer.paint(g, 0, 0);
flushGraphics(x, y, largura, altura);
}

Para a detecção de estı́mulos do usuário, o método keyPressed é definido recebendo o


código da tecla pressionada como parâmetro. O que fazemos é detectar se o que foi passado
corresponde às teclas 4, 6 ou 5, que movimentam a ficha do jogador para esquerda, para a direita
ou confirma a jogada, respectivamente. Há outro trecho condicional que verifica se o código da
tecla pressionada corresponde à algum gameAction.

O gameAction é um mapeamento das teclas especiais de movimentação um aparelho celular


especı́fico para jogos. Assim, com os dois tipos de verificação de teclas apresentados abaixo, é
possı́vel utilizar tanto as teclas numéricas quanto as direcionais para se jogar.

/**
* Detecta as teclas que s~
ao pressionadas durante o jogo.
* @param keyCode
* Código da tecla pressionada.
*/
public void keyPressed(int keyCode) {
switch (keyCode) {
case KEY_NUM4:
32

controle.clicouParaEsquerda();
return;
case KEY_NUM6:
controle.clicouParaDireita();
return;
case KEY_NUM5:
controle.clicouPraBaixo();
controle.animando = true;
return;
}

switch (getGameAction(keyCode)) {
case LEFT:
controle.clicouParaEsquerda();
return;
case RIGHT:
controle.clicouParaDireita();
return;
case DOWN:
controle.clicouPraBaixo();
controle.animando = true;
return;
case FIRE:
controle.clicouPraBaixo();
controle.animando = true;
return;
}
}

Para fazer a tela do jogo, são utilizadas várias camadas. Cada camada adicionada é posici-
onada num array, e a camada mais próxima do usuário é a situada na primeira posição, e a da
última posição é a que fica ao fundo de todas. Desse modo, o método append insere a camada
adicionada no final do array e portanto mais ao fundo. Demonstrando o sistema de camadas
da classe GameCanvas, no método abaixo adicionamos primeiramente a grade transparente do
33

tabuleiro, que é na verdade a camada que fica acima de todas na tela, em seguida as duas bolas
usadas para a animação das jogadas e por último o fundo com o tabuleiro totalmente pintado.
Assim, as bolas ficam na frente do fundo e por trás das grades, o que cria o efeito das bolas
deslizando para dentro do tabuleiro.

/**
* Junta as camadas com imagens do fundo, sprites e grade
* transparente do tabuleiro da tela do jogo.
*/
private void juntarCamadasDeImagens() {
layer.append(tabuleiro.getTabuleiroTransparente());
layer.append(bolaDeEscolha.getSprite());
layer.append(bolaAuxiliarDeAnimacao.getSprite());
layer.append(tabuleiro.getFundoDoTabuleiro());
flushGraphics();
}

7.2.4 A classe Bola

Os objetos da classe Bola possuem os sprites das fichas utilizadas no jogo, assim como
métodos para operar sobre esses sprites.

Este campo é o próprio sprite, que vai ser utilizado nos métodos da classe.

private Sprite bola;

Segue abaixo a sequência que representa a ordem dos frames do sprite e serve para definir
a animação da bola. Os números dos frames são definidos durante a criação do sprite. Neste
caso, ao executar a sequência (com o método bola.nextFrame()), a bola parece estar caindo.

private int[] sequenciaDeSaida = {0, 1, 2, 4};


bola.setFrameSequence(sequenciaDeSaida);
34

Para criar o sprite, passamos como parâmetro a imagem contendo os frames desejados, e
também a dimensão do sprite (neste caso, a altura e a largura são iguais).

bola = new Sprite(imagemDaAmarela, diametro, diametro);

Abaixo temos o método responsável por mover a bola para a direita. O método inicia to-
mando a posição atual da bola. A seguir, o sprite é atualizado para o último frame da sequência
do sprite, que representa uma imagem vazia. Essa imagem é pintada na tela com o método
refresh da classe TelaDoJogo. Isso faz com que a bola suma na tela do usuário.

Logo após, o sprite é movido uma casa para a direita e o sprite é atualizado para o primeiro
frame da sequência, que contém a bola cheia. Essa bola é pintada novamente na tela com o
mesmo método, fazendo com que ela reapareça.

Por questões de otimização, o método refresh() pinta apenas a área em que a bola está se
movimentando, para não pintar desnecessariamente as regiões em que não há modificação na
tela.

/**
* Move a bola para uma casa para a direita.
*/
public void moverParaDireita() {
int x = bola.getX();
int y = bola.getY();

bola.setFrame(3);
TelaDoJogo.getInstance().refresh(x, y, diametro, diametro);

bola.move(diametro, 0);

bola.setFrame(0);
TelaDoJogo.getInstance().refresh(x + diametro, y, diametro,
diametro);
}

Este método simples move o sprite x pixels no eixo x e y no eixo y.


35

public void mover(int x, int y) {


bola.move(x, y);
}

7.2.5 PlayTone

Para a utilização de sons no jogo, optamos pelo uso do método playTone. Este método
pertence à classe Manager do Java ME e é responsável por produzir um tom, dado sua duração
e a nota. Os sons presentes no Liga Quatro são produzidos após cada jogada e ao mostrar a tela
de vitória, derrota ou empate. Um exemplo pode ser conferido abaixo:

/**
* Toca o efeito sonoro da bola amarela.
*/
public void tocarEfeitoBolaAmarela() {
try {
Manager.playTone(79, 100, 100);
Thread.sleep(100);
Manager.playTone(77, 100, 100);

} catch (InterruptedException ex) {


ex.printStackTrace();
} catch (MediaException ex) {
ex.printStackTrace();
}
}

O método playTone recebe como primeiro parâmetro a nota, que deve ser um número entre
0 e 127 e é calculada através de uma fórmula encontrada em http://java.sun.com/javame/
reference/apis/jsr118/. Como segundo parâmetro, é passada a duração da nota em milis-
segundos. O terceiro parâmetro do método corresponde ao volume do tom, que deve ser entre 0
e 100. Assim, produzimos dois tons para o efeito sonoro da bola amarela, com uma pausa entre
eles. O Thread.sleep é utilizado para que os tons produzidos não se sobreponham durante sua
execução.
36

8 Bibliografia

CLDC 1.1 http://java.sun.com/javame/reference/apis/jsr139/

MIPD 2.0 http://java.sun.com/javame/reference/apis/jsr118/

IDE Netbeans http://www.netbeans.org/

Sun http://www.sun.com/

Wikipedia http://en.wikipedia.org/wiki/Java_Community_Process

Asked Questions List http://bellsouthpwp.net/m/c/mcpierce/javamefaq.html

Você também pode gostar