Java编程---贪吃蛇游戏

目标:

制作使用Java语言编写贪吃蛇小游戏,实现基本贪吃蛇功能。更深刻体会Java语言的应用。

功能简介:

  • 在本游戏中,开始游戏前有设置项可以选择。首先,可以选择是否显示网卡。其次,设置地图,共提供了三种选择,地图1 是默认地图,地图2可选,还有个随机地图,贵随机获取40个坐标作为石头。最后,可以设置蛇的速度,提供了三种速度。
  • 在游戏过程中,蛇吃到自己,或者碰到石头,以及当吃完所有食物没有地方再可以生成食物时,蛇都会死亡,并弹出提示框,显示得分。在程序中还提供了一些快捷键可以使用。Shift:开始新游戏,空格键:暂停继续,方向键:控制蛇的方向等。
  • 在游戏过程中,每次吃到一个食物,分数都会更新显示。游戏还会记录历史最高分。也会显示当前蛇移动的速度。

这里写图片描述

图:游戏主界面

游戏框架分析:

  1. Snake,Food,Ground:这是三个类,每个类里面完成各自的方法。如,蛇有蛇初始化,蛇吃食物,蛇运动等方法。食物类有判断蛇是否吃到食物,尝试食物的方法。石头类有产生石头,判断蛇是否撞到石头等方法。这是三个实体类。
  2. Controller控制类:所有控制游戏,以及逻辑上的方法在这里面实现。比如,控制蛇的方向,开始新游戏等方法。
  3. SnakeListener蛇监听类:一个接口,用来监听蛇是否运动。
  4. GamePanel游戏界面类:用来显示游戏的画面。
  5. Global全局类:用来存放全局变量,如游戏界面的宽度,高度。
  6. MainWindow类:这是游戏的主窗体。各种按键事件在这里面实现。
  7. Game类:游戏的主方法,用来开始程序。

这里写图片描述

图:游戏结构图

代码实现及解释:

有关蛇的代码实现:

package snake.entities;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Point;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Set;

import snake.listener.SnakeListener;
import snake.util.Global;

public class Snake {

    //定义方向变量,用来控制蛇的方向
    public static final int UP = -1;
    public static final int DOWN = 1;
    public static final int LEFT = 2;
    public static final int RIGHT = -2;
    /*
     *    定义一个旧方向,和新方向。用来在改变方向时
     * 判断新方向与旧方向是否相同,如果相同则说明
     * 是无效方向,忽略。如果不同方向改变 
     */
    private int oldDirection, newDirection;

    Ground ground = new Ground();
    //定义一个坐标,用来存放食物坐标
    public Point point = null;
    //存放蛇身体总长度,占用坐标个数
    public int snakeBodyCount;
    private Point oldTail;//存放尾巴的坐标
    private boolean life; //判断蛇是否活着
    private boolean pause; //蛇是否暂停
    private boolean isPause; //每次开开局蛇为暂停状态
    public boolean isDie; //蛇是否死亡
    public int speed = 500; //初始化蛇速度: 500ms/格

    //存放蛇身体节点坐标
    private LinkedList<Point> body =
            new LinkedList<Point>();
    //定义蛇监听列表
    private Set<SnakeListener> listener =
            new HashSet<SnakeListener>();

    //构造方法,进行蛇的初始化
    public Snake() {
        init();
    }
    /*
     * 初始化蛇的位置,让蛇头出现在游戏界面中心,
     */
    public void init() {
        int x = Global.WIDTH/ 2 - 3;
        int y = Global.HEIGHT / 2 ;
        //初始化蛇,给蛇添加三个节点
        for(int i = 0; i < 3; i++) {
            body.addLast(new Point(x--, y));
        }
        //初始化方向,向右
        oldDirection = newDirection = RIGHT;
        life = true;
        pause = false;
        isPause = true;

    }
    /*
     * 蛇移动,先判断新旧方向是否相同,相同则忽略
     * 不同,进行改变方向。蛇移动,通过添加一个头节点,
     * 去除一个最后一个节点,达到移动的目的
     */
    public void move() {
        if (!(oldDirection + newDirection == 0)) {
            oldDirection = newDirection;
        }
        //去尾
        oldTail = body.removeLast();
        int x = body.getFirst().x;
        int y = body.getFirst().y;
        switch(oldDirection) {
        case UP: //向上移动
            y--;
            //到边上了可以从另一边出现 
            if (y < 0) {
                y = Global.HEIGHT - 1;
            }
            break;
        case DOWN:
            y++;
            //到边上了可以从另一边出现 
            if (y >= Global.HEIGHT) {
                y = 0;
            }
            break;
        case LEFT:
            x--;
            if (x < 0) {
                x = Global.WIDTH - 1;
            }
            break;
        case RIGHT:
            x++;
            if (x >= Global.WIDTH) {
                x = 0;
            }
            break;

        }
        //记录蛇头的坐标
        Point newHead = new Point(x, y);
        //加头
        body.addFirst(newHead);
    }
    //蛇改变方向
    public void chanceDirection(int direction) {
        newDirection = direction;

    }
    //蛇吃食物
    public void eatFood() {
        //通过添加删去的最后的尾节点,达到吃食物的目的
        body.addLast(oldTail);

    }

    //判断蛇是否吃到身体
    public boolean isEatBody() {
        //body.get(0)存放的为蛇头的坐标,
        //所有要排除蛇头,从i=1开始比较
        for (int i = 1; i < body.size(); i++) {
            if (body.get(i).equals(getHead())) {
                return true;
            }
        }
        return false;
    }

     /**
     * 获取蛇的snakeBody链表,让食物与蛇身不重叠
     *        body    表示蛇身体的链表
     * 返回与蛇身体坐标不重复的坐标
     */
    public Point getFood(LinkedList<Point> body) {
        //获得与石头不重叠的坐标
        point = ground.getPoint();
        while (checkPoints(body)) {
            point = ground.getPoint();
        }
        // 如果发现食物的位置和蛇身体重叠,则重新随机食物的位置
        return point;
        // 返回这个对象本身,为创建实例时带来方便
    }
    //获得食物坐标
    public Point getFoodPoint() {
        return getFood(body);
    }

    /**
     * 检查蛇身体链表中是否有一块与当前食物坐标相同
     * @return 如果有重复返回true
     * 否则返回 false
     */
    public boolean checkPoints(LinkedList<Point> body) {

        for (Point p : body)
            if (p.getX() == point.getX() && p.getY() == point.getY())
                return true;
        // 循环遍历是否有重复
        return false;
    }


    //画蛇
    public void drawMe(Graphics g) {
        for(Point p : body) {
            g.setColor(Color.PINK);//设置身体颜色
            g.fill3DRect(p.x * Global.CELL_SIZE, p.y * Global.CELL_SIZE,
                    Global.CELL_SIZE, Global.CELL_SIZE, true);
            //最后一个参数,raised 是否凸起的,true为是。
        }
        //画蛇头,覆盖蛇头位置
        g.setColor(Color.RED);
        g.fill3DRect(getHead().x * Global.CELL_SIZE, getHead().y * Global.CELL_SIZE,
                Global.CELL_SIZE, Global.CELL_SIZE, true);
    }

    //获得蛇头的坐标
    public Point getHead() {
        return body.getFirst();
    }
    //蛇死亡,生命改为false
    public void die() {
        life = false;
        isDie = true;

    }

    //一个内部类, 驱动蛇定时移动
    public class SnakerDriver implements Runnable{

        public void run() {
            //当蛇活着的时候才进行循环
            while(life) {
                //入伙蛇没有暂停才能移动
                if (!pause) {
                    move();
                    //蛇每次移动后,获得蛇身体总长度
                    getSnakeBodyCount();
                    //触发 SnakeListener 的状态改变事件
                    for(SnakeListener l : listener) {
                        l.snakeMove(Snake.this);
                    }
                    //让蛇开开始时为暂停状态
                    if (isPause) {
                        pause = true;
                        isPause = false;
                    }
                }
                try {
                    //定时移动
                    Thread.sleep(speed);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    //让蛇开始运动, 开启一个新的线程
    public void start() {
        new Thread(new SnakerDriver()).start();
    }

    //添加监听器
    public void addSnakeListener(SnakeListener l) {
        if(l != null) {
            this.listener.add(l);
        }
    }

    public void getSnakeBodyCount() {
        snakeBodyCount = body.size();
    }
    //改变蛇暂停状态
    public void changePause() {
        pause = !pause;
    }
    //清除身体所有节点
    public void bodyClear() {
        body.clear();
    }
}

石头的代码实现:

package snake.entities;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Point;
import java.util.Random;

import snake.util.Global;

public class Ground {
    /*
    *定义存放石头坐标的数组,1为石头,0为空白区域。 
    *一定要用全局静态变量,始终占用空间。否则会在产生食物时出错
    */
    private static final int rocks[][] =
            new int[Global.WIDTH][Global.HEIGHT];
    //存放石头的个数
    public int rocksCount = 0;
    //是否画网格
    private boolean isDrawGriding;
    //选择地图是使用
    public int MAP = 1;
    //构造方法,初始化地图
    public Ground() {
        init();
    }
    //清除所有石头
    public void clear() {
        for (int x = 0; x < Global.WIDTH; x++)
            for (int y = 0; y < Global.HEIGHT; y++)
                rocks[x][y] = 0;
    }
    //初始化石头位置
    public void init() {
        //清除所有石头
        clear();
        //选择地图
        switch(MAP) {
        case 1:
            map1(); //地图1
            //获得石头个数
            getScoksCount();
            break;
        case 2:
            map2(); //地图2
            getScoksCount();
            break;
        case 3:
            map3(); //随机地图
            getScoksCount();
            break;
        default :
            map1(); //默认地图1
            getScoksCount();
            break;
        }
    }
    //第一组默认地图石头坐标
    public void map1() {
        for(int x = 0; x < Global.WIDTH; x++) {
            rocks[x][0] = 1;
            rocks[x][Global.HEIGHT-1] = 1;
        }
        for(int y = 0; y < Global.HEIGHT; y++) {
            rocks[0][y] = 1;
            rocks[Global.WIDTH-1][y]  = 1;
        }
    }
    //第二个地图
    public void map2() {
        for(int x = 5; x < Global.WIDTH-5; x++) {
            rocks[x][5] = 1;
            rocks[x][Global.HEIGHT-4] = 1;
        }
        for(int y = 9; y < Global.HEIGHT-8; y++) {
            rocks[9][y] = 1;
            rocks[Global.WIDTH-9][y] = 1;
        }
    }
    //随机地图,随机获得40个坐标座位石头
    public void map3() {
        Random random = new Random();
        int x = 0,y = 0;
        for(int i = 0; i < 40; i++) {
            x = random.nextInt(Global.WIDTH);
            y = random.nextInt(Global.HEIGHT);
            rocks[x][y] = 1;
        }
    }

    //获得石头总共数目
    public void getScoksCount() {
        //每次更换地图时清零,重新获得
        rocksCount = 0;
        for (int x = 0; x < Global.WIDTH; x++)
            for (int y = 0; y < Global.HEIGHT; y++)
                if (rocks[x][y] == 1) {
                    rocksCount++;
                }
    }
    //判断蛇是否吃到石头
    //把蛇的所有节点与石头坐标进行比较如果想等则证明吃到石头
    public boolean isSnakeEatRock(Snake snake) {
        for(int x = 0; x < Global.WIDTH; x++) {
            for (int y = 0; y < Global.HEIGHT; y++) {
                if (rocks[x][y] == 1 
                        && x == snake.getHead().x  
                            && y == snake.getHead().y) {
                    return true;
                }
            }
        }
        return false;
    }
    //获得不会与石头重叠的随机坐标
    public Point getPoint() {
        Random random = new Random();
        int x = 0, y = 0;
        do{
             x = random.nextInt(Global.WIDTH);
             y = random.nextInt(Global.HEIGHT);
        }while(rocks[x][y] == 1);
        return new Point(x, y);
    }
    //画石头和网格
    public void drawMe(Graphics g) {
        drawRocks(g);
        if (isDrawGriding) {
            drawGriding(g);
        }
    }
    //画石头
    public void drawRocks(Graphics g) {
        for(int x = 0; x < Global.WIDTH; x++) {
            for (int y = 0; y < Global.HEIGHT; y++) {
                if (rocks[x][y] == 1) {
                    g.setColor(Color.DARK_GRAY);
                    g.fill3DRect(x * Global.CELL_SIZE, y * Global.CELL_SIZE,
                            Global.CELL_SIZE, Global.CELL_SIZE, true);
                }
            }
        }
    }
    //画网格
    public void drawGriding(Graphics g) {
        for(int x = 0; x < Global.WIDTH; x++) {
            for (int y = 0; y < Global.HEIGHT; y++) {
                g.setColor(Color.GRAY);
                g.fillRect(x * Global.CELL_SIZE , y * Global.CELL_SIZE,
                        1 , Global.HEIGHT * Global.CELL_SIZE);
                g.fillRect(x * Global.CELL_SIZE , y * Global.CELL_SIZE,
                        Global.HEIGHT * Global.CELL_SIZE, 1 );

            }
        }
    }
    //需要要画网格
    public void drawGriding() {
        isDrawGriding = true;
    }
    //不需要画网格
    public void notDrawGriding() {
        isDrawGriding = false;
    }
}

食物的代码实现:

package snake.entities;

import java.awt.Color;
import java.awt.Graphics;
import java.awt.Point;

import snake.util.Global;

public class Food extends Point{
    Point point = null;
    //设置食物位置坐标
    public void newFood(Point p) {
        this.point = p;
        this.setLocation(p);
    }
    //判断蛇是否吃到食物
    public boolean isSnakeEatFood(Snake snake) {
        return this.equals(snake.getHead());
    }
    //显示食物
    public void drawMe(Graphics g) {
        g.setColor(Color.GREEN);
        g.fill3DRect(point.x * Global.CELL_SIZE, point.y * Global.CELL_SIZE,
                Global.CELL_SIZE, Global.CELL_SIZE, true);
    }
}

游戏界面GamePanel代码:

package snake.view;

import java.awt.Color;
import java.awt.Graphics;

import javax.swing.JPanel;

import snake.entities.Food;
import snake.entities.Ground;
import snake.entities.Snake;
import snake.util.Global;
//游戏的显示界面
@SuppressWarnings("serial")
public class GamePanel extends JPanel{

    private Snake snake;
    private Food food;
    private Ground ground;
    //显示画面
    public void display(Snake snake, Food food, Ground ground) {
        this.snake = snake;
        this.food = food;
        this.ground = ground;       
        //会重新显示,此方法会调用下面的方法
        this.repaint();
    }
    @Override
    protected void paintComponent(Graphics g) {
        //重新显示
        //设置背景颜色
        g.setColor(Color.LIGHT_GRAY);
        g.fillRect(0, 0, Global.WIDTH * Global.CELL_SIZE, 
                Global.HEIGHT * Global.CELL_SIZE);
        if(ground != null && snake != null && food != null ) {
            this.ground.drawMe(g);
            this.snake.drawMe(g);
            this.food.drawMe(g);
        }

    }
}

蛇监听类代码:

package snake.listener;

import snake.entities.Snake;

public interface SnakeListener{
    //蛇移动的监听
    void snakeMove(Snake snake);

}

控制类代码实现:

package snake.controller;

import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;

import javax.swing.JOptionPane;

import snake.entities.Food;
import snake.entities.Ground;
import snake.entities.Snake;
import snake.listener.SnakeListener;
import snake.util.Global;
import snake.view.GamePanel;

/*控制器
* 控制Ground, Snake, Food<BR>
* 负责游戏的逻辑
* 处理按键事件
* 实现了SnakeListener接口, 可以处理Snake 触发的事件
*/
public class Controller extends KeyAdapter implements SnakeListener {

    private Snake snake;
    private Food food;
    private Ground ground;
    private GamePanel gamePanel;
    //存放当局游戏得分
    public int score = 0;
    //存放历史最高得分,这个数据通过读取文件来赋值
    public int maxScore;
    public Thread thread;

    //构造方法,初始化
    public Controller(Snake snake, Food food, Ground ground, GamePanel gamePanel) {
        super();
        this.snake = snake; 
        this.food = food;
        this.ground = ground;
        this.gamePanel = gamePanel;
        //每次开始游戏读取文件,给maxScore赋值
        readFile();
    }

    @Override
    //处理按键事件
    public void keyPressed(KeyEvent e) {

        switch (e.getKeyCode()) {
        case KeyEvent.VK_UP://向上
            snake.chanceDirection(Snake.UP);
            break;
        case KeyEvent.VK_DOWN://向下
            snake.chanceDirection(Snake.DOWN);
            break;
        case KeyEvent.VK_LEFT://向左
            snake.chanceDirection(Snake.LEFT);
            break;
        case KeyEvent.VK_RIGHT://向右
            snake.chanceDirection(Snake.RIGHT);
            break;
        case KeyEvent.VK_SPACE://空格键,实现游戏暂停
            snake.changePause();
            break;
        case KeyEvent.VK_SHIFT://Shift键,实现开始新游戏
            newGame();
            break;
        }
    }
   //处理Snake 触发的 snakeMoved 事件
    @Override
    public void snakeMove(Snake snake){
        /*
         * 判断是否还可以放下食物
         * 当身体占满全部空位,没有地方再可以放食物时
         * 游戏结束
         * Global.count : 全局游戏界面总坐标,默认1000
         * this.snake.snakeBodyCount : 蛇的身体总长度
         * ground.rocksCount : 石头总数
         * 
         */
        if (Global.count - this.snake.snakeBodyCount - ground.rocksCount < 3) {
            snake.die();
            writeMaxScore();
            //弹出消息框,提示游戏结束,并显示得分
            JOptionPane.showMessageDialog(gamePanel, "您已获得最高分,游戏结束!\n       游戏得分:"+ score);
        }
        //如果蛇吃到食物,,处理蛇吃到食物的方法,并获得新的食物
        if (food.isSnakeEatFood(snake)) {
            snake.eatFood();
            food.newFood(snake.getFoodPoint());
            this.score +=10;

        }
        //判断是否吃到石头,如果吃到石头,蛇死亡。
        if (ground.isSnakeEatRock(snake)) {
            snake.die();
            //如果游戏得分大于历史记录最高分,把当前得分赋给最高分,并写入文件
            writeMaxScore();
            //弹出消息框,提示游戏结束,并显示得分
            JOptionPane.showMessageDialog(gamePanel, "蛇撞墙死亡,游戏结束!\n       游戏得分:"+ score);
        }
        //如果蛇吃到身体也死亡
        if(snake.isEatBody()) {
            snake.die();
            writeMaxScore();
            JOptionPane.showMessageDialog(gamePanel, "蛇咬到自己死亡,游戏结束!\n       游戏得分:"+ score);
        }
        //如果蛇死亡,最后一次不刷新画面,如果刷新,蛇头会与石头重叠
        if (!(ground.isSnakeEatRock(snake) | snake.isEatBody())) {
            gamePanel.display(snake, food, ground);
        }
    }
    //开始游戏
    public void beginGame() {
        //开始游戏时,得分归零
        score = 0;
        //每次开始游戏是读取文件,获得历史最高分
        readFile();
        //获得新的食物坐标
        food.newFood(snake.getFoodPoint());
        //开始蛇驱动的线程
        snake.start();
        //开启主窗体界面刷新的线程,用来更新分数
        new Thread(thread).start();
    }

    //开始新游戏
    public void newGame() {
        //开始新游戏后,清除蛇的身体
        snake.bodyClear();
        //重新初始化蛇
        snake.init();
        //得分归零
        score = 0;
        //获得新食物坐标
        food.newFood(snake.getFoodPoint());
        /*
         * 判断蛇是否处于死亡状态,如果是,
         * 则在蛇驱动中已经跳出循环,不会触发蛇的监听
         * 此时再开始调用开始游戏,重新初始化游戏,重新监听蛇运动
         * 
         * 如果蛇不是死亡状态,则不执行开始游戏初始化,此时蛇处于正常监听状态
         * 只重新初始化蛇和食物,分数即可开始新游戏。
         */
        if (snake.isDie) {
            beginGame();
            snake.isDie = false;
        }
    }

    //读文件,获取历史最高分
    public void readFile(){
        File file = new File("MaxScore.txt");
        //如果文件不存在,文件输出流会自动创建文件
        if (!file.exists()) {
            try {
                file.createNewFile();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        //读取文件
        BufferedReader br;
        try {
            br = new BufferedReader(
                    new InputStreamReader(
                            new FileInputStream(file), "UTF-8"));
            maxScore = br.read();
            br.close();

        } catch (UnsupportedEncodingException e1) {
            e1.printStackTrace();
        } catch (FileNotFoundException e1) {
            e1.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public void writeMaxScore() {
        if (score > maxScore) {
            maxScore = score;
            writeFile();
        }
    }
    public void writeFile() {

        File file = new File("MaxScore.txt");
        //如果文件不存在,文件输出流会自动创建文件
        if (!file.exists()) {
            try {
                file.createNewFile();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        //写文件
        try {

            BufferedWriter bw = new BufferedWriter(
                    new OutputStreamWriter(
                            new FileOutputStream(file), "UTF-8"));
            bw.write(maxScore);//向文件写入最高分
            bw.close();//关闭流

        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }

    }
    //接收主窗体中刷新界面的线程
    public Thread startRefresh(Thread thread) {
        this.thread = thread;
        return this.thread;
    }
}

工具类全局代码:

package snake.util;

public class Global {
    //定义格子大小
    public static final int CELL_SIZE = 20;
    //定义边界的宽度
    public static final int WIDTH = 40;
    //定义边界的高度
    public static final int HEIGHT = 25;
    //记录游戏界面总共有多少坐标
    public static final int count = WIDTH * HEIGHT;

}

游戏主窗体代码:

package snake.view;

import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.ComponentOrientation;
import java.awt.FlowLayout;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;

import javax.swing.ButtonGroup;
import javax.swing.GroupLayout;
import javax.swing.GroupLayout.Alignment;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.JSeparator;
import javax.swing.JTextField;
import javax.swing.LayoutStyle.ComponentPlacement;
import javax.swing.SwingConstants;
import javax.swing.border.EmptyBorder;
import javax.swing.border.EtchedBorder;

import snake.controller.Controller;
import snake.entities.Food;
import snake.entities.Ground;
import snake.entities.Snake;
import snake.util.Global;
/*
 * 这些代码基本都在窗体直接涉及而成,所有代码基本可以忽略不看。
 * 只看有注释的关键地方即可。
 * 在设计中,除了让GamePanel获得焦点,其他组件都不能获得焦点。
 */

@SuppressWarnings("serial")
public class MainWindow extends JFrame{

    protected static final Object SnakeListener = null;

    private JPanel contentPane;

    Snake snake = new Snake();
    Food food = new Food();
    Ground ground = new Ground();
    public JTextField txt_score;
    private JTextField txt_speed;
    private JTextField txt_maxScore;

    GamePanel gamePanel = new GamePanel();
    Controller controller = new Controller(snake, food, ground, gamePanel);

    public MainWindow() {
        setResizable(false);
        setTitle("\u8D2A\u5403\u86C7");
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        //让窗体居中
        setLocation(getToolkit().getScreenSize().width / 2 - Global.CELL_SIZE * Global.WIDTH / 2,
                getToolkit().getScreenSize().height / 2 - Global.CELL_SIZE * Global.WIDTH / 2);

        setSize(821, 760);
        addKeyListener(controller);
        contentPane = new JPanel();
        contentPane.setFocusCycleRoot(true);
        contentPane.setFocusTraversalPolicyProvider(true);
        contentPane.setBorder(new EmptyBorder(5, 5, 5, 5));
        setContentPane(contentPane);


        JPanel panel = new JPanel();
        panel.setBorder(new EtchedBorder(EtchedBorder.LOWERED, null, null));
        panel.setFocusCycleRoot(true);
        panel.setFocusTraversalPolicyProvider(true);
        gamePanel.setFocusTraversalPolicyProvider(true);
        gamePanel.setFocusCycleRoot(true);


        gamePanel.setSize(Global.CELL_SIZE * Global.WIDTH, Global.CELL_SIZE * Global.HEIGHT);
        gamePanel.setLayout(new BorderLayout(0, 0));
        GroupLayout gl_panel = new GroupLayout(panel);
        gl_panel.setHorizontalGroup(
            gl_panel.createParallelGroup(Alignment.LEADING)
                .addGroup(gl_panel.createSequentialGroup()
                    .addComponent(gamePanel, GroupLayout.PREFERRED_SIZE, 800, GroupLayout.PREFERRED_SIZE)
                    .addContainerGap(GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
        );
        gl_panel.setVerticalGroup(
            gl_panel.createParallelGroup(Alignment.LEADING)
                .addComponent(gamePanel, GroupLayout.DEFAULT_SIZE, 505, Short.MAX_VALUE)
        );
        panel.setLayout(gl_panel);

        JPanel panel_1 = new JPanel();
        panel_1.setFocusable(false);
        GroupLayout gl_contentPane = new GroupLayout(contentPane);
        gl_contentPane.setHorizontalGroup(
            gl_contentPane.createParallelGroup(Alignment.TRAILING)
                .addGroup(gl_contentPane.createSequentialGroup()
                    .addComponent(panel, GroupLayout.PREFERRED_SIZE, 801, Short.MAX_VALUE)
                    .addGap(10))
                .addGroup(Alignment.LEADING, gl_contentPane.createSequentialGroup()
                    .addComponent(panel_1, GroupLayout.PREFERRED_SIZE, 795, Short.MAX_VALUE)
                    .addContainerGap())
        );
        gl_contentPane.setVerticalGroup(
            gl_contentPane.createParallelGroup(Alignment.LEADING)
                .addGroup(gl_contentPane.createSequentialGroup()
                    .addComponent(panel, GroupLayout.PREFERRED_SIZE, 505, GroupLayout.PREFERRED_SIZE)
                    .addPreferredGap(ComponentPlacement.RELATED)
                    .addComponent(panel_1, GroupLayout.PREFERRED_SIZE, 205, Short.MAX_VALUE)
                    .addContainerGap())
        );

        JPanel lable = new JPanel();
        lable.setFocusable(false);
        lable.setBorder(new EtchedBorder(EtchedBorder.LOWERED, null, null));

        JPanel panel_control = new JPanel();
        panel_control.setFocusable(false);
        panel_control.setBorder(new EtchedBorder(EtchedBorder.LOWERED, null, null));

        JPanel panel_set = new JPanel();
        panel_set.setFocusable(false);
        panel_set.setBorder(new EtchedBorder(EtchedBorder.LOWERED, null, null));

        JPanel panel_display = new JPanel();
        panel_display.setFocusable(false);
        panel_display.setBorder(new EtchedBorder(EtchedBorder.LOWERED, null, null));
        GroupLayout gl_panel_1 = new GroupLayout(panel_1);
        gl_panel_1.setHorizontalGroup(
            gl_panel_1.createParallelGroup(Alignment.LEADING)
                .addGroup(gl_panel_1.createSequentialGroup()
                    .addComponent(panel_set, GroupLayout.PREFERRED_SIZE,
                            302, GroupLayout.PREFERRED_SIZE)
                    .addPreferredGap(ComponentPlacement.RELATED)
                    .addGroup(gl_panel_1.createParallelGroup(Alignment.TRAILING)
                        .addComponent(panel_display, GroupLayout.PREFERRED_SIZE,
                                216, GroupLayout.PREFERRED_SIZE)
                        .addComponent(panel_control, 0, 0, Short.MAX_VALUE))
                    .addPreferredGap(ComponentPlacement.RELATED)
                    .addComponent(lable, GroupLayout.PREFERRED_SIZE,
                            GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
                    .addGap(19))
        );
        gl_panel_1.setVerticalGroup(
            gl_panel_1.createParallelGroup(Alignment.TRAILING)
                .addGroup(gl_panel_1.createSequentialGroup()
                    .addGroup(gl_panel_1.createParallelGroup(Alignment.LEADING)
                        .addComponent(panel_set, 
                                GroupLayout.DEFAULT_SIZE, 197, Short.MAX_VALUE)
                        .addGroup(gl_panel_1.createSequentialGroup()
                            .addComponent(panel_display, 
                                    GroupLayout.PREFERRED_SIZE, 123, GroupLayout.PREFERRED_SIZE)
                            .addPreferredGap(ComponentPlacement.RELATED)
                            .addComponent(panel_control, 
                                    GroupLayout.DEFAULT_SIZE, 68, Short.MAX_VALUE))
                        .addComponent(lable, GroupLayout.DEFAULT_SIZE, 195, Short.MAX_VALUE))
                    .addContainerGap())
        );

        JLabel lable_score = new JLabel("\u5F53\u524D\u5F97\u5206\uFF1A");
        lable_score.setFocusable(false);
        lable_score.setHorizontalAlignment(SwingConstants.LEFT);
        lable_score.setHorizontalTextPosition(SwingConstants.CENTER);
        lable_score.setAlignmentX(Component.CENTER_ALIGNMENT);



        txt_score = new JTextField();
        txt_score.setText("0 分");
        txt_score.setEditable(false);
        txt_score.setFocusable(false);
        txt_score.setColumns(10);

        JLabel label_maxScore = new JLabel("\u5386\u53F2\u6700\u9AD8\u5206\uFF1A");
        label_maxScore.setFocusable(false);

        txt_maxScore = new JTextField();
        txt_maxScore.setText(controller.maxScore + " 分");
        txt_maxScore.setEditable(false);
        txt_maxScore.setFocusable(false);
        txt_maxScore.setColumns(10);

        JLabel label_speed = new JLabel("\u5F53\u524D\u901F\u5EA6\uFF1A");
        label_speed.setFocusable(false);

        txt_speed = new JTextField();
        txt_speed.setText(snake.speed + " 毫秒 / 格");
        txt_speed.setEditable(false);
        txt_speed.setFocusable(false);
        lable_score.setLabelFor(txt_speed);
        txt_speed.setColumns(10);
        GroupLayout gl_panel_display = new GroupLayout(panel_display);
        gl_panel_display.setHorizontalGroup(
            gl_panel_display.createParallelGroup(Alignment.LEADING)
                .addGroup(gl_panel_display.createSequentialGroup()
                    .addGap(6)
                    .addGroup(gl_panel_display.createParallelGroup(Alignment.LEADING)
                        .addComponent(lable_score, 
                                GroupLayout.PREFERRED_SIZE, 75, GroupLayout.PREFERRED_SIZE)
                        .addComponent(label_maxScore, 
                                GroupLayout.PREFERRED_SIZE, 78, GroupLayout.PREFERRED_SIZE)
                        .addComponent(label_speed, 
                                GroupLayout.DEFAULT_SIZE, 83, Short.MAX_VALUE))
                    .addPreferredGap(ComponentPlacement.RELATED)
                    .addGroup(gl_panel_display.createParallelGroup(Alignment.LEADING, false)
                        .addComponent(txt_maxScore, 
                                GroupLayout.DEFAULT_SIZE, 111, Short.MAX_VALUE)
                        .addComponent(txt_speed, 
                                GroupLayout.DEFAULT_SIZE, 111, Short.MAX_VALUE)
                        .addComponent(txt_score))
                    .addContainerGap(26, Short.MAX_VALUE))
        );
        gl_panel_display.setVerticalGroup(
            gl_panel_display.createParallelGroup(Alignment.LEADING)
                .addGroup(gl_panel_display.createSequentialGroup()
                    .addContainerGap(24, Short.MAX_VALUE)
                    .addGroup(gl_panel_display.createParallelGroup(Alignment.TRAILING)
                        .addGroup(gl_panel_display.createSequentialGroup()
                            .addComponent(txt_score, GroupLayout.PREFERRED_SIZE,
                                    21, GroupLayout.PREFERRED_SIZE)
                            .addPreferredGap(ComponentPlacement.UNRELATED)
                            .addComponent(txt_maxScore, GroupLayout.PREFERRED_SIZE, 
                                    GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
                            .addPreferredGap(ComponentPlacement.RELATED)
                            .addComponent(txt_speed, GroupLayout.PREFERRED_SIZE,
                                    GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
                            .addGap(16))
                        .addGroup(gl_panel_display.createSequentialGroup()
                            .addComponent(lable_score, GroupLayout.PREFERRED_SIZE,
                                    18, GroupLayout.PREFERRED_SIZE)
                            .addPreferredGap(ComponentPlacement.UNRELATED)
                            .addComponent(label_maxScore, GroupLayout.PREFERRED_SIZE,
                                    25, GroupLayout.PREFERRED_SIZE)
                            .addPreferredGap(ComponentPlacement.RELATED)
                            .addComponent(label_speed)
                            .addGap(21))))
        );
        panel_display.setLayout(gl_panel_display);

        JLabel label_set = new JLabel("\u8BBE\u7F6E\u9879\uFF1A");
        label_set.setFocusable(false);

        JSeparator separator = new JSeparator();

        JCheckBox checkBox_isGriding = new JCheckBox("\u663E\u793A\u7F51\u683C");

        checkBox_isGriding.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseClicked(MouseEvent e) {
                if (checkBox_isGriding.isSelected()) {
                    ground.drawGriding();
                }else {
                    ground.notDrawGriding();
                }

            }
        });
        checkBox_isGriding.setFocusable(false);

        JSeparator separator_1 = new JSeparator();

        JLabel label_isGriding = new JLabel("\u662F\u5426\u663E\u793A\u7F51\u683C\uFF1A");
        label_isGriding.setFocusable(false);

        JSeparator separator_2 = new JSeparator();

        JPanel panel_setMap = new JPanel();
        panel_setMap.setFocusable(false);

        JPanel panel_setSpeed = new JPanel();
        panel_setSpeed.setFocusable(false);
        panel_setSpeed.setComponentOrientation(ComponentOrientation.LEFT_TO_RIGHT);
        GroupLayout gl_panel_set = new GroupLayout(panel_set);
        gl_panel_set.setHorizontalGroup(
            gl_panel_set.createParallelGroup(Alignment.LEADING)
                .addGroup(gl_panel_set.createSequentialGroup()
                    .addGroup(gl_panel_set.createParallelGroup(Alignment.LEADING, false)
                        .addGroup(gl_panel_set.createSequentialGroup()
                            .addGap(10)
                            .addComponent(label_set))
                        .addGroup(gl_panel_set.createSequentialGroup()
                            .addGap(20)
                            .addComponent(label_isGriding)
                            .addPreferredGap(ComponentPlacement.UNRELATED)
                            .addComponent(checkBox_isGriding))
                        .addGroup(gl_panel_set.createSequentialGroup()
                            .addGap(20)
                            .addComponent(separator_1, GroupLayout.PREFERRED_SIZE,
                                    222, GroupLayout.PREFERRED_SIZE))
                        .addGroup(gl_panel_set.createSequentialGroup()
                            .addGap(20)
                            .addComponent(separator_2, GroupLayout.PREFERRED_SIZE,
                                    224, GroupLayout.PREFERRED_SIZE))
                        .addGroup(gl_panel_set.createSequentialGroup()
                            .addGap(14)
                            .addComponent(panel_setSpeed, GroupLayout.PREFERRED_SIZE,
                                    264, GroupLayout.PREFERRED_SIZE))
                        .addGroup(gl_panel_set.createSequentialGroup()
                            .addContainerGap()
                            .addComponent(panel_setMap, GroupLayout.DEFAULT_SIZE,
                                    GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)))
                    .addGap(10))
                .addGroup(gl_panel_set.createSequentialGroup()
                    .addContainerGap()
                    .addComponent(separator, GroupLayout.DEFAULT_SIZE, 272, Short.MAX_VALUE)
                    .addContainerGap())
        );
        gl_panel_set.setVerticalGroup(
            gl_panel_set.createParallelGroup(Alignment.LEADING)
                .addGroup(gl_panel_set.createSequentialGroup()
                    .addGap(10)
                    .addComponent(label_set)
                    .addGap(10)
                    .addComponent(separator, GroupLayout.PREFERRED_SIZE, 8, GroupLayout.PREFERRED_SIZE)
                    .addPreferredGap(ComponentPlacement.RELATED)
                    .addGroup(gl_panel_set.createParallelGroup(Alignment.BASELINE)
                        .addComponent(label_isGriding)
                        .addComponent(checkBox_isGriding))
                    .addGap(12)
                    .addComponent(separator_2, GroupLayout.PREFERRED_SIZE,
                            GroupLayout.DEFAULT_SIZE, GroupLayout.PREFERRED_SIZE)
                    .addPreferredGap(ComponentPlacement.RELATED)
                    .addComponent(panel_setMap, GroupLayout.PREFERRED_SIZE,
                            37, GroupLayout.PREFERRED_SIZE)
                    .addGap(12)
                    .addComponent(separator_1, GroupLayout.PREFERRED_SIZE,
                            2, GroupLayout.PREFERRED_SIZE)
                    .addPreferredGap(ComponentPlacement.RELATED)
                    .addComponent(panel_setSpeed, GroupLayout.DEFAULT_SIZE,
                            34, Short.MAX_VALUE)
                    .addGap(14))
        );

        JLabel label_5 = new JLabel("\u9009\u62E9\u96BE\u5EA6\uFF1A");
        label_5.setFocusable(false);

        JRadioButton radioButton_speed1 = new JRadioButton("\u521D\u7EA7");
        radioButton_speed1.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseClicked(MouseEvent e) {
                    snake.speed = 500;
                    txt_speed.setText(snake.speed + " 毫秒 / 格");
            }
        });
        radioButton_speed1.setSelected(true);
        radioButton_speed1.setFocusable(false);

        JRadioButton radioButton_speed2 = new JRadioButton("\u4E2D\u7EA7");
        radioButton_speed2.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseClicked(MouseEvent e) {
                    snake.speed = 300;
                    txt_speed.setText(snake.speed + " 毫秒 / 格");
            }
        });
        radioButton_speed2.setFocusable(false);

        JRadioButton radioButton_speed3 = new JRadioButton("\u9AD8\u7EA7");
        radioButton_speed3.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseClicked(MouseEvent e) {
                    snake.speed = 100;
                    txt_speed.setText(snake.speed + " 毫秒 / 格");
            }
        });
        radioButton_speed3.setFocusable(false);

        ButtonGroup groupSpeed = new ButtonGroup();
        groupSpeed.add(radioButton_speed1);
        groupSpeed.add(radioButton_speed2);
        groupSpeed.add(radioButton_speed3);

        panel_setSpeed.setLayout(new FlowLayout(FlowLayout.CENTER, 5, 5));
        panel_setSpeed.add(label_5);
        panel_setSpeed.add(radioButton_speed1);
        panel_setSpeed.add(radioButton_speed2);
        panel_setSpeed.add(radioButton_speed3);


        JLabel label_setMap = new JLabel("\u9009\u62E9\u5730\u56FE\uFF1A");
        label_setMap.setFocusable(false);

        JRadioButton radioButton_map1 = new JRadioButton("\u5730\u56FE1");

        radioButton_map1.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseClicked(MouseEvent e) {
                ground.MAP = 1;
                ground.init();
            }
        });
        radioButton_map1.setSelected(true);

        radioButton_map1.setFocusable(false);

        JRadioButton radioButton_map2 = new JRadioButton("\u5730\u56FE2");
        radioButton_map2.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseClicked(MouseEvent e) {
                //点击鼠标后,设置为地图2
                ground.MAP = 2;
                //重新初始化地图
                ground.init();
            }
        });
        radioButton_map2.setFocusable(false);

        JRadioButton radioButton_map3 = new JRadioButton("\u968F\u673A\u5730\u56FE");
        radioButton_map3.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseClicked(MouseEvent e) {
                ground.MAP = 3;
                ground.init();
            }
        });
        radioButton_map3.setFocusable(false);

        ButtonGroup groupMap = new ButtonGroup();
        groupMap.add(radioButton_map1);
        groupMap.add(radioButton_map2);
        groupMap.add(radioButton_map3);

        panel_setMap.setLayout(new FlowLayout(FlowLayout.CENTER, 5, 5));
        panel_setMap.add(label_setMap);
        panel_setMap.add(radioButton_map1);
        panel_setMap.add(radioButton_map2);
        panel_setMap.add(radioButton_map3);
        panel_set.setLayout(gl_panel_set);

        JButton button_pause = new JButton("\u5F00\u59CB/\u6682\u505C");
        button_pause.setFocusable(false);
        button_pause.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseClicked(MouseEvent e) {
                snake.changePause();
            }
        });
        button_pause.setFocusPainted(false);

        JButton button_newGame = new JButton("\u5F00\u59CB\u65B0\u6E38\u620F");
        button_newGame.setFocusable(false);
        button_newGame.addMouseListener(new MouseAdapter() {
            @Override
            public void mouseClicked(MouseEvent e) {
                controller.newGame();

            }
        });
        button_newGame.setFocusPainted(false);
        GroupLayout gl_panel_control = new GroupLayout(panel_control);
        gl_panel_control.setHorizontalGroup(
            gl_panel_control.createParallelGroup(Alignment.TRAILING)
                .addGroup(gl_panel_control.createSequentialGroup()
                    .addContainerGap(GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                    .addComponent(button_newGame, GroupLayout.PREFERRED_SIZE,
                            95, GroupLayout.PREFERRED_SIZE)
                    .addGap(3)
                    .addComponent(button_pause, GroupLayout.PREFERRED_SIZE,
                            94, GroupLayout.PREFERRED_SIZE)
                    .addContainerGap())
        );
        gl_panel_control.setVerticalGroup(
            gl_panel_control.createParallelGroup(Alignment.TRAILING)
                .addGroup(Alignment.LEADING, gl_panel_control.createSequentialGroup()
                    .addContainerGap()
                    .addGroup(gl_panel_control.createParallelGroup(Alignment.BASELINE)
                        .addComponent(button_newGame, GroupLayout.PREFERRED_SIZE,
                                44, GroupLayout.PREFERRED_SIZE)
                        .addComponent(button_pause, GroupLayout.PREFERRED_SIZE, 
                                44, GroupLayout.PREFERRED_SIZE))
                    .addContainerGap(18, Short.MAX_VALUE))
        );
        panel_control.setLayout(gl_panel_control);

        JLabel lblNewLabel = new JLabel("\u8BF4\u660E\uFF1A");
        lblNewLabel.setFocusable(false);
        lblNewLabel.setHorizontalAlignment(SwingConstants.CENTER);
        lblNewLabel.setHorizontalTextPosition(SwingConstants.CENTER);

        JLabel label = new JLabel("\u65B9\u5411\u952E\u64CD\u4F5C\u65B9\u5411");
        label.setFocusable(false);

        JLabel label_1 = new JLabel("\u7A7A\u683C\u952E\u53EF\u5B9E\u73B0\u6682\u505C/\u7EE7\u7EED");
        label_1.setFocusable(false);

        JLabel lblShift = new JLabel("Shift\u952E \u5F00\u59CB\u65B0\u6E38\u620F");
        lblShift.setFocusable(false);

        JLabel label_2 = new JLabel("\u968F\u673A\u5730\u56FE\u4F1A"
                + "\u968F\u673A\u83B7\u5F9740\u4E2A\u5750\u6807\u4F5C\u4E3A\u77F3\u5934");
        label_2.setHorizontalAlignment(SwingConstants.LEFT);
        label_2.setInheritsPopupMenu(false);
        label_2.setFocusable(false);
        label_2.setFocusTraversalKeysEnabled(false);
        label_2.setAlignmentX(Component.CENTER_ALIGNMENT);
        GroupLayout gl_lable = new GroupLayout(lable);
        gl_lable.setHorizontalGroup(
            gl_lable.createParallelGroup(Alignment.TRAILING)
                .addGroup(gl_lable.createSequentialGroup()
                    .addGroup(gl_lable.createParallelGroup(Alignment.LEADING)
                        .addComponent(lblNewLabel, GroupLayout.PREFERRED_SIZE,
                                71, GroupLayout.PREFERRED_SIZE)
                        .addGroup(gl_lable.createSequentialGroup()
                            .addGap(26)
                            .addGroup(gl_lable.createParallelGroup(Alignment.LEADING)
                                .addComponent(label_1, 
                                        GroupLayout.DEFAULT_SIZE, 212, Short.MAX_VALUE)
                                .addComponent(label, GroupLayout.PREFERRED_SIZE,
                                        113, GroupLayout.PREFERRED_SIZE)
                                .addComponent(lblShift)
                                .addComponent(label_2, 
                                        GroupLayout.DEFAULT_SIZE, 212, Short.MAX_VALUE))))
                    .addContainerGap())
        );
        gl_lable.setVerticalGroup(
            gl_lable.createParallelGroup(Alignment.LEADING)
                .addGroup(gl_lable.createSequentialGroup()
                    .addGap(8)
                    .addComponent(lblNewLabel, GroupLayout.PREFERRED_SIZE,
                            30, GroupLayout.PREFERRED_SIZE)
                    .addPreferredGap(ComponentPlacement.RELATED)
                    .addComponent(label, GroupLayout.PREFERRED_SIZE, 
                            26, GroupLayout.PREFERRED_SIZE)
                    .addGap(2)
                    .addComponent(label_1, GroupLayout.PREFERRED_SIZE,
                            26, GroupLayout.PREFERRED_SIZE)
                    .addPreferredGap(ComponentPlacement.RELATED)
                    .addComponent(lblShift, GroupLayout.PREFERRED_SIZE,
                            25, GroupLayout.PREFERRED_SIZE)
                    .addPreferredGap(ComponentPlacement.UNRELATED)
                    .addComponent(label_2)
                    .addContainerGap(39, Short.MAX_VALUE))
        );
        lable.setLayout(gl_lable);
        panel_1.setLayout(gl_panel_1);
        contentPane.setLayout(gl_contentPane);
        //给游戏面板和蛇添加监听器
        gamePanel.addKeyListener(controller);
        snake.addSnakeListener(controller);
        //开始一个新的线程,用来更新分数
        controller.startRefresh(new Thread(new refresh()));
        //开始游戏
        controller.beginGame();
    }
    //创建一个线程让一直刷新分数
    public class refresh implements Runnable{
        @Override
        public void run() {
            //当蛇活着的时候才进行循环
            while(!snake.isDie) {
                txt_score.setText(controller.score + " 分");
                txt_maxScore.setText(controller.maxScore + " 分");
                try {
                    Thread.sleep(snake.speed);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }   
    }
}

主方法启动程序:

package snake.game;

import java.awt.EventQueue;

import snake.view.MainWindow;
/*
 * 作为游戏的主方法,启动游戏,通过启动窗体,实现启动程序
 */
public class Game {

    public static void main(String[] args) {

        EventQueue.invokeLater(new Runnable() {
            public void run() {
                try {
                    MainWindow frame = new MainWindow();
                    frame.setVisible(true);

                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
    }
}   

程序运行效果:

设置项效果:

游戏是否显示网格,可通过是否选中来设置。包括选择地图,选择速度。所有设置后在开始新游戏后生效。速度在游戏过程中也可以随时调节。效果如下图:

这里写图片描述

图:显示网格,随机地图

显示分数:

在蛇每次吃到食物后,分数会加10分,并实时更新分数。以及有历史最高分。包括蛇的速度也会显示。

游戏分数

图:分数实时更新

蛇穿越边界:

在边界没有石头时,蛇可以从一边进去,从另外一边出来。

穿越边界

图:穿越边界

蛇吃到石头:

蛇吃到石头后就会死亡,并弹出提框,同时显示分数。

撞墙

图:蛇撞墙

蛇咬到自己:

蛇咬到自己也会死亡,弹出提示框。

## ![咬到自己](Java编程-贪吃蛇游戏/咬到自己.png)

图:蛇咬到自己

游戏代码下载:

可点击下载原代码包。


已标记关键词 清除标记
相关推荐
©️2020 CSDN 皮肤主题: 技术工厂 设计师:CSDN官方博客 返回首页