- 浏览: 205215 次
- 性别:
- 来自: 长沙
最新评论
-
螺旋懒虫:
原本是想自己画点阵的,一来画的不标准,也不好解析(用excel ...
超级详细解析——字模 -
螺旋懒虫:
这个程序解决了我怎么加载点阵字库文件的问题,和怎么去拿到汉字对 ...
超级详细解析——字模 -
慕容墨风:
你好,我用你的测试程序发现数字提取不了
超级详细解析——字模 -
文之若素:
求代码,我最近在学这个,公司需要,呜呜呜,159374403 ...
初学EXTJS做的系统界面 -
csrhlu:
为什么我压缩之后文件还变大了呢? 而且解压eclipse下边会 ...
自己动手写压缩软件,超详细解释(哈夫曼实现)
以前都不了解双缓冲是什么个东东,
但是为了解决我的坦克大战疯狂的闪屏
后,终于对其有了一个了解。
package My2; import java.awt.Color; import java.awt.Graphics; import java.awt.Image; import java.awt.event.KeyEvent; import java.awt.event.WindowAdapter; import java.awt.event.WindowEvent; import java.util.ArrayList; import java.util.List; import javax.tools.JavaCompiler; public class tankGame extends java.awt.Frame implements java.awt.event.KeyListener{ //窗体宽度 public static final int WINDOW_WIDTH = 800; //窗体高度 public static final int WINDOW_HEIGHT = 600; //窗体刷新率 public static final int REFRESH_RATE = 50; //玩家坦克 static Tank myTank = null; // 坦克起始位置左上角坐标 public static final int Tank_X = 385; public static final int Tank_Y = 550; //敌方坦克同时出现的最大数量 private int tankMaxNum = 10; //当前敌方坦克数量 private int tankNum = 0; //存放敌方坦克的容器 static List<Tank> enemyTanks = new ArrayList<Tank>(); //存放墙的容器 static List<Wall> wallList = new ArrayList<Wall>(); static int wallNum=35; //屏幕画布 static java.awt.Graphics g = null; Image offScreenImage = null; public static void main(String[] args){ tankGame game = new tankGame(); game.showUI(); game.Init(); game.addKeyListener(game); } public void paint(java.awt.Graphics g){ drawTank(); } public static void drawTank(){ if(myTank==null) return ; g.fillRect(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT); myTank.draw(g); for (int i=0;i<enemyTanks.size();i++){ enemyTanks.get(i).draw(g); } for (int i=0;i<wallList.size();i++){ wallList.get(i).draw(g); } } public void Init(){ //加入我方坦克 myTank = new Tank(Tank_X,Tank_Y,true,0); myTank.draw(g); //加入敌方坦克 for (int i = 0; i < tankMaxNum; i++) { Tank enemyTank = new Tank(200 + 50 * i, 100, false, 1); enemyTanks.add(enemyTank); tankNum++; } // 加入城墙 for (int i=0;i<wallNum;i++){ Wall wall = new Wall(100+i*18,300); wallList.add(wall); wall = new Wall(100+i*18,318); wallList.add(wall); } // 启动重画线程 PaintThread pt = new PaintThread(); Thread t = new Thread(pt); t.start(); } /** * 显示窗体 */ public void showUI(){ this.setTitle("我打坦克大战"); this.setLocation(200, 100); this.setSize(WINDOW_WIDTH, WINDOW_HEIGHT); this.setResizable(false); this.addWindowListener( new WindowAdapter() { public void windowClosing(WindowEvent e) { setVisible(false); System.exit(0); } } ); this.setBackground(Color.BLACK); this.setVisible(true); g=this.getGraphics(); } /** * 重写update方法,先将窗体上的图形画在图片对象上,再一次性显示 */ // public void update(Graphics g) { // if (offScreenImage == null) { // offScreenImage = this.createImage(WINDOW_WIDTH, // WINDOW_HEIGHT); // } // Graphics gOffScreen = offScreenImage.getGraphics();// 获取图片内的所有图形,形成虚拟窗口 // Color c = gOffScreen.getColor(); // gOffScreen.setColor(Color.BLACK);// 设置屏幕窗口的颜色 // gOffScreen.fillRect(0, 0, WINDOW_WIDTH,WINDOW_HEIGHT); // gOffScreen.setColor(c); // paint(gOffScreen); // 把虚拟窗口画在图片上 // g.drawImage(offScreenImage, 0, 0, null);// 把图片画在窗口上 // // drawTank(); // // } /** * 用线程重画,每隔一段时间重画窗体 * @author Magci * */ public class PaintThread implements Runnable { /** * 每隔REFRESH_RATE毫秒重画一次窗体 */ public void run() { while (true) { repaint(); try { Thread.sleep(REFRESH_RATE); } catch (InterruptedException e) { e.printStackTrace(); } } } } public void keyPressed(KeyEvent e) { if(e.getKeyCode()==java.awt.event.KeyEvent.VK_UP){//如果按键为向上键 myTank.move(0);//向上 } if(e.getKeyCode()==java.awt.event.KeyEvent.VK_DOWN){//如果按键为向下键 myTank.move(1); } if(e.getKeyCode()==java.awt.event.KeyEvent.VK_LEFT){//如果按键为向左键 myTank.move(2); } if(e.getKeyCode()==java.awt.event.KeyEvent.VK_RIGHT){//如果按键为向右键 myTank.move(3); }if(e.getKeyCode()==java.awt.event.KeyEvent.VK_SPACE){ myTank.fire(g); } //drawTank(); } public void keyReleased(KeyEvent e) { } public void keyTyped(KeyEvent e) { } }
能是能画出一个坦克,但是闪得你受不了,
其主要的原因是,要了解paint闪烁的原因
每一个paint的过后,程序会自行的调用repaint的方法,但是repaint方法中的绘制有的分配一个与原来窗口一样的的内存空间,但里面是没有存储东西的,所以一次次的paint,repaint的交替就会产生闪烁
解决方法:双缓冲技术的工作原理:先在内存中分配一个和窗口一样大的空间(在内存中的空间我门是看不到的),然后利用getGraphics()方法去获得该空间并将它全部一次性的显示到屏幕上.这样显示出来就非常的流畅了.避免了闪烁效果.
package My4; import java.awt.*; import java.awt.event.*; import java.util.ArrayList; import java.util.List; public class TankClient extends Frame implements java.awt.event.KeyListener{ public static final int WINDOW_WIDTH = 800; public static final int WINDOW_HEIGHT = 600; //窗体刷新率 public static final int REFRESH_RATE = 50; Image offScreenImage = null; //屏幕画布 static java.awt.Graphics g = null; //玩家坦克 static Tank myTank = null; // 坦克起始位置左上角坐标 public static final int Tank_X = 385; public static final int Tank_Y = 550; //敌方坦克同时出现的最大数量 private int tankMaxNum = 10; //当前敌方坦克数量 private int tankNum = 0; //存放敌方坦克的容器 static List<Tank> enemyTanks = new ArrayList<Tank>(); //存放墙的容器 static List<Wall> wallList = new ArrayList<Wall>(); static int wallNum=35; //草容器 static List<Grass> grassList = new ArrayList<Grass>(); //方块容器 static List<Stone> stoneList = new ArrayList<Stone>(); static boolean GameBegin=false; public static void main(String[] args) { // 创建一个窗体 TankClient main =new TankClient(); main.showUI(); main.addKeyListener(main); } public void showUI() { this.setTitle("我打坦克大战"); this.setBounds(200, 100, WINDOW_WIDTH, WINDOW_HEIGHT); this.setResizable(false); this.setBackground(Color.GREEN); this.addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent e) { setVisible(false); System.exit(0); } }); this.setVisible(true); g=this.getGraphics(); } public void Init(){ if(GameBegin==false){ return; } //加入我方坦克 myTank = new Tank(Tank_X,Tank_Y,true,0); myTank.draw(g); //清空各种容器 enemyTanks.clear(); wallList.clear(); grassList.clear(); stoneList.clear(); //加入敌方坦克 for (int i = 0; i < tankMaxNum; i++) { Tank enemyTank = new Tank(200 + 50 * i, 50, false, 1); enemyTanks.add(enemyTank); enemyTanks.get(i).Go(); tankNum++; } //加入城墙 for (int i=0;i<10;i++){ Wall wall = new Wall(100+i*18,100); wallList.add(wall); wall = new Wall(100+i*18,118); wallList.add(wall); } for (int i=0;i<16;i++){ Wall wall = new Wall(450+i*18,100); wallList.add(wall); wall = new Wall(450+i*18,118); wallList.add(wall); } for (int i=1;i<=13;i++){ Wall wall = new Wall(450+18*7,100+i*18); wallList.add(wall); wall = new Wall(450+18*8,100+i*18); wallList.add(wall); } for (int i=1;i<=2;i++){ Wall wall = new Wall(100-18*i,136); wallList.add(wall); wall = new Wall(100-18*i,154); wallList.add(wall); } for (int i=1;i<=2;i++){ Wall wall = new Wall(100-18*i,172); wallList.add(wall); wall = new Wall(100-18*i,180); wallList.add(wall); } for (int i=0;i<8;i++){ Wall wall = new Wall(100+i*18,198); wallList.add(wall); wall = new Wall(100+i*18,216); wallList.add(wall); } for (int i=8;i<=9;i++){ Wall wall = new Wall(100+18*i,234); wallList.add(wall); wall = new Wall(100+18*i,252); wallList.add(wall); } for (int i=8;i<=9;i++){ Wall wall = new Wall(100+18*i,270); wallList.add(wall); wall = new Wall(100+18*i,288); wallList.add(wall); } for (int i=-2;i<8;i++){ Wall wall = new Wall(100+i*18,306); wallList.add(wall); wall = new Wall(100+i*18,324); wallList.add(wall); } for (int i=-2;i<35;i++){ Wall wall = new Wall(100+i*18,400); wallList.add(wall); wall = new Wall(100+i*18,418); wallList.add(wall); } //画草 for (int i=1;i<=13;i++){ Grass grass = new Grass(250+18*7,100+i*18); grassList.add(grass); grass = new Grass(250+18*8,100+i*18); grassList.add(grass); } for (int i=1;i<=13;i++){ Grass grass = new Grass(250+18*5,100+i*18); grassList.add(grass); grass = new Grass(250+18*6,100+i*18); grassList.add(grass); } //画出石头 for (int i=-2;i<35;i++){ Stone stone = new Stone(100+i*18,382); stoneList.add(stone); stone = new Stone(100+i*18,364); stoneList.add(stone); } // 启动重画线程 PaintThread pt = new PaintThread(); Thread t = new Thread(pt); t.start(); } /** * 绘制方法 */ public void paint(Graphics g) { g.setColor(Color.BLACK);//设置黑色背景 g.fillRect(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT); if(GameBegin==false){//游戏开始提示标语 g.setColor(Color.WHITE); g.drawString("请按F1开始游戏", 100, 100); } if(myTank==null) return ; myTank.draw(g); myTank.draw(g); for (int i=0;i<enemyTanks.size();i++){ enemyTanks.get(i).draw(g); } //画出城墙 for (int i=0;i<wallList.size();i++){ wallList.get(i).draw(g); } //画出草地 for (int i=0;i<grassList.size();i++){ grassList.get(i).draw(g); } //画出钢快 for (int i=0;i<stoneList.size();i++){ stoneList.get(i).draw(g); } } // 重写update方法,先将窗体上的图形画在图片对象上,再一次性显示 public void update(Graphics g) { if (offScreenImage == null) { offScreenImage = this.createImage(WINDOW_WIDTH, WINDOW_HEIGHT); } Graphics gImage = offScreenImage.getGraphics(); Color c = gImage.getColor(); gImage.setColor(Color.GREEN); gImage.fillRect(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT); gImage.setColor(c); paint(gImage); g.drawImage(offScreenImage, 0, 0, null); } // 用线程重画,每隔一段时间重画窗体 private class PaintThread implements Runnable { public void run() { while (true) { repaint(); try { Thread.sleep(REFRESH_RATE); } catch (InterruptedException e) { e.printStackTrace(); } } } } public void keyPressed(KeyEvent e) { if(e.getKeyCode()==java.awt.event.KeyEvent.VK_UP){//如果按键为向上键 myTank.move(0);//向上 } if(e.getKeyCode()==java.awt.event.KeyEvent.VK_DOWN){//如果按键为向下键 myTank.move(1); } if(e.getKeyCode()==java.awt.event.KeyEvent.VK_LEFT){//如果按键为向左键 myTank.move(2); } if(e.getKeyCode()==java.awt.event.KeyEvent.VK_RIGHT){//如果按键为向右键 myTank.move(3); }if(e.getKeyCode()==java.awt.event.KeyEvent.VK_SPACE){ myTank.fire(); }if(e.getKeyCode()==java.awt.event.KeyEvent.VK_F1){//游戏开始 GameBegin=true; Init(); } //drawTank(); } public void keyReleased(KeyEvent e) { } public void keyTyped(KeyEvent e) { } }
就不会再次闪烁了~
主要就是重写了update方法,制定了其刷新的方式~
原理:
1.建立一个Image对象DbBuffer,通过DbBuffer=createrImage(int width,int height)来在内存中开辟一个长为width 宽为heithr空间.次空间的大小可以和你动画窗口的大小保持一致,也可以利用getwidth()和getheight()来获得动画窗口的大小.
2.建立一个Graphics 对象GraImage通过GraImage=DbBuffer.getGraphics();去把要绘制的对象并存放到分配好的内存空间中.
3.利用paint(GraImage);将其全部绘制带内存之中,最后调用我门的paint(Graphics g)方法中的g.drawImage(DbBuffer,0,0,null)将DbBuffer全部一次性的绘制到我门的动画窗口,然后把我门内存中分配的空间窗口关闭调用dispose()方法.
// 重写update方法,先将窗体上的图形画在图片对象上,再一次性显示 public void update(Graphics g) { if (offScreenImage == null) { offScreenImage = this.createImage(WINDOW_WIDTH, WINDOW_HEIGHT); } Graphics gImage = offScreenImage.getGraphics(); Color c = gImage.getColor(); gImage.setColor(Color.GREEN); gImage.fillRect(0, 0, WINDOW_WIDTH, WINDOW_HEIGHT); gImage.setColor(c); paint(gImage); g.drawImage(offScreenImage, 0, 0, null); }
发表评论
-
《动感地下城》,让宅男变猛男
2011-10-03 11:11 1600动感地下城 写在之前: 好久都没有发表文章了, ... -
RGP游戏的非主流应用——虚拟地图
2011-06-30 01:02 6115RGP游戏的非主流应用——虚拟地图 写在之前: 本 ... -
一个项目完整制作过程的分享
2011-05-22 14:23 8217St书店管理系统 写在文章之前: 本项 ... -
一个项目完整制作过程的分享
2011-05-22 14:18 0St书店管理系统 写在文章之前: 本项 ... -
eclipse下的1245个图标
2011-05-19 17:23 2157eclipse下的1245个图标 大家随便拿写 ... -
超级详细解析——字模
2011-05-17 21:09 6729超级详细解析——字模 一、简介 汉字库: 即 ... -
JavaSE 的MV模式(国际化)
2011-05-14 12:13 2771JavaSE 的 MV 模式 ... -
分享一个绿色版本的 JAR TO EXE工具(附上一个视频教程)
2011-03-15 14:05 1564如题,使用灰常简单 附上一个视频教程: -
偶然玩分形 java测试
2011-03-09 13:54 2601分形世界 我从拉丁文形容词fractus(分裂的)造出了fr ... -
Oracle, SQL Server, My SQL数据分页查询语句汇总
2011-03-05 23:16 2026之前一直在学习SQL Server,然而其的分 ... -
小议MVC模式开发
2011-02-27 22:08 1288做web项目是所经常提到的mvc模式。 MVC是三个 ... -
菜鸟 利用VE、截图 快速打造仿QQ绚丽界面
2011-02-12 02:52 2955自绘菜鸟 利用VE、截图 快速打造仿QQ绚丽界面 ... -
JAVA的几个重要概念小总结
2011-01-17 12:20 1409JAVA的几个重要概念小总结 方法声明: 写一个方 ... -
几个文字编码小总结
2011-01-17 12:03 1203ASCII 我们入门学习是最 ... -
超详细解释常用网络命令
2011-01-15 12:10 1582常用网络命令 PING请求 Ping www.google ... -
达通杯 赛后感想
2010-12-23 13:09 3588比赛结束两三天了今天才抽出了时间写写心得。 其实想想自己 ... -
自己动手实现高压缩比压缩软件 超详细解释(LZW算法)
2010-12-07 17:27 5003Lzw 针对大量的子串多次重复出现的压缩 之前 ... -
自己动手写压缩软件,超详细解释(哈夫曼实现)
2010-12-04 10:58 19708说到文件压缩大家很容 ... -
JAVA BMP解码 超详细解释
2010-11-21 23:02 13401首先,对于BMP 格式的图片大家都不感觉到陌生吧。 ... -
Java 挂链 Hash表 手动实现
2010-11-19 13:08 2183前些天一直在纠结的 ...
相关推荐
Java双缓冲技术是一种图形渲染优化策略,主要用于提高GUI(图形用户界面)的性能和减少屏幕闪烁,提升用户体验。在Java中,它主要应用于Swing和JavaFX等图形库。本教程将深入探讨Java双缓冲技术的原理,并通过实例...
`paintComponent()`方法的调用顺序至关重要,`super.paintComponent(g)`会开启Swing的默认双缓冲机制。 双缓冲不仅可以应用于Swing组件,还可以用于游戏开发或者其他需要频繁更新图形的场景。通过使用双缓冲,我们...
此外,Java Swing提供了更好的绘图组件JComponent,它的 paintComponent 方法已经内置了双缓冲机制,所以如果你使用Swing,无需手动实现双缓冲,只需重写`paintComponent(Graphics g)`方法进行绘图即可。 通过以上...
在这个名为"JAVA人物动画.rar"的压缩包中,我们重点探讨的是如何利用Java来实现生动的人物动画,特别是针对"java画人像"、"java人物动画"、"动漫人物java"和"双缓冲"这些标签所涉及的技术。 首先,让我们深入理解...
而双缓冲机制则在后台创建了一个额外的帧缓冲区,游戏逻辑和渲染过程都在这个隐藏的缓冲区进行,只有当一帧完全绘制完毕后,才会一次性将其复制到前台显示,从而确保用户看到的是完整、平滑的画面。 在1.0.1版本的...
在VC++环境中,利用双缓冲机制来绘制坐标系可以显著提高用户界面的平滑度和视觉效果。本文将详细讲解如何在VC++中实现双缓冲画坐标系,以及其工作原理。 首先,我们要理解双缓冲的基本概念。在单缓冲系统中,图形的...
在IT行业中,服务器端双缓冲队列是一种优化数据处理效率的设计模式,特别是在高并发和实时性要求较高的场景下。双缓冲队列(Double Buffering Queue)借鉴了图形处理中的双缓冲技术,通过两个独立的数据缓冲区来提高...
例如,Java的AWT/Swing库、C#的Windows Forms或WPF、Python的Pygame等都有相应的双缓冲机制。在提供的压缩文件"用双缓冲解决闪烁问题"中,可能包含了使用特定编程语言或库实现双缓冲的示例代码,供开发者参考学习。 ...
【标题】"利用双缓冲做的...学习这个程序,可以深入了解双缓冲机制以及如何在Java环境下实现。此外,这也提供了一个很好的案例,对于想要提升自己GUI编程技能,尤其是解决屏闪问题的人来说,这是一个很好的学习资源。
这个“双缓冲绘图实例”是一个使用Java实现的示例,旨在展示如何在GUI应用程序中有效地利用双缓冲来提高图形绘制的性能和视觉效果。 双缓冲的基本原理是将绘图操作先在一个“后台缓冲区”完成,然后一次性将整个...
这个"商业编程-源码-实例解说双缓冲.zip"压缩包文件显然包含了一些源代码示例,用于解释和展示如何在实际项目中实现双缓冲机制。下面,我们将深入探讨双缓冲的工作原理、其重要性以及如何在编程中应用。 双缓冲是一...
双缓冲机制通过创建一个后台缓冲区,将所有的绘图操作先执行在缓冲区上,然后再一次性将完成的图像刷新到屏幕上,从而避免了直接在屏幕上连续修改像素引发的闪烁。 在Windows API或Java Swing等GUI库中,`OnDraw`...
Java对象缓冲实现中间件是一种高效的数据存储和访问机制,它通过在内存中创建一个对象池来减少对硬盘或网络资源的频繁访问,从而提高应用程序的性能。这种中间件设计的核心理念是利用内存的高速访问特性,将常用或者...
而双缓冲机制则引入了两个缓冲区:一个是用于绘图的后台缓冲区,另一个是用于显示的前台缓冲区。当后台缓冲区完成绘图后,再一次性将整个图像内容复制到前台缓冲区,最后更新到屏幕上。这样,用户看到的始终是一次...
在双缓冲机制下,组件首先在后台缓冲区进行所有的绘图操作,然后一次性将完成的图像刷新到前台缓冲区,即用户看到的实际屏幕上。这种方法避免了连续更新屏幕导致的闪烁,提高了用户体验。 在`ScrollView`中应用双...
双缓冲区内存调度机制的实现可以分为两个阶段:首先,在解释器工作时,使用双缓冲区来存储指令,并对指令进行分类和优化;其次,在执行指令时,使用缓冲区中的指令来减少对外部存储器的访问次数。 5. 算法实现的...
本文将详细解释双缓冲的概念、工作原理,并提供一个简单的J2ME实现双缓冲解决屏幕闪烁的代码示例。 双缓冲是一种图形绘制优化技术,主要目的是减少屏幕闪烁和图像撕裂。在单缓冲系统中,每次图形绘制完成后立即显示...
9. **反射**:Java反射机制的使用,包括获取类信息、创建对象、访问私有成员、动态调用方法等,以及在实际开发中的应用场景。 10. **JVM**:对Java虚拟机的理解,包括内存管理(堆、栈、方法区)、垃圾回收机制、类...
Java生成密钥、保存密钥的实例源码,通过本源码可以了解到Java如何产生单钥加密的密钥(myKey)、产生双钥的密钥对(keyPair)、如何保存公钥的字节数组、保存私钥到文件privateKey.dat、如何用Java对象序列化保存私钥...
本资料包"应用源码之VIEW双缓冲与SurfaceView比较.zip"聚焦于这两者的原理及对比,旨在帮助开发者深入理解它们的工作机制,以便在实际项目中做出合适的选择。 **一、View双缓冲技术** 双缓冲是一种优化UI渲染的...