Namespaces
Variants
Actions
Revision as of 21:48, 22 June 2011 by reflexus@ig.com.br (Talk | contribs)
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)

Parallax Scrolling in Java ME

Jump to: navigation, search

Parallax Scrolling in Java ME.

This is what i use im my game. Guess Which game? I decided to release the code 'cause i couldn't find it in any web page. The net is an open book (or university) and i've taken too much from it....now it's my time to contribute... I still want to give back to the community for helping me so much when I was a novice, and as a result I decided to write this tutorial.

It also demonstrate how to:

  • pause/unpause an app (RIGHT SOFT KEY / incoming phone call);
  • get high frames por second (fps);
  • get randomNumbers / randomBoolean;
  • use custom fonts (bitmap fonts);
  • use LayerManager/TiledLayer.

Go to my projects and grab JavaMEParallaxScrolling project (with all resources and full source code). You can easily open it in NetBeans 6.9


Please don't forget to check my game projects (need feedback):

Queops, LunarPatrol & X-Rally.

Thank you.

Another yet is to come....maybe a new version of Space Impact...


Brought to you by George Roberto Peres (JavaMan)

                 reflexus@ig.com.br


/**********************************************************************************************/


import java.io.IOException; import javax.microedition.lcdui.Display; import javax.microedition.midlet.MIDlet; import javax.microedition.midlet.MIDletStateChangeException;


public class Main extends MIDlet {

 private ParallaxScrollingCanvas canvas;
 private Display display;


 public Main() {
   //
 }


 public void startApp() {
   if (canvas==null) canvas = new ParallaxScrollingCanvas();
   display = Display.getDisplay(this);
   //Display.getDisplay(this).setCurrent(canvas);
   try {
     canvas.init();
     canvas.start();
   }
   catch (IOException e) { }
   display.setCurrent(canvas);
 }


 public void pauseApp() {
   //
 }


 public void destroyApp(boolean destroy) throws MIDletStateChangeException {
   display = null;
   if (canvas!=null) canvas.stop();
   System.gc();
   notifyDestroyed();
 }



}


/**********************************************************************************************/


/*

* This class demonstrate how to use LayerManager/TiledLayer.
*  by George Roberto Peres
*  reflexus@ig.com.br
*/

import java.io.IOException; import javax.microedition.lcdui.Image; import javax.microedition.lcdui.game.LayerManager; import javax.microedition.lcdui.game.TiledLayer;


/**

*
* @author George Roberto Peres
*/

public final class Design {

 // Tiled Layers
 private Image back, front, solo;
 protected TiledLayer layerBack, layerFront, layerSolo;
 // fonte
 private Image imgFont;   // imagem da fonte.png
 protected int charLarg, charAlt;


 protected Design() {
   try {
     layerBack = defineCenario(layerBack, getBack(), 4);
     layerFront = defineCenario(layerFront, getFront(), 4);
     layerSolo = defineCenario(layerSolo, getSolo(), 2);
   }
   catch (IOException ex) { }
 }


 // ordem e' importante. o ultimo a ser anexado representa a camada mais profunda
 protected void updateLayerManager(LayerManager lm) throws IOException {
   lm.append(layerSolo);
   lm.append(layerFront);
   lm.append(layerBack);
 }


 protected TiledLayer defineCenario(TiledLayer layer, Image image, int rows) throws IOException {
   short c=1, l=0;
   layer = new TiledLayer(32, rows, image, 16, 16);
   for (byte lin=0; lin<rows; lin++) {
     for (byte col=0; col<32; col++) {
       layer.setCell(col, lin, c);
       c++;
       if ((c-1)%(l+16)==0) c = (byte)(l+1);
     }
     l+=16;
     c+=16;
   }
   return layer;
 }


 private Image getBack() throws IOException {
   if (back == null) {
     back = Image.createImage("/back.png");
   }
   return back;
 }


 private Image getFront() throws IOException {
   if (front == null) {
     front = Image.createImage("/front.png");
   }
   return front;
 }


 private Image getSolo() throws IOException {
   if (solo == null) {
     solo = Image.createImage("/solo.png");
   }
   return solo;
 }


 protected Image getFont() {
   if (imgFont == null)
     try {
       imgFont = Image.createImage("/fonts.png");
       charLarg = imgFont.getWidth() / 96;   //9
       charAlt = imgFont.getHeight();   //12
     }
     catch (IOException e) { }
   return imgFont;
 }



}


/**********************************************************************************************/


/*

* This class demonstrate how to get randomNumber/randBoolean.
* brought to you by George Roberto Peres
*                   reflexus@ig.com.br
*/

import java.util.Random; import javax.microedition.lcdui.Graphics;


public final class Tools {


 private static final Random RAND = new Random();
 //static { RAND = new Random(); }
 protected static final int GRAPHICS_TOP_LEFT = Graphics.LEFT | Graphics.TOP;
 // Dimensoes da tela  Game Canvas
 protected static final int LARG = 240;   // largura da tela (x)
 protected static final int ALT = 320;   // altura da tela (y)
 protected static final int Y = 200;   // altura da canvas principal game
 protected static final boolean TELA_CHEIA = true;   // tela cheia (full screen)
 // codigos dos softkey variam dependendo do modelo do celular
 // -6 e -7  sao valores validos para telefones Nokia. Consultar para outros modelos.
 protected static final int LEFT_SOFTKEY_CODE = -6;
 protected static final int RIGHT_SOFTKEY_CODE = -7;


 /****************************************************************************
  * gerador de numero aleatorio inteiro                                      *
  * @param max  numero gerado e' maior ou igual a zero e menor que max       *
  * @return  r, um numero pseudo-randomico                                   *
  ****************************************************************************/
 protected static int randomNumber(int max) {
   return Math.abs(RAND.nextInt() % max);   // valor absoluto de -(max-1) ate' max-1
 }
 /****************************************************************************
  * gerador de booleano aleatorio                                            *
  * @return  boolean                                                         *
  ****************************************************************************/
 protected static boolean randBoolean() {
   return (randomNumber(2)==1);
 }


 /*// numero randomico inteiro entre min e max
 protected static int getRand(int min, int max) {
   return (Math.abs(RAND.nextInt()) % (max - min)) + min;
 }*/


}


/**********************************************************************************************/


/*

* This is a simple ParallaxScrolling example.
* It also demonstrate how to: pause/unpause an app (RIGHT SOFT KEY / phone call);
* use custom fonts; get high frames por second (fps): smaller painted screen area,
* no threads (only canvas thread, of course), no timers, no synchronized.
* The code presented here is easy to understand (clean code).
* brought to you by George Roberto Peres
*                   reflexus@ig.com.br
*/

import java.io.IOException; //import javax.microedition.lcdui.Font; import javax.microedition.lcdui.Graphics; import javax.microedition.lcdui.game.GameCanvas; import javax.microedition.lcdui.game.LayerManager;

/**

* @author  George Roberto Peres
* @version 1.0
*/

public class ParallaxScrollingCanvas extends GameCanvas implements Runnable {


 private final Graphics g = getGraphics();
 private LayerManager lm;
 private Thread t;
 private Design design;
 private static final byte[] PAUSE = { 80, 65, 85, 83, 69 };
 private static final byte[] FPS = { 70, 80, 83, 58 };
 private boolean pausa;
 private byte cont;   // timing
 private int a;   // auxiliar geral
 private boolean isRunning;
 private boolean showFPS = true;
 // ciclos = frames por segundo mostrado na tela
 private int fps;
 private int cyclesThisSecond;
 private long lastFPSTime;


 // sobrescreve o metodo de javax.microedition.lcdui.Canvas
 public void keyPressed(int keyCode) {
   if (keyCode==Tools.RIGHT_SOFTKEY_CODE) {
     if (!pausa) pausa(); else resume();
   }
   else
   if (keyCode==Tools.LEFT_SOFTKEY_CODE) {
     if (pausa) resume();
   }
   else
   if (keyCode == this.KEY_NUM0) {
     showFPS = !showFPS;   // inverte boolean
   }
 }


 /** Creates a new instance of theCanvas */
 public ParallaxScrollingCanvas() {
   super(true);
   setFullScreenMode(Tools.TELA_CHEIA);
   //g.setFont(Font.getFont(Font.FACE_SYSTEM, Font.STYLE_BOLD, Font.SIZE_SMALL));
   design = new Design();
   g.setColor(0,0,0);
   g.fillRect(0, 0, getWidth(), getHeight());
 }


 protected void init() throws IOException {
   // gerenciador de camadas
   lm = new LayerManager();
   design.updateLayerManager(lm);
   design.layerBack.setPosition(0, 56);
   design.layerFront.setPosition(0, 80);
   design.layerSolo.setPosition(0, 140);
   //System.out.println("lm size = "+lm.getSize());
 }


 public void start() {
   isRunning = true;
   t = new Thread(this);
   //t.setPriority(Thread.MAX_PRIORITY);
   t.start();
 }


 public void stop() {
   isRunning = false;
   t = null;
 }


 private void tarefas() {
   // cenario do fundo. move mais devagar
   if (cont%5==0) {
     design.layerBack.move(-1, 0);
     // empacota para repetir os mesmos graficos novamente
     if (Math.abs(design.layerBack.getX())>255) design.layerBack.setPosition(0, 56);
   }
   // cenario da frente. move mais depressa
   if (cont%3==0) {
     design.layerFront.move(-1, 0);
     // empacota para repetir os mesmos graficos novamente
     if (Math.abs(design.layerFront.getX())>255) design.layerFront.setPosition(0, 80);
   }
   // move layerSolo
   design.layerSolo.move(-1, 0);
   // empacota para repetir os mesmos graficos novamente
   if (Math.abs(design.layerSolo.getX())>255) design.layerSolo.setPosition(0, 140);
 }


 private void renderiza() {
   // background
   g.setColor(0, 0, 0);
   g.fillRect(0, 0, Tools.LARG, Tools.Y);
   lm.paint(g, 0, 0);   // desenha layer manager
   if (showFPS) {
     drawString(FPS, 0, 0);
     drawScoreInt(fps, 2, design.charLarg*4, 0);
   }
   drawScoreInt(Math.abs(design.layerSolo.getX()), 3, 120, 0);
   flushGraphics(0, 0, Tools.LARG, Tools.Y);   // descarrega graficos
 }


 // desenha caracter de acordo com o codigo da tabela ASCII
 // util para acrescentarmos caracteres especiais customizados
 private void drawCharASCII(int ASCII, int x, int y) {
   // caractere espaco ou nao imprimivel ou o caractere>255 nao sao desenhados
   if (ASCII < 33  ||  ASCII > 255) return;
   g.clipRect(x, y, design.charLarg, design.charAlt);   // altera clip pra tamanho do caractere
   // desenha o caractere dentro do retangulo. (cIndex - 32) * charLarg = posicao do caractere
   g.drawImage(design.getFont(), x - ((ASCII - 32) * design.charLarg), y, Tools.GRAPHICS_TOP_LEFT);
   g.setClip(0, 0, Tools.LARG, Tools.ALT);   // reset o clip - full screen, ou seja, volta o normal
 }


 private void drawString(byte []s, int x, int y) {
   int cx = x;   // posicao inicial
   // loop atraves de todos os caracteres na string
   for (a = 0; a < s.length; a++) {
     drawCharASCII(s[a], cx, y);
     cx += design.charLarg;   // va para a proxima posicao para desenhar
   }
 }


 // desenha score int em x,y (ASCII de 48~57).  ex: 000777 (6 digitos)
 private void drawScoreInt(int score, int digitos, int x, int y) {
   if (digitos<1 || digitos>8) return;
   int d = 1;
   for (a=1; a<digitos; a++)  d *= 10;
   a = x;   // posicao inicial
   for (int i=0; i<digitos; i++) {
     drawCharASCII(score/d + 48, a, y);  // +48 pra coincidir com tabela ASCII
     a += design.charLarg;   // va para a proxima posicao para desenhar
     score -= d*(score/d);
     d /= 10;
   }
 }


 public void hideNotify() {
   if (!pausa) pausa();
 }


 // nada e' feito, porque despausa e' feito manualmente
 public void showNotify() {
   //
 }


 protected void pausa() {
   pausa = true;
   stop();
   drawString(PAUSE, Tools.LARG/2 - (design.charLarg*5)/2, Tools.Y/2 - design.charAlt/2);
   flushGraphics(Tools.LARG/2 - (design.charLarg*5)/2, Tools.Y/2 - design.charAlt/2,
           design.charLarg*5, design.charAlt);
 }


 protected void resume() {
   start();
   pausa = false;
 }


 public void run() {
   while(isRunning) {
     // metodo pra conseguir uma medida do FPS mostrado na tela
     if (System.currentTimeMillis() - lastFPSTime > 1000) {
       lastFPSTime = System.currentTimeMillis();
       fps = cyclesThisSecond;
       cyclesThisSecond = 0;
     } else
     cyclesThisSecond++;
     // timing base 60. why? it divides by 1,2,3,4,5,6,10,12,15,20,30
     cont++;
     if (cont==60) cont=0;
     //
     tarefas();
     renderiza();
     // dorme um pouco
     try { Thread.sleep(0); }
     catch (InterruptedException e) { }
   }
 }



}


/**********************************************************************************************/

182 page views in the last 30 days.
Nokia Developer aims to help you create apps and publish them so you can connect with users around the world.

京ICP备05048969号  © Copyright Nokia 2013 All rights reserved