`
stchou
  • 浏览: 202602 次
  • 性别: Icon_minigender_1
  • 来自: 长沙
社区版块
存档分类
最新评论

JAVA双缓冲机制

阅读更多

以前都不了解双缓冲是什么个东东,

 

但是为了解决我的坦克大战疯狂的闪屏

 后,终于对其有了一个了解。

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);
    }

 

分享到:
评论
2 楼 无心流泪wan 2013-09-19  
说的不错。。。。。
1 楼 malijie3414 2012-10-12  
双缓冲在马士兵的坦克视频中有用到,楼主的代码与马的视频里的代码基本一致,

相关推荐

    Java双缓冲技术原理详细讲解例子Java实用源码整理learns

    Java双缓冲技术原理详细讲解例子Java实用源码整理learns

    java源码包---java 源码 大量 实例

     Java生成密钥、保存密钥的实例源码,通过本源码可以了解到Java如何产生单钥加密的密钥(myKey)、产生双钥的密钥对(keyPair)、如何保存公钥的字节数组、保存私钥到文件privateKey.dat、如何用Java对象序列化保存私钥...

    java实现俄罗斯方块源码

    使用Java实现俄罗斯方块的源码,采用了双缓冲机制避免了闪屏问题,并实现了稳定帧频

    java中生产者和消费者模型

    理解java中生产者消费者模型,以及如何利用双缓冲机制来解决同步与死锁问题。

    JAVA上百实例源码以及开源项目源代码

     Java生成密钥、保存密钥的实例源码,通过本源码可以了解到Java如何产生单钥加密的密钥(myKey)、产生双钥的密钥对(keyPair)、如何保存公钥的字节数组、保存私钥到文件privateKey.dat、如何用Java对象序列化保存私钥...

    JAVA上百实例源码以及开源项目

     Java生成密钥、保存密钥的实例源码,通过本源码可以了解到Java如何产生单钥加密的密钥(myKey)、产生双钥的密钥对(keyPair)、如何保存公钥的字节数组、保存私钥到文件privateKey.dat、如何用Java对象序列化保存私钥...

    java源码包4

     Java生成密钥、保存密钥的实例源码,通过本源码可以了解到Java如何产生单钥加密的密钥(myKey)、产生双钥的密钥对(keyPair)、如何保存公钥的字节数组、保存私钥到文件privateKey.dat、如何用Java对象序列化保存私钥...

    java源码包3

     Java生成密钥、保存密钥的实例源码,通过本源码可以了解到Java如何产生单钥加密的密钥(myKey)、产生双钥的密钥对(keyPair)、如何保存公钥的字节数组、保存私钥到文件privateKey.dat、如何用Java对象序列化保存私钥...

    java源码包2

     Java生成密钥、保存密钥的实例源码,通过本源码可以了解到Java如何产生单钥加密的密钥(myKey)、产生双钥的密钥对(keyPair)、如何保存公钥的字节数组、保存私钥到文件privateKey.dat、如何用Java对象序列化保存私钥...

    词法分析器

    C实现 JAVA语言词法分析器,关键字、运算符更新到JDK7标准,包含几个简单的错误检查,双缓冲区机制

    成百上千个Java 源码DEMO 4(1-4是独立压缩包)

    Java生成密钥的实例 1个目标文件 摘要:Java源码,算法相关,密钥 Java生成密钥、保存密钥的实例源码,通过本源码可以了解到Java如何产生单钥加密的密钥(myKey)、产生双钥的密钥对(keyPair)、如何保存公钥的字节数组、...

    基于Java的视频会议系统(软件程序+WORD论文文档).zip

    GetOutputStream方法得到一个输出流,客户端Socket对象上的getOutputStream方法返回的输出流就是将要发送到服务器端的数据流,(其实是一个缓冲区,暂时存储将要发送过去的数据)。 多播通信是一类特殊的IP地址(D类...

    java内部学习笔记.docx

    Java技术基础 4 1.1编程语言 4 1.2 Java的特点 4 1.3 Java开发环境 4 1.4 Java开发环境配置 5 1.5 Linux命令与相关知识 5 1.6 Eclipse/Myeclipse程序结构 6 Java语言基础 7 2.1基础语言要素 7 ...5.15双缓冲队列 68

    Java版计算器

    不支持双缓冲。 &lt;2&gt;Swing构件都是AWT的Cotainer类的直接或间接子类。AWT是Swing的基础,Swing是AWT的扩展。Swing构件均以“J”开头,Swing是扩展AWT后得到的轻量级构件,不依赖于操作系统的支持,显示效果在不同...

    成百上千个Java 源码DEMO 3(1-4是独立压缩包)

    Java生成密钥的实例 1个目标文件 摘要:Java源码,算法相关,密钥 Java生成密钥、保存密钥的实例源码,通过本源码可以了解到Java如何产生单钥加密的密钥(myKey)、产生双钥的密钥对(keyPair)、如何保存公钥的字节数组、...

    飞机大战.rar

    只是一个使用java开发的窗体小游戏,该游戏通过键盘操作飞机,该车程序可以加深你对java绘图机制的了解以及了解双缓冲机制、重绘等技术

    java编程基础,应用与实例

    18.7 双缓冲 338 18.8 巩固练习 340 第19章 Applet 342 19.1 制作Applet 342 19.2 Applet的生命周期 344 19.3 Applet的多媒体处理 346 19.3.1 图像处理 346 19.3.2 声音处理 347 19.4 Applet参数...

    java 面试题 总结

    assertion(断言)在软件开发中是一种常用的调试方式,很多开发语言中都支持这种机制。在实现中,assertion就是在程序中的一条语句,它对一个boolean表达式进行检查,一个正确程序必须保证这个boolean表达式的值为...

    Java典型模块

    13.2.3 实现窗口类——通过双缓冲技术 13.3 知识点扩展——画图的基础知识 13.3.1 画图的基础知识 13.3.2 各种类型对象的绘制 13.4 小结 第14章 指针时钟项目(Swing组件+时间算法) 14.1 指针时钟原理 14.1.1 项目...

Global site tag (gtag.js) - Google Analytics