您的位置:首页 > 其它

J2ME游戏开发中的双缓冲技术

2012-07-24 00:39 134 查看

双缓冲的原理:

在手机游戏开发中,可能一个完整的游戏界面需要很多图像的绘制,而在绘制过程中手机屏幕需要不断显示,如果该界面的游戏图像元素太多,很可能当前帧还没有绘制完成下一帧的绘制任务就已经开始,这样会导致屏幕的闪烁。

为了解决这一个问题,我们可以使用一种叫做“双缓冲”的方法来解决该问题。各个手机厂商可以从硬件上支持双缓冲,这样在显示屏幕绘图之前,可以使用一个图像内存来进行屏幕外绘图,等到这个图像内存将整个界面的所有元素在后台绘制完成后,在使用另外一个图像内存来将这个屏幕外的图像绘制到实际屏幕上,这样,整个界面的所有元素好像是一张图。屏幕外的图像内存和屏幕内的图像内存如此交叉绘图,就避免了图像闪烁的产生。

如果手机本身不支持双缓冲功能,在设计时可以通过可变图像来自行设计双缓冲绘图。方法是:在屏幕外内存中创建一个可变图像,一次游戏循环中的所有绘图任务都绘制到该可变图像中,当所有绘图任务完成后,一次性将屏幕内存中的可变图像绘制到屏幕,如下图所示:

从上图中可以看出,当发生n次绘图时,传统的直接绘图方式会在屏幕上直接“擦除--重绘”n次,因此发生闪烁;双缓冲绘图方式是将n次所要绘制的内容绘制到屏幕外内存的可变图像中,完成后一次性绘制到屏幕上,因此画面不会闪烁。

双缓冲的具体实现方法:

了解了上面所讲解的双缓冲原理之后,就可以使用可变的Image图像来进行设计。在双缓冲设计中,可以使用Canvas类中的isDoubleBuffered()方法来检测手机设备是否支持双缓冲。如果支持,则不必使用双缓冲,否则就使用双缓冲;另外通过Image的getGraphics()方法,该方法可用于获取可变图像的画笔Graphics,然后用该Graphics在可变图像中绘图。

MainMIDlet类:

import javax.microedition.lcdui.Display;
import javax.microedition.midlet.MIDlet;
import javax.microedition.midlet.MIDletStateChangeException;

public class MainMIDlet extends MIDlet {

public MainMIDlet() {
Display.getDisplay(this).setCurrent(new MyCanvas());
}
public void exit(){
try {
destroyApp(true);
} catch (MIDletStateChangeException e) {
e.printStackTrace();
}
notifyDestroyed();
}

protected void destroyApp(boolean arg0) throws MIDletStateChangeException {
// TODO Auto-generated method stub

}

protected void pauseApp() {
// TODO Auto-generated method stub

}

protected void startApp() throws MIDletStateChangeException {
// TODO Auto-generated method stub

}

}


MyCanvas类:

import java.io.IOException;

import javax.microedition.lcdui.Canvas;
import javax.microedition.lcdui.Graphics;
import javax.microedition.lcdui.Image;

//双缓冲:解决屏幕抖动的问题
public class MyCanvas extends Canvas {
private Image off_screen;//后台屏幕(可变图像)
private Graphics off_g;//后台屏幕画笔
private Image backg;
private Image cock1,cock2,cock3,cock4;
private static int SW,SH;//屏幕宽高
public MyCanvas() {
this.setFullScreenMode(true);
off_screen = Image.createImage(this.getWidth(), this.getHeight());//生成后台屏幕
off_g = off_screen.getGraphics();//得到后台画笔
try {
backg=Image.createImage("/map1.png");
cock1=Image.createImage("/cock1.png");
cock2=Image.createImage("/cock2.png");
cock3=Image.createImage("/cock3.png");
cock4=Image.createImage("/cock4.png");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

new MyThread().start();
}

protected void paint(Graphics g) {//g:前台画笔
g.drawImage(off_screen, 0, 0, 20);
}

public void render(){
this.setFullScreenMode(true);
SW=this.getWidth();
SH=this.getHeight();
off_g.setColor(0xff000000);
off_g.fillRect(0, 0, off_screen.getWidth(), off_screen.getHeight());
off_g.drawImage(backg, 0, 0, Graphics.LEFT|Graphics.TOP);
off_g.drawImage(cock1, 0, 0, Graphics.LEFT|Graphics.TOP);
off_g.drawImage(cock2, SW, 0, Graphics.RIGHT|Graphics.TOP);
off_g.drawImage(cock3, 0, SH, Graphics.LEFT|Graphics.BOTTOM);
off_g.drawImage(cock4, SW, SH, Graphics.RIGHT|Graphics.BOTTOM);
}

private class MyThread extends Thread{
public void run(){
while(true){
long start = System.currentTimeMillis();
render();
repaint();//发出(重绘请求),UI线程负责回调paint方法
long end = System.currentTimeMillis();
System.out.println("绘制屏幕花费的时间:"+(end-start));
if (end - start < 30) {// 使FPS(Frame Per Second)尽量平缓
sleepGame(30 - (end - start));
} else {
sleepGame(1);
}
}
}

}
public void sleepGame(long time) {
try {
Thread.sleep(time);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}


结果如下图所示:



内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: