http://stchou.iteye.com/blog/810701
以前都不了解双缓冲是什么个东东,
但是为了解决我的坦克大战疯狂的闪屏
后,终于对其有了一个了解。
- 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);
- }