tags:

views:

1311

answers:

6

I'm writing a mobile phone game using j2me. In this game, I am using multiple Canvas objects. For example, the game menu is a Canvas object, and the actual game is a Canvas object too. I've noticed that, on some devices, when I switch from one Canvas to another, e.g from the main menu to the game, the screen momentarily "flickers". I'm using my own double buffered Canvas.

Is there anyway to avoid this?

A: 

Do you use double buffering? If the device itself does not support double buffering you should define a off screen buffer (Image) and paint to it first and then paint the end result to the real screen. Do this for each of your canvases. Here is an example:

public class MyScreen extends Canvas {
   private Image osb;
   private Graphics osg;
   //...

   public MyScreen()
   {
      // if device is not double buffered
      // use image as a offscreen buffer
      if (!isDoubleBuffered())
         {
            osb = Image.createImage(screenWidth, screenHeight);
            osg = osb.getGraphics();
            osg.setFont(defaultFont);
         }
   }

   protected void paint(Graphics graphics)
   {
      if (!isDoubleBuffered())
      {
         // do your painting on off screen buffer first
         renderWorld(osg);

      // once done paint it at image on the real screen
         graphics.drawImage(osb, 0, 0, Tools.GRAPHICS_TOP_LEFT);
      }
      else
      {
         osg = graphics;
         renderWorld(graphics);
      }
   }
}
Vivek
I use double buffering by defining an off screen buffer with a method similar to yours. The flickering always occurs when the new Canvas' main thread starts running.
Dimitris
A: 

A possible fix is by synchronising the switch using Display.callSerially(). The flicker is probably caused by the app attempting to draw to the screen while the switch of the Canvas is still ongoing. callSerially() is supposed to wait for the repaint to finish before attempting to call run() again.

But all this is entirely dependent on the phone since many devices do not implement callSerially(), never mind follow the implementation listed in the official documentation. The only devices I've known to work correctly with callSerially() were Siemens phones.

Another possible attempt would be to put a Thread.sleep() of something huge like 1000 ms, making sure that you've called your setCurrent() method beforehand. This way, the device might manage to make the change before the displayable attempts to draw.

The most likely problem is that it is a device issue and the guaranteed fix to the flicker is simple - use one Canvas. Probably not what you wanted to hear though. :)

Shane Breatnach
I followed your advice and put a long Thread.sleep() in the code. I discovered that the flickering actually occurs after the first repaint() in my Canvas' run() method. Any thoughts?
Dimitris
+3  A: 

I would say, that using multiple canvases is generally bad design. On some phones it will even crash. The best way would really be using one canvas with tracking state of the application. And then in paint method you would have

protected void paint(final Graphics g) {
  if(menu) {
    paintMenu(g);
  } else if (game) {
    paintGame(g);
  }
}

There are better ways to handle application state with screen objects, that would make the design cleaner, but I think you got the idea :)

/JaanusSiim

JaanusSiim
A: 

I would agree with JaanusSiim, The best way especially for a game is to have a single canvas calls

Azlam
A: 

It might be a good idea to use GameCanvas class if you are writing a game. It is much better for such purpose and when used properly it should solve your problem.

Honza
I originally used GameCanvas objects. I observed the same "flicker" behaviour with that implementation too.
Dimitris
This is because one some phones even for GameCanvas back-buffering is not implemented.
Honza
A: 

Hypothetically, using 1 canvas with a sate machine code for your application is a good idea. However the only device I have to test applications on (MOTO v3) crashes at resources loading time just because there's too much code/to be loaded in 1 GameCanvas ( haven't tried with Canvas ). It's as painful as it is real and atm I haven't found a solution to the problem. If you're lucky to have a good number of devices to test on, it is worth having both approaches implemented and pretty much make versions of your game for each device.

OMG, wow I just found it... Basically at first I used an introCanvas, a loadingCanvas and a gameOverCanvas which in the new version (all in 1 canvas/state machine) we're not used at all. Turns out that simply clearing these files from the src folder solved the Application Error problem :). Amazing!!