Bola basket

Escrevendo jogos para dispositivos celulares

Traduzido do artigo original Writing Cool Games for Cellular Devices. By Kobi Krasnoff 04/25/2006 
( http://today.java.net/pub/a/today/2006/04/25/writing-cool-games-for-mobile-devices.html ).

            Um dos principais usos de JME é programação do jogo.  Neste artigo, será explicado este por um jogo autônomo desenvolvido.  Esta aplicação é um jogo de basketball simples, em que o jogador joga de encontro a um oponente do computador.  Cada jogo dura exatamente dois minutos.  O alvo do jogo é disparar em tantas cestas quanto pode e impedir que o oponente do computador faça o mesmo. 


Conteúdo

Principais Funções
Estrutura do jogo
A classe
Midlet
Tela Inicial
A classe MenuPrincipal
A classe TelaPrincipal
      Regras do jogo
      Elementos da tela principal
      Segurando a tecla pressionada
      Algoritmo do jogo
      Tratando a
interrupção de chamada 
      Salvando os dados persistentes
Outras telas
Conclusão
Recursos


Principais Funções
        Um jogo típico no ambiente de JME pode consistir no seguinte:
  • Uma tela bem-vindo que o redirija automaticamente ao menu principal. 
  • O Menu principal.
  • A tela principal. Este é o lugar onde o jogo é jogado realmente. 
  • Menu de Níveis (ajustar a dificuldade do jogo, etc.). 
  • Tela de Instrução.
  • Sobre a tela.
A tela principal e a tela bem-vindo são executadas pelas classes que são estendidas de Canvas.  Esta classe é definida como sendo API de baixo nível (low-level), significando que permite o controle cheio da exposição no nível do pixel.  Todas as telas restantes são executadas pelas classes de API de alto nível (high-level) que usam controles padrão JME.

            Quando programamos aplicativos para JME, devemos também considerar como seguros os eventos do sistema tais como interrupções da chamada.  Quando tratamos deste evento, podemos estar congelando o estado atual do jogo ou mesmo podemos salvar este estado do jogo quando saímos da aplicação.

        De início, teremos que baixar do JME wireless toolkit.  Este é o SDK o mais elementar para este ambiente.  O objetivo principal deste toolkit é compilar os arquivos fontes de Java e disponibilizar dois arquivos:  um arquivo JAR que encapsule todos os arquivos de classe e um arquivo JAD (Java Application Descriptor).  Um arquivo JAD fornece dados sobre a aplicação tal como o vendedor, o tamanho do JAR, o número da versão, etc..  Este SDK pode ser integrado com o IDEs mais avançados tais como o Eclipse ou o NetBeans.  Poderá aprender mais sobre como começar com o wireless toolkit no artigo "Wireless Development Tutorial Part I."

Estrutura do jogo

A figura 1 mostra a classe da estrutura do jogo.


Figura 1

Figura 1. Estrutura geral do jogo

        O jogo começa com uma tela de abertura, e após cinco segundos redireciona a uma tela de menu.  Da tela de menu, o jogador tem a opção para começar o jogo, para ajustar o nível, para indicar "sobre" a tela, ou para mostrar as instruções.  Cada tela é gerenciada por sua própria classe.  Por exemplo, a tela bem-vindo é controlada pela classe da Tela Inicial, a tela principal do jogo é controlada pela TelaPrincipal, e assim por diante.

A classe Midlet

        Uma classe muito importante que não é mostrada no diagrama acima é a classe de TestMidletMIDlet.  Esta classe estende da classe Midlet e seu trabalho deve inicializar todos as outras classes e controles.  O método principal da classe de Midlet é o método startApp().  Normalmente, quando um midlet é chamado, está inicialmente em um estado pausado.  Se tudo funcionar normalmente e nenhuma exceção ocorrer, a seguir o midlet incorpora o estado ativo.  Este é chamado do método startApp().  Aqui está um exemplo:

 
public void startApp() {
// some code here...
display.setCurrent(splash);
}

        Para este estágio, o mais importante é a chamada do método display.setCurrent(splash).  Esta chamada ajusta os midlet's Displayable;  ou seja, determina qual de nossas telas deve ser mostrada no dispositivo.  Neste exemplo será a Tela Inicial, conforme a Figura 2.

Figura 2

Figura 2. Tela Inicial

Tela Inicial

        A Tela Inicial é a primeira tela que aparece quando a aplicação acima começa.  Estende a classe Canvas.  A classe Canvas, como API de baixo nível (low-level)assim chamada, permite o controle completo da exposição no nível de pixel.  Na ordem de pintar alguma coisa, temos que ignorar o método paint().  Por exemplo:

public void paint(Graphics g) {
// sets background color
g.setColor(255, 255, 255);
// fill the screen with the color
//defined previously
g.fillRect(0, 0, this.getWidth(),
this.getHeight());
// draw some image
g.drawImage(screenshot,
(this.getWidth() - 128) / 2,
(this.getHeight() - 128) / 2,
Graphics.TOP|Graphics.LEFT);
}

        Depois que um período de cinco segundos ou pressionando alguma tecla, nosso jogo altera para mostrar a tela MenuPrincipal.  Na ordem segurando a tecla pressionada temos que implementar a interface CommandListener.  Todos os eventos de teclas pressionadas serão tratados pela função commandAction().

public void commandAction(Command command,
Displayable displayable) {
// stops timer operation
timer.cancel();
/*
switch the display to menu. parent
is the Midlet object which actually does the
job.
*/
parent.setCurrent("MainMenu2");
}

Timer and TimerTask são duas classes que permitem que executemos uma função após um período do tempo pré-definido.  Usamos estas classes para comutar automaticamente a exposição da próxima tela.

public void startTimer() {
// TimerTask defines the task or subroutine
// to be executed.
TimerTask gotoMenu = new TimerTask() {
public void run() {
timer.cancel();
parent.setCurrent("MainMenu2");
}
};
// Timer class creates and manages threads on
// which the tasks are executed
timer = new Timer();
timer.schedule(gotoMenu, 5000);
}


A classe MenuPrincipal

A classe MenuPrincipal, mostrada na Figura 3, mostra o menu principal do jogo.
Figura 3
Figura 3. Tela do menu principal

O MenuPrincipal tem cinco opções:
  •  Continuar: Continua um jogo que esteja parado previamente.     
  •  Começar um jogo: Começa um jogo novo.     
  •  Escolha do nível: Escolhe a velocidade do jogador do computador.     
  •  Instruções: Mostra a tela de instruções.     
  •  A respeito do jogo: Mostra uma tela sobre o jogo.
Em JME, a classe List é responsável por mostrar listas ou menus, porque MenuPrincipal é subclasses dela. Como na classe TelaInicial, temos que executar a interface CommandListener a fim de segurar a tecla pressionada.


A classe TelaPrincipal

            A TelaPrincipal, mostrada na Figura 4, é a classe que mostra o jogo de basketball que está sendo jogado. A classe TelaPrincipal é estendida da classe Canvas.

Figura 4
Figura 4. Tela principal do jogo


Regras do jogo

                        Como mencionado antes, este é um jogo autônomo em que o jogador joga de encontro ao computador. Existem dois jogadores na tela. O jogador azul é controlado pelo humano, que tenta pôr a esfera na cesta sobre a direita. O jogador vermelho é controlado pelo computador. Cada jogo dura dois minutos e o jogador que dispara a maioria das cestas ganha. O jogador humano controla o jogador vermelho pelas teclas de seta ou pelas teclas de numeros: 2 para cima, 8 para baixo, 6 para a direita, 4 para a esquerda e 5 arremessar a bola.


  Elementos da tela principal


                       Diversos elementos gráficos compõem esta tela. Estes incluem o fundo, os jogadores, a esfera, etc. Cada elemento deve ser um arquivo gráfico PNG, conforme ilustrados nas Figuras 5 a 8.

Figura 5
Figura 5. Fundo de tela

Figura 6
Figura 6. Ícone do jogador humano

Figura 7
Figura 7. Ícone do jogador computador

Figura 8
Figura 8. Ícone da bola     

                          Quando a classe TelaPrincipal inicializa, devemos conseguir cada elemento gráfico do arquivo JAR e armazená-lo em um campo Image. Isto pode ser feito no construtor.     

private Image screenShot;

public MainScreen(TestMidletMIDlet parent) {
// some code...

try {
screenShot = Image.createImage
("/Images/screenShot.png");
}
catch(IOException e) {}
// we do this for all the other images
}
          

 Segurando a tecla pressionada

                            Como visto nas classes anteriores, executamos outra vez a interface CommandListener. Aqui, usaremos  somente CommandListener para parar o jogo e retornar ao menu principal. Para fazer isto, temos que inicializar primeiro o objeto Command. Em nosso exemplo etiquetamos este objeto como Pause. A etiqueta pode ser vista na Figura 4, na tecla inferior do lado esquerdo da tela.

/ this is the Command object, labeled as "Pause"
private Command pauseCommand =
new Command("Pause", Command.STOP, 1);

                            Todos os eventos CommandListener são assegurados pelo commandAction().

/**
* Handles command actions
*/
public void commandAction
(Command c, Displayable d) {
// verified that the pause button was pressed
if (c == pauseCommand) {
// do something
}
}

                          Na classe Canvas, podemos também segurar as teclas pressionadas com os métodos keyPressed(int keyCode) e keyReleased(int keyCode). Toda tecla pressionada invoca keyPressed() e toda tecla liberada invoca keyReleased(). Em nosso jogo, usamos estas funções para segurar as teclas de seta e seleção. Estas são as teclas que movem o ícone do jogador humano.

private keyStatus = 0;

/**
* Called when a key is pressed.
*/
protected void keyPressed(int keyCode) {
if (this.getGameAction(keyCode) == UP) {
keyStatus = KEY_NUM2;
}
else if(this.getGameAction(keyCode) == DOWN){
keyStatus = KEY_NUM8;
}
else if(this.getGameAction(keyCode) == LEFT){
keyStatus = KEY_NUM4;
}
else if(this.getGameAction(keyCode) == RIGHT){
keyStatus = KEY_NUM6;
}
else if(this.getGameAction(keyCode) == FIRE){
keyStatus = KEY_NUM5;
}
else {
keyStatus = keyCode;
}
}

/**
* Called when a key is released.
*/
protected void keyReleased(int keyCode) {
keyStatus = 0;
}

                            Em geral, este método atualiza o valor do campo keyStatus. Se nenhuma tecla estiver sendo pressionada neste  momento, então o valor do keyStatus é 0; se não, é algum valor inteiro. O movimento atual do jogador será  explicado mais tarde.


  Algoritmo do jogo
                            Esta classe inicializa uma classe interna TimerTask. A iniciação real é feita por um Timer, que invoca periódicamente o TimerTask  (por default, cada 50 milissegundos, mas estes são ajustáveis pelo jogador humano na tela de níveis). A classe TimerTask executa a função myMove2 ().

private Timer timer;

public void startTimer()
{
// this class is being executed periodically.
TimerTask mover = new TimerTask() {
public void run() {
myMove2();
}
};

timer = new Timer();
// invokes the mover class.
try {
// if anything is being set by the level
// screen
timer.schedule(mover, parent.getLevel(),
parent.getLevel());
}
catch (IllegalArgumentException e) {
timer.schedule(mover, 50, 50);
}

}

        O método myMove2() tem duas regras:

        1. Verifica o valor do keyStatus (que é ajustado pelos métodos keyPressed() e keyReleased()) e move conforme o ícone do jogador humano.

        2. Move o ícone do jogador do computador de acordo com a evolução da situação no jogo.

       Conforme explicado anteriormente, cada tecla pressionada (na seta e tecla de seleção) ajusta o valor do campo keyStatus. De acordo com esse valor, calculamos as coordenadas do ícone do jogador humano.

// coordinates of human player icon
private int meX, meY;
// coordinates of the ball
private int xBall, freeBallY, yBall;
// coordinates of the field's corner
private int x1, x2, x3, x4, y1, y2, y3, y4;


private void myMove2()
{
// some code

// define me Coordinates
switch(keyStatus) {
// up
case Canvas.KEY_NUM2:
meY--;
if (meY < y1)
meY = y1;
break;
// down
case Canvas.KEY_NUM8:
meY++;
if (meY > y4)
meY = y4;
break;
// right
case Canvas.KEY_NUM6:
meX++;
if ((x2 + x3) / 2 < meX) {
meX = (x2 + x3) / 2;
}
if (ballOwner == 0 && compMode != 5)
xBall = meX + 8;
break;
// left
case Canvas.KEY_NUM4:
meX--;
if ((x1 + x4) / 2 > meX) {
meX = (x1 + x4) / 2;
}
if (ballOwner == 0 && compMode != 5)
xBall = meX + 8;
break;
// fire
case Canvas.KEY_NUM5:
if (compMode == 2) {
originalY = freeBallY;
compMode = 5;
}
break;
default:
/////
}

// some code...
}

    Como o jogo roda, os controles do computador dos ícones de jogadores opondo-se. As ações da CPU dos jogadores variam de acordo com as situações que evoluem durante o jogo - o computador se comporta de acordo com a situação. O campo interno compMode armazena um código de situação (code situation) para cada dado momento. As situações são:

    1. Salto da bola: Esta situação ocorre somente no começo do jogo. Os ícones de ambos os jogadores estão no meio do campo e o ícone da bola está a direita entre eles caindo para baixo. A mudança do status da situação  é dada quando um ou outro ícone do jogador trava a bola.

    2. O ícone do jogador humano tem a bola: Quando o jogador humano se move, podemos ver a bola sendo driblada com o jogador humano. O computador move seu jogador para o jogador humano e tenta roubar a bola quando está bem próximo a ele.

    3. O ícone do jogador do computador tem a bola: Nesta situação, o computador tenta mover-se para a cesta do jogador humano. Se o ícone do jogador humano for na frente dele, tentará contorná-lo. Quando o jogador do computador está bem perto à cesta do jogador humano, tentará arremessar na cesta.

    4. Não usado.

   5. O jogador humano arremessa na cesta: O computador muda esta situação quando o jogador pressiona a tecla de seleção (ou disparar, em alguns dispositivos). Podemos ver a bola que está sendo jogada para a  cesta. Se o arremesso for bem sucedido, os dois jogadores voltam para o lado do campo correspondente ao início do jogo e o computador muda a situação para 3 (compMode=3).

    6. O jogador do computador arremessa na bola: Quando o jogador do computador tem a bola e o ícone do jogador do computador está bem perto da cesta do jogador humano, tentará automaticamente arremessar na cesta. Se o arremesso for bem sucedido, os dois jogadores voltam para o lado do campo correspondente ao início do jogo e o computador muda a situação para 2 (compMode=2).

// coordinates of human player icon
private int meX, meY;
// coordinates of the ball
private int xBall, freeBallY, yBall;
// coordinates of the field's corner
private int x1, x2, x3, x4, y1, y2, y3, y4;
// computer player situation state
private int compMode;


private void myMove2()
{
// some code...

// switch by situation
switch (compMode) {
/*
*free ball jumps
*/
case 1:
// this method controls the jump
// movement of the ball
ballJump();

// decides who gets the ball


// computer gets the ball
if (Math.abs(xBall - compX) <= 21 &&
Math.abs(freeBallY - compY) <= 5) {
case3Mode = 1;
compMode = 3;
delay = 0;
}
// me kidnaps the ball
if (Math.abs(meX - xBall) <= 21 &&
Math.abs(meY - freeBallY) <= 5) {
compMode = 2;
delay = 0;
delay2 = 0;
}

// calculate comp moves
if (compY > freeBallY) {
compY--;
}
if (compY < freeBallY) {
compY++;
}
if (compX > xBall) {
compX--;
}
if (compX < xBall) {
compX++;
}
compCheckBorders();
break;
/*
*the ball is at me player
*/
case 2:
xBall = meX + 6;
freeBallY = meY;
ballOwner = 0;
delay2++;
ballJump();
// computer steals the ball
if (Math.abs(meX - compX) <= 21 &&
Math.abs(meY - compY) <= 5 &&
delay >= 10) {
case3Mode = 1;
compMode = 3;
delay = 0;
delay2 = 0;
}
if (delay2 > 30) {
if (Math.abs(meX + 20 - compX) >
Math.abs(meY - compY)) {
if (compX > meX + 20)
compX--;
else
compX++;
}
else {
if (compY > meY)
compY--;
else
compY++;
}
// check borders for comp players
compCheckBorders();
}
break;
/*
*the ball is at computer player
*/
case 3:
xBall = compX - 3;
freeBallY = compY;
ballOwner = 1;
ballJump();
// me kidnaps the ball
if (Math.abs(meX - compX) <= 21 &&
Math.abs(meY - compY) <= 5 &&
delay >= 5) {
compMode = 2;
delay = 0;
delay2 = 0;
}
/*
here we compute how the computer
player icon will move
*/
switch (case3Mode)
{
// go back from player
case 1:
compX++;
if (compX > meX + 29) {
case3Mode = 2;
}
break;
// go side from player
case 2:
if (compY < myHeight * 3 / 4) {
compY++;
compYDirection = 1;
case3Mode = 3;
}
compY--;
compYDirection = 0;
case3Mode = 3;
break;
// continue go side
case 3:
if (compYDirection == 1 &&
compY <= meY + 15) {
compY++;
if (compY > y4)
compY = y4;
}
else if (compYDirection == 0 &&
compY >= meY - 15) {
compY--;
if (compY < y1)
compY = y1;
}
else {
case3Mode = 4;
}
break;
// go forward
case 4:
if (compX > myWidth * 11 / 32) {
compX--;
}
else if (compX < myWidth * 1 / 4 - 3)
{
compX++;
}
else {
originalY = freeBallY;
compMode = 6;
}
break;
// finally throw the ball
case 5:
case3Mode = 1;
originalY = freeBallY;
compMode = 6;
yBall = 9;
deltaX = 1;
break;
default:
//
}

if (compX - meX <= 15 &&
Math.abs(compY - meY) <= 10) {
case3Mode = 1;
}
// check borders for comp player
compCheckBorders();
break;
/*
*not in use
*/
case 4:
break;
/*
*me throws the ball
*/
case 5:
freeBallY = originalY - deltaY[deltaX];
deltaX++;
xBall++;

// checks if the ball hits the basket
if (deltaX >= 29 &&
(xBall >= (x2 + x3) / 2 - 10 &&
xBall <= (x2 + x3) / 2 + 10)) {
myScore += 2;
oldY = 0;
compMode = 3;

// reset player and the balls
meX = myWidth * 3 / 16;
meY = myHeight * 3 / 4;
compX = myWidth * 13 / 16;
compY = myHeight * 3 / 4;
xBall = compX - 8;
yBall = 3;
deltaX = 0;
}
// if the ball reaches the floor
else if (deltaX >= 29 &&
(xBall < (x2 + x3) / 2 - 10 ||
xBall > (x2 + x3) / 2 + 10)) {
freeBallY = meY;
compMode = 1;
oldY = 0;
deltaX = 0;
}
else {
oldY = freeBallY;
}
break;
/*
*comp throws the ball
*/
case 6:
freeBallY = originalY - deltaY[deltaX];
deltaX++;
xBall--;

// checkes if the ball hits the basket
if (deltaX >= 29) {
compScore += 2;
oldY = 0;
compMode = 2;

// reset player and the balls
meX = myWidth * 3 / 16;
meY = myHeight * 3 / 4;
compX = myWidth * 13 / 16;
compY = myHeight * 3 / 4;
xBall = meX + 8;
yBall= 3;
deltaX = 0;
}
else {
oldY = freeBallY;
}
break;
default:
//sdfgsdfgsdgf
}

/*
after the coordinates of the human player
icon, the computer player icon and the ball
has been set we can go to the last stage,
which is painting the screen.
*/
repaint();
}

/*
this method controls the jump movement of the
ball
*/
private void ballJump()
{
if (ballDir == 0) {
yBall--;
if (yBall < 3) {
ballDir = 1;
}
}
else {
yBall++;
if (yBall > 9) {
ballDir = 0;
}
}
}

/*
this function checks if the computer
passed the border of the field
*/
private void compCheckBorders()
{
if (compY < y1) {
compY = y1;
}
if (compY > y4) {
compY = y4;
}
if (compX > (x2 + x3) / 2) {
compX = (x2 + x3) / 2;
}
if (compX < (x1 + x4) / 2) {
compX = (x1 + x4) / 2;
}
}

        Depois que todas as coordenadas foram ajustadas, podemos prosseguir ao estágio final, que é realmente pintar a tela. Isto é feito chamando o método paint(). Chamamos este método no fim da função move(), chamando repaint().
  
/**
* paints the screen
*/
public void paint(Graphics g) {

Graphics saved = g;
// these fields show the clock in the game
String clockMinuteStr = new String();
String clockSecondStr = new String();

// initialize a buffered image
if (offscreen != null) {
g = offscreen.getGraphics();
}

// cleans the screen
g.setColor(255, 255, 255);
g.fillRect(0, 0, this.getWidth(),
this.getHeight());

// define corners of field
x1 = myWidth*3/16;
x2 = myWidth*13/16;
x3 = myWidth - 2;
x4 = 2;
y1 = myHeight * 1 / 2;
y4 = myHeight - 1;

// draw solid background
g.setColor(255, 255, 255);
g.fillRect(offsetWidth, offsetHeight,
myWidth, myHeight);
g.setColor(0, 0, 0);
g.drawImage(screenShot, offsetWidth,
offsetHeight, 0);

// draw Scores
g.fillRect(offsetWidth + myWidth / 4,
offsetHeight + myHeight / 8,
myWidth / 2, myHeight / 4);
g.setColor(255, 255, 255);
g.drawRect(offsetWidth + myWidth / 4,
offsetHeight + myHeight / 8,
myWidth / 2, myHeight / 4);
clockMinuteStr =
String.valueOf(clockMinute);
clockSecondStr =
String.valueOf(clockSecond);
if (clockMinuteStr.length() == 1)
clockMinuteStr = "0" + clockMinuteStr;
if (clockSecondStr.length() == 1)
clockSecondStr = "0" + clockSecondStr;
g.drawString(":", offsetWidth + myWidth / 2,
offsetHeight + 15,
Graphics.TOP|Graphics.LEFT);
g.drawString(clockMinuteStr,
offsetWidth + myWidth / 2 - 15,
offsetHeight + 17,
Graphics.TOP|Graphics.LEFT);
g.drawString(clockSecondStr,
offsetWidth + myWidth / 2 + 5,
offsetHeight + 17,
Graphics.TOP|Graphics.LEFT);
g.drawString(String.valueOf(myScore),
offsetWidth + myWidth / 2 - 25,
offsetHeight + 32,
Graphics.TOP|Graphics.LEFT);
g.drawString(String.valueOf(compScore),
offsetWidth + myWidth / 2 + 20,
offsetHeight + 32,
Graphics.TOP|Graphics.LEFT);

// paint player me
g.drawImage(mePlayer, offsetWidth + meX,
offsetHeight + meY - 19, 0);

// paint Computer Player
g.drawImage(compPlayer, offsetWidth + compX,
offsetHeight + compY - 19, 0);

//paintBall
g.drawImage(tinyBall, offsetWidth + xBall,
offsetHeight + freeBallY - yBall, 0);


// paints the buffered image
if (g != saved) {
saved.drawImage(offscreen, 0, 0,
Graphics.LEFT | Graphics.TOP);
}

}


                       
Tratando a interrupção de chamada

        Quando uma chamada ocorrer no meio do jogo, a tela Canvas pode desaparecer, assim quiser congelar o estado do jogo (salvar todos os dados a respeito das posições dos jogadores, posição da bola, número de pontos, etc.). Neste caso, o congelamento do jogo é feito parando o temporizador. Existem duas funções relacionadas à Canvas, desaparecendo e reaparecendo. hideNotify() é chamado depois que Canvas desaparece e showNotify() é chamado quando Canvas reaparece. Paramos o temporizador no evento hideNotify() e reativamos o temporizador no evento showNotify().

**
* called when the screen disappears
*/
protected void hideNotify() {
if (finishGame == 0) {
parent.setCurrent("MainMenu");
}
// stops the internal timer and thus
// freezes the game.
stopTimer();
}

/**
* called when the screen reappears
*/
protected void showNotify() {
// restarts the internal timers.
startTimer();
}


                           
Salvando os dados persistentes

        As aplicações de JME têm um método para armazenar dados mesmo depois que o usuário terminou a aaplicação. Controlamos estes dados com a classe RecordStore. Nesta aplicação, necessitamos armazenar dados persistentes, permitindo o jogador parar o jogo em um dado momento, retiramos a aplicação e retornamos o jogo algumas horas mais tarde e de continuar exatamente do momento onde parou. Os dados que necessitamos armazenar incluem: cronometrar o jogo, coordenar de dois ícones de jogador, coordenar a bola, etc. Arranjamos todos estes dados em um byte[] array e só então podemos armazená-lo.
                
/**
* Writes all the game data into recordstore
* @param rec
*/
public void writeRMS(byte[] rec) {
try {
rs = RecordStore.openRecordStore("pocket",
true);
if (rs.getNumRecords() > 0)
rs.setRecord(1, rec, 0, 31);
else
rs.addRecord(rec, 0, 31);
rs.closeRecordStore();
}
catch (Exception e) {}
}

/**
* Reads the data from the recordstore
* @return
*/
public byte[] readRMS() {
byte[] rec = new byte[31];
try {
rs = RecordStore.openRecordStore("pocket",
true);
rec = rs.getRecord(1);
rs.closeRecordStore();
}
catch (Exception e) {}
return rec;
}

/**
*
* Deletes all the record stores
*/
public void deleteRMS() {
if (RecordStore.listRecordStores() != null) {
try {
RecordStore.deleteRecordStore
("pocket");
}
catch (Exception e) {}
}
}


 
Outras telas
                         

        Duas outras telas nesta aplicação são InstructionsForm (mostrado na Figura 9) e AboutForm. Estendemos estas duas classes da classe Form e implementamos também CommandListener para segurar as teclas pressionadas. A classe Form é parte da API  de alto nível (high-level) e nos permite introduzir facilmente o texto liso, imagens e outros artigos a ser indicados.

Figura 9
Figura 9. Tela de instrução

public class InstructionsForm extends Form
implements CommandListener {
private TestMidletMIDlet parent;
private Command mainMenu =
new Command("Back", Command.BACK, 1);

public InstructionsForm(TestMidletMIDlet
parent) {
super("Instructions");
addCommand(mainMenu);
setCommandListener(this);

// insert some text to be seen.
this.append("The objective of this game
is to shoot as many baskets as
possible while preventing your
opponent shoot to your basket.\n\n");
this.append("move your player using
4 for moving left, 2 for moving up,
6 for moving right and 8 for moving
down.\n\n");
this.append("press 5 to throw the ball");

this.parent = parent;
}

public void commandAction(Command c,
Displayable d) {
if (c == mainMenu) {
parent.setCurrent("MainMenu2");
}
}
}


Conclusão

          Neste artigo, discutimos algumas  características mais comuns no ambiente JME. Estas características incluem a classe MIDlet, que é a classe base para todas as aplicações JME, a classe de API's de baixo-nível (low-level) Canvas e classes de API de alto-nível (high-level), tais como a List e Form. Cobrimos também a estrutura organizacional do jogo, tal como as telas típicas da aplicação.

          Como mencionado anteriormente, esta é uma descrição breve de um jogo típico em JME. Embora os jogos sejam abundantes em ambiente já sustentados, existem muitos outros usos para aplicações ME, tais como os leitores com quotas de estoque, leitores RSS, etc.


Recursos
Kobi Krasnoff é sério do programador e tem o computador como hobby.