Java解決遊戲界面閃屏

一、問題描述

我們在做有關於圖形繪製方面的問題非常之多。比如,有時我們用普通的方法去繪製圖形,會產生閃屏的現象,導致我們所做的遊戲或者是別的項目效果非常差,這完全不是我們想要的結果。那麼,有沒有一種技術,實現不閃屏的效果,特別是在多線程環境下。答案是有的,這就是Java的什麼技術——雙緩衝技術。

這裏寫圖片描述

                    圖1 雙緩衝技術效果圖

二、實現方法

1、首先,我們定義一個類繼承JFrame,要自定義繪圖,我們就要實現JFrame的paint()方法,如下:

@Override
public void paint(Graphics g) {

}

2、創建一個緩衝區畫筆;

public BufferedImage(int width,
                     int height,
                     int imageType)
    width          緩衝區的寬度       
    height         緩衝區的高度
    imageType      緩衝圖形模式
// 創建一個緩衝區
BufferedImage paint = new BufferedImage(width, height,
    BufferedImage.TYPE_3BYTE_BGR);
// 得到緩衝區的畫筆
Graphics g2 = paint.getGraphics();

3、將你想要繪製的圖形繪製在緩衝區內;

// 將想要繪製的圖形繪製到緩衝區
g2.drawImage(GameImage.getInstance().game_main_bg, 0, 0, width, height,
        this);

4、將緩衝區的圖形繪製到顯示面板上;

g.drawImage(paint, 0, 0, this);

經過上面四步之後,我們就解決了閃屏的問題,現在放心大膽的去做你的遊戲吧!

三、項目源碼

由於項目過大,源碼只給出了雙緩衝技術得用法,具體的實現並沒有做。如果想驗證源碼的,可以聯繫我要項目所用到的圖片,也可以上網查找。

由於技術和能力有限,文中難免會有錯誤之處,還望指正,謝謝合作!

1、程序入口類Main.java

/**
 * 主函數入口
 * @author Admin
 *
 */
public class Main {

    public static void main(String[] args) {
        new MainGameFrame();
    }

}

2、遊戲主界面類MainGameFrame.java

/**
 * 遊戲主面板
 * 
 * @author Admin
 *
 */
public class MainGameFrame extends JFrame implements Runnable {

    private int width = 1200;
    private int height = 600;

    private BufferedImage clickGame;

    private List<PlantAutoMoveInfo> allPlant;

    private boolean isRunnableStart;

    public MainGameFrame() {

        isRunnableStart = true;

        new Thread(this).start();

        init();

        newPlant();
    }

    /**
     * 創造植物
     */
    private void newPlant() {
        allPlant = new ArrayList<PlantAutoMoveInfo>();

        //如果你想讓所有的植物都不是相同的動作,
        //你可以在PlantAutoMoveInfo去控制它切換圖片的時機

        // constructor sunflower
        PlantAutoMoveInfo plantObj = new PlantAutoMoveInfo(new Point(300, 100),
                Constants.SUNFLOWER, GameImage.getInstance().sunflower);
        allPlant.add(plantObj);
       plantObj = new PlantAutoMoveInfo(new Point(300, 250),
                Constants.SUNFLOWER, GameImage.getInstance().sunflower);
        allPlant.add(plantObj);

        // constructor pea
        plantObj = new PlantAutoMoveInfo(new Point(400, 100), Constants.PEA,
                GameImage.getInstance().pea);
        allPlant.add(plantObj);
        plantObj = new PlantAutoMoveInfo(new Point(400, 250), Constants.PEA,
                GameImage.getInstance().pea);
        allPlant.add(plantObj);

        // constructor nut
        plantObj = new PlantAutoMoveInfo(new Point(500, 100), Constants.NUT,
                GameImage.getInstance().nut);
        allPlant.add(plantObj);
        plantObj = new PlantAutoMoveInfo(new Point(500, 250), Constants.NUT,
                GameImage.getInstance().nut);
        allPlant.add(plantObj);
    }

    /**
     * 繪製圖像
     */
    @Override
    public void paint(Graphics g) {
        // 創建一個緩衝區
        BufferedImage paint = new BufferedImage(width, height,
                BufferedImage.TYPE_3BYTE_BGR);
        // 得到緩衝區的畫筆
        Graphics g2 = paint.getGraphics();

        // 將想要繪製的圖形繪製到緩衝區
        // draw background image
        g2.drawImage(GameImage.getInstance().game_main_bg, 0, 0, width, height,
                this);

        // 繪製植物
        for (PlantAutoMoveInfo obj : allPlant) {
            g2.drawImage(obj.getShowImage(), (int) obj.getPos().getX(),
                    (int) obj.getPos().getY(), this);
        }

        // 將緩衝區的圖形繪製到顯示面板上
        g.drawImage(paint, 0, 0, this);
    }

    private void init() {
        this.setTitle("植物大戰殭屍");
        this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        this.setLocationRelativeTo(null);
        this.setLocation(200, 200);
        this.setSize(width, height);
        this.setVisible(true);
    }

    /**
     * 當遊戲界面圖形變化時,我們要不斷的刷新顯示
     */
    @Override
    public void run() {
        while (isRunnableStart) {
            this.repaint();
            try {
                Thread.sleep(50);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

}

3、圖片獲取類GameImage.java

/**
 * 獲取圖片
 * @author Admin
 *
 */
public class GameImage {

    //plant
    public static List<BufferedImage> sunflower;
    public static List<BufferedImage> nut;
    public static List<BufferedImage> pea;

    //game main bg
    public static BufferedImage game_main_bg;

    private static GameImage instance = null;

    /**
     * 提供對外接口,並加上同步鎖
     * @return
     */
    public static GameImage getInstance() {
        if(instance == null){
            synchronized(GameImage.class){
                if(instance == null){
                    instance = new GameImage();
                }
            }
        }
        return instance;
    }


    private  GameImage(){
        sunflower = new ArrayList<BufferedImage>();
        nut = new ArrayList<BufferedImage>();
        pea = new ArrayList<BufferedImage>();

        try {
            game_main_bg = ImageIO.read(new File("image\\bk1.jpg"));

            //plant
            for(int i=1;i<=11;i++){     
                nut.add(ImageIO.read(new File("image\\plant\\p_3_0"+i+".png")));
                if(i <= 8){
                    sunflower.add(ImageIO.read(new File("image\\plant\\p_1_0"+i+".png")));
                    pea .add(ImageIO.read(new File("image\\plant\\p_2_0"+i+".png")));
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}

4、植物信息保存類PlantAutoMoveInfo.java

/**
 * 植物信息業務類
 * @author Admin
 *
 */
public class PlantAutoMoveInfo implements Runnable{

    //植物對象的座標
    private Point pos;
    //線程執行標識
    private boolean isStart;
    //將要顯示的圖片
    private BufferedImage showImage;
    //顯示圖片索引值
    private int index;
    //構造植物對象的類型
    private int type;
    //陽光每隔多少秒產生
    private int SUNTIME;
    //陽光產生剩餘時間
    private int tempTime;
    //判斷是否可以生產陽光
    private boolean isProduction;
    //產生植物動畫的所有圖片
    private List<BufferedImage> plantImage;

    public PlantAutoMoveInfo(Point pos,int type,List<BufferedImage> gameImage) {
        this.pos = pos;
        this.plantImage = gameImage;

        SUNTIME = Constants.PRODUCTION_SUNNING_TIME;
        index = 0;
        tempTime = 0;
        showImage = plantImage.get(index);
        this.type = type;
        isStart = true; 
        isProduction = false;
        new Thread(this).start();
    }


    public PlantAutoMoveInfo() {
    }

    public void stopSunflowerThread(boolean bool){
        isStart = bool;
    }

    public boolean isProduction() {
        return isProduction;
    }


    public int getType() {
        return type;
    }


    public void setType(int type) {
        this.type = type;
    }


    public void setProduction(boolean isProduction) {
        this.isProduction = isProduction;
    }

    public Point getPos() {
        return pos;
    }

    public void setPos(Point pos) {
        this.pos = pos;
    }

    public boolean isStart() {
        return isStart;
    }

    public void setStart(boolean isStart) {
        this.isStart = isStart;
    }

    public BufferedImage getShowImage() {
        return showImage;
    }

    public void setShowImage(BufferedImage showImage) {
        this.showImage = showImage;
    }


    public int getTempTime() {
        return tempTime;
    }


    public void setTempTime(int tempTime) {
        this.tempTime = tempTime;
    }

    @Override
    public void run() {
        while(isStart){
            index++;
            showImage = plantImage.get(index % plantImage.size());
            try {
                Thread.sleep(150);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            if(type == Constants.SUNFLOWER && !isProduction){
                tempTime++;
                if((tempTime * 150) >= SUNTIME * 1000){
                    isProduction = true;
                }
            }
        }
    }

}

6、常量類Constants.java

/**
 * 常量類
 * @author Admin
 *
 */
public class Constants {

    public final static int SUNFLOWER = 0x0002;
    public final static int PEA = 0x0004;
    public final static int NUT = 0x0008;

    public final static int PRODUCTION_SUNNING_TIME = 0;

}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章