双缓冲技术解决闪烁问题
上节,我们实现了动画效果,但是发现窗口会不停的闪烁,体验度非常差。在实际开发中,绘制图形是非常复杂的,绘图可能需要几秒甚至更长时间,也经常发生闪烁现象, 为了解决这个问题,我们通常使用“双缓冲技术”。
1)“双缓冲技术”的绘图过程如下:
a. 在内存中创建与画布一致的缓冲区
b. 在缓冲区画图
c. 将缓冲区位图拷贝到当前画布上
e. 释放内存缓冲区
双缓冲即在内存中创建一个与屏幕绘图区域一致的对象,先将图形绘制到内存中的这个对象上,再一次性将这个对象上的图形拷贝到屏幕上,这样能大大加快绘图的速度。
我们只需将如下“双缓冲”实现代码,放入MyGrameFrame类中,即可:
【示例1】添加双缓冲技术
privateImageoffScreenImage=null;
publicvoidupdate(Graphics g) {
if(offScreenImage==null)
offScreenImage=this.createImage(500,500);//这是游戏窗口的宽度和高度
Graphics gOff =offScreenImage.getGraphics();
paint(gOff);
g.drawImage(offScreenImage, 0, 0,null);
}
GameObject类设计
1) GameObject类的定义
我们发现,窗口中所有的对象(飞机、炮弹等等)都有很多共性:“图片对象、坐标位置、运行速度、宽度和高度”。为了方便程序开发,我们需要设计一个GameObject类,它可以作为所有游戏物体的父类,方便我们编程。
【示例2】GameObject类
packagecn.sxt.game;
importjava.awt.Graphics;
importjava.awt.Image;
importjava.awt.Rectangle;
publicclassGameObject {
Imageimg;//该物体对应的图片对象
doublex,y;//该物体的坐标
intspeed;//该物体的运行速度
intwidth,height;//该物体所在矩形区域的宽度和高度
/**
*怎么样绘制本对象
*@paramg
*/
publicvoiddrawMySelf(Graphics g){
g.drawImage(img, (int)x, (int)y,null);
}
publicGameObject(Image img,doublex,doubley) {
this.img= img;
this.x= x;
this.y= y;
if(img!=null){
this.width= img.getWidth(null);
this.height= img.getHeight(null);
}
}
publicGameObject(Image img,doublex,doubley,intspeed,intwidth,
intheight) {
this.img= img;
this.x= x;
this.y= y;
this.speed= speed;
this.width= width;
this.height= height;
}
publicGameObject() {
}
/**
*返回物体对应矩形区域,便于后续在碰撞检测中使用
*@return
*/
publicRectangle getRect(){
returnnewRectangle((int)x,(int)y,width,height);
}
}
2)设计飞机类
有了GameObject这个父类,我们设计飞机类特别简单,目前飞机类没有特别复杂的要求。我们只需简单的继承,即可使用:
【示例3】Plane类
packagecn.sxt.game;
importjava.awt.Graphics;
importjava.awt.Image;
publicclassPlaneextendsGameObject {
@Override
publicvoiddrawMySelf(Graphics g) {
super.drawMySelf(g);
this.x+=3;//飞机水平飞,我们也可以调整x、y算法,按照我们指定的路径飞行
}
publicPlane(Image img,doublex,doubley) {
super(img,x,y);
}
}
通过继承,我们发现实现新的类,爽了很多!
3) MyGameFrame类调用方式的调整
我们将Plane类封装后,也无需在MyGameFrame类中添加那么多飞机的属性,我们全部封装到了Plane类里面,因此,调用也变得更加简单。
【示例4】封装后的MyGameFrame类
publicclassMyGameFrameextendsFrame {
ImagebgImg= GameUtil.getImage("images/bg.jpg");
ImageplaneImg= GameUtil.getImage("images/plane.png");
Planeplane=newPlane(planeImg,300,300);
//paint方法作用是:画出整个窗口及内部内容。被系统自动调用。
@Override
publicvoidpaint(Graphics g) {
g.drawImage(bgImg, 0, 0,null);
plane.drawMySelf(g);//画出飞机本身
}
//其余代码,没有任何变化,不在附上,自行参考上一个版本。
}
通过面向对象封装后,如果我们要再创建多个飞机,也变得异常简单。
【示例5】创建多个飞机
publicclassMyGameFrameextendsFrame {
ImagebgImg= GameUtil.getImage("images/bg.jpg");
ImageplaneImg= GameUtil.getImage("images/plane.png");
Planeplane=newPlane(planeImg,300,300);
Planeplane2=newPlane(planeImg,300,350);
Planeplane3=newPlane(planeImg,300,400);
//paint方法作用是:画出整个窗口及内部内容。被系统自动调用。
@Override
publicvoidpaint(Graphics g) {
g.drawImage(bgImg, 0, 0,null);
plane.drawMySelf(g);//画出飞机本身
plane2.drawMySelf(g);//画出飞机本身
plane3.drawMySelf(g);//画出飞机本身
}
//其余代码,和上个版本一致,为节省篇幅突出重点,不在附上。
}
「全栈Java笔记」是一部能帮大家从零到一成长为全栈Java工程师系列笔记。笔者江湖人称 Mr. G,10年Java研发经验,曾在神州数码、航天院某所研发中心从事软件设计及研发工作,从小白逐渐做到工程师、高级工程师、架构师。精通Java平台软件开发,精通JAVAEE,熟悉各种流行开发框架。
笔记包含从浅入深的六大部分:
A-Java入门阶段
B-数据库从入门到精通
C-手刃移动前端和Web前端
D-J2EE从了解到实战
E-Java高级框架精解
F-Linux和Hadoop