贪吃蛇小游戏 java双缓冲防闪屏

最近跟着b站视频手敲了一遍贪吃蛇小游戏的代码,虽然简单但是写完运行出来还是挺有成就感的,而且里面还涉及到有个jframe重绘闪动的问题,对遇到该问题的朋友或许有帮助。

游戏界面截图:

 代码目录结构

 代码简单讲解:

1、GameWin.java :继承JFrame,用于布局游戏界面,作为游戏启动的入口

ps:刚做完的时候界面一直频繁闪动,查了很多方法都没有处理好,最后才注意到在重写paint方法时,没有注释掉super.paint(g)

当然只注释掉这个还是不够的,还需要增加一个参数(Image offScreenImage = null;)来将界面上的小元素整合到一张图片上,实现双缓冲防止闪屏。

具体实现逻辑在paint方法中:注意了,后续的g都要替换成gImage,也就是咱们新生成的图片缓存

 代码如下

package com.example.snake;

import javax.swing.*;
import java.awt.*;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.util.ArrayList;
import java.util.List;

/**
 * Author: wangwang
 * Date: 2022/11/1
 */
public class GameWin extends JFrame {

    //游戏状态 0:未开始 1:游戏中 2:暂停 3:失败 4:通关 5:失败后重新开始 6:下一关
    public static int state = 0;

    public int score = 0;
    int winWidth = 800;
    int winHeight = 600;



    HeadObj headObj = new HeadObj(GameUtils.rightImg,60,570,this);

    public List<BodyObj> bodyObjList = new ArrayList<>();

    public FoodObj foodObj = new FoodObj().getFood();

    Image offScreenImage = null;

    public void launch(){
        this.setVisible(true);
        this.setSize(winWidth,winHeight);
        this.setLocationRelativeTo(null);
        this.setTitle("snake");

        bodyObjList.add(new BodyObj(GameUtils.bodyImg,30,570,this));
        bodyObjList.add(new BodyObj(GameUtils.bodyImg,0,570,this));

        this.addKeyListener(new KeyAdapter() {
            @Override
            public void keyPressed(KeyEvent e) {
                if(e.getKeyCode() == KeyEvent.VK_SPACE){
                    switch (state){
                        case 0:
                        case 2:
                            state = 1;
                            break;
                        case 1:
                            state = 2;
                            repaint();
                            break;
                        case 3:
                            state = 5;
                            break;
                        case 4:
                            state = 6;
                            break;
                        default:
                            break;
                    }
                }
            }
        });

        while (true){
            if(state == 1){
                repaint();
            }
            if(state == 5){
                state = 0;
                resetGame();
            }
            if(state == 6 && GameUtils.level != 3){
                state = 1;
                GameUtils.level++;
                resetGame();
            }
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    @Override
    public void paint(Graphics g) {
        if(offScreenImage == null){
            offScreenImage = this.createImage(winWidth,winHeight);
        }
        Graphics gImage = offScreenImage.getGraphics();
        gImage.setColor(Color.gray);
        gImage.fillRect(0,0,winWidth,winHeight);
        gImage.setColor(Color.black);
        for (int i = 0; i <= 20; i++) {
            gImage.drawLine(0,i * 30,600,i * 30);
            gImage.drawLine(i * 30,0,i * 30,600);
        }
        for (int i = bodyObjList.size() - 1 ; i >= 0; i--){
            bodyObjList.get(i).paintSelf(gImage);
        }
        headObj.paintSelf(gImage);

        foodObj.paintSelf(gImage);

        GameUtils.drawWord(gImage,"第"+GameUtils.level+"关",Color.orange,40,650,260);

        GameUtils.drawWord(gImage,score+"分",Color.blue,50,650,330);

        gImage.setColor(Color.GRAY);
        prompt(gImage);

        g.drawImage(offScreenImage,0,0,null);
    }

    void prompt(Graphics g){
        if(state == 0){
            g.fillRect(120,240,400,70);
            GameUtils.drawWord(g,"按下空格开始游戏",Color.yellow,35,150,290);
        }
        if(state == 2){
            g.fillRect(120,240,400,70);
            GameUtils.drawWord(g,"按下空格继续游戏",Color.yellow,35,150,290);
        }
        if(state == 3){
            g.fillRect(120,240,400,70);
            GameUtils.drawWord(g,"游戏失败!按下空格重新进入游戏",Color.red,35,150,290);
        }
        if(state == 4){
            g.fillRect(120,240,400,70);
            if(GameUtils.level == 3){
                GameUtils.drawWord(g,"恭喜通关!",Color.GREEN,35,150,290);
            }else {
                GameUtils.drawWord(g,"恭喜!按下空格进入下一关",Color.GREEN,35,150,290);
            }

        }
    }

    //游戏重置
    void resetGame(){
        //关闭当前窗口
        this.dispose();
        //开启新窗口
        String[] args = {};
        main(args);
    }
    public static void main(String[] args) {
        GameWin gameWin = new GameWin();
        gameWin.launch();
    }
}

2、GameObj.java:游戏中所有物体的父类,例如蛇头,蛇身体、食物,其中对应HeadObj.java、BodyObj.java、FoodObj.java,这三个类都继承GameObj。

详细代码如下:

package com.example.snake;

import java.awt.*;

/**
 * Author: wangwang
 * Date: 2022/11/1
 */
public class GameObj {

    Image img;

    int x;
    int y;

    int width = 30;
    int height = 30;

    GameWin frame;

    public Image getImg() {
        return img;
    }

    public void setImg(Image img) {
        this.img = img;
    }

    public int getX() {
        return x;
    }

    public void setX(int x) {
        this.x = x;
    }

    public int getY() {
        return y;
    }

    public void setY(int y) {
        this.y = y;
    }

    public int getWidth() {
        return width;
    }

    public void setWidth(int width) {
        this.width = width;
    }

    public int getHeight() {
        return height;
    }

    public void setHeight(int height) {
        this.height = height;
    }

    public GameWin getFrame() {
        return frame;
    }

    public void setFrame(GameWin frame) {
        this.frame = frame;
    }

    public GameObj() {
    }

    public GameObj(Image img, int x, int y, GameWin frame) {
        this.img = img;
        this.x = x;
        this.y = y;
        this.frame = frame;
    }

    public GameObj(Image img, int x, int y, int width, int height, GameWin frame) {
        this.img = img;
        this.x = x;
        this.y = y;
        this.width = width;
        this.height = height;
        this.frame = frame;
    }

    public void paintSelf(Graphics g){
        g.drawImage(img,x,y,width,height,null);
    }
}
package com.example.snake;

import java.awt.*;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.util.List;

/**
 * Author: wangwang
 * Date: 2022/11/1
 */
public class HeadObj extends GameObj{

    //方向
    private String direction = "right";

    public String getDirection() {
        return direction;
    }

    public void setDirection(String direction) {
        this.direction = direction;
    }

    public HeadObj(Image img, int x, int y, GameWin frame) {
        super(img, x, y, frame);
        this.frame.addKeyListener(new KeyAdapter() {
            @Override
            public void keyPressed(KeyEvent e) {
                changeDirection(e);
            }
        });
    }

    public void changeDirection(KeyEvent e){
        switch (e.getKeyCode()){
            case KeyEvent.VK_A:
                if(!"right".equals(direction)){
                    direction = "left";
                    img = GameUtils.leftImg;
                }
                break;
            case KeyEvent.VK_D:
                if(!"left".equals(direction)){
                    direction = "right";
                    img = GameUtils.rightImg;
                }
                break;
            case KeyEvent.VK_W:
                if(!"down".equals(direction)){
                    direction = "up";
                    img = GameUtils.upImg;
                }
                break;
            case KeyEvent.VK_S:
                if(!"up".equals(direction)){
                    direction = "down";
                    img = GameUtils.downImg;
                }
                break;
            default:
                break;
        }
    }
    public void move(){

        List<BodyObj> bodyObjList = this.frame.bodyObjList;
        for(int i = bodyObjList.size() - 1; i >= 1; i--){
            bodyObjList.get(i).x = bodyObjList.get(i-1).x;
            bodyObjList.get(i).y = bodyObjList.get(i-1).y;
            if(this.x == bodyObjList.get(i).x && this.y == bodyObjList.get(i).y){
                //失败
                GameWin.state = 3;
            }
        }
        bodyObjList.get(0).x = this.x;
        bodyObjList.get(0).y = this.y;

        switch (direction){
            case "up":
                y -= height;
                break;
            case "down":
                y +=height;
                break;
            case "left":
                x -= width;
                break;
            case "right":
                x += width;
                break;
            default:
                break;

        }
    }
    @Override
    public void paintSelf(Graphics g) {
        super.paintSelf(g);

        FoodObj foodObj = this.frame.foodObj;

        Integer newX = null;
        Integer newY = null;
        if(this.x == foodObj.x && this.y == foodObj.y){
            this.frame.foodObj = foodObj.getFood();
            BodyObj lastBody = this.frame.bodyObjList.get(this.frame.bodyObjList.size() -1);
            newX = lastBody.x;
            newY = lastBody.y;
            this.frame.score++;
        }

        if(this.frame.score >= 3){
            GameWin.state = 4;
        }
        move();

        if(newX != null && newY != null){
            this.frame.bodyObjList.add(new BodyObj(GameUtils.bodyImg,newX,newY,this.frame));
        }

        //越界处理
        if(x < 0){
            x = 570;
        }else if(x >570){
            x = 0;
        }
        if(y < 30){
            y = 570;
        }else if(y > 570){
            y = 30;
        }
    }
}
package com.example.snake;

import java.awt.*;

/**
 * Author: wangwang
 * Date: 2022/11/1
 */
public class BodyObj extends GameObj{

    public BodyObj(Image img, int x, int y, GameWin frame) {
        super(img, x, y, frame);
    }

    @Override
    public void paintSelf(Graphics g) {
        super.paintSelf(g);
    }
}
package com.example.snake;

import java.awt.*;
import java.util.Random;

/**
 * Author: wangwang
 * Date: 2022/11/2
 */
public class FoodObj extends GameObj{

    Random r = new Random();

    public FoodObj() {
        super();
    }

    public FoodObj(Image img, int x, int y, GameWin frame) {
        super(img, x, y, frame);
    }

    public FoodObj getFood(){
        return new FoodObj(GameUtils.foodImg,r.nextInt(20)*30,(r.nextInt(19)+1)*30,this.frame);
    }
    @Override
    public void paintSelf(Graphics g) {
        super.paintSelf(g);
    }
}

3、GameUtils.java是工具类,主要用来获取界面内物品的图片,生成界面内文字

package com.example.snake;

import java.awt.*;

/**
 * Author: wangwang
 * Date: 2022/11/1
 */
public class GameUtils {

    public static Image upImg = Toolkit.getDefaultToolkit().getImage("img/up.png");
    public static Image downImg = Toolkit.getDefaultToolkit().getImage("img/down.png");
    public static Image leftImg = Toolkit.getDefaultToolkit().getImage("img/left.png");
    public static Image rightImg = Toolkit.getDefaultToolkit().getImage("img/right.png");

    public static Image bodyImg = Toolkit.getDefaultToolkit().getImage("img/body.png");

    public static Image foodImg = Toolkit.getDefaultToolkit().getImage("img/food.png");

    public static int level = 1;

    public static void  drawWord(Graphics g, String str, Color color, int fontSize, int x, int y){
        g.setColor(color);
        g.setFont(new Font("仿宋",Font.BOLD,fontSize));
        g.drawString(str,x,y);

    }
}

4、界面静态资源存放在img下,大家也可以去我的资源列表下载完整代码,也可以自己找图片代替。

猜你喜欢

转载自blog.csdn.net/knowwait/article/details/127655333