相关文章推荐

功能简介:

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

    游戏框架分析:

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

      代码实现及解释:

      有关蛇的代码实现:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      55
      56
      57
      58
      59
      60
      61
      62
      63
      64
      65
      66
      67
      68
      69
      70
      71
      72
      73
      74
      75
      76
      77
      78
      79
      80
      81
      82
      83
      84
      85
      86
      87
      88
      89
      90
      91
      92
      93
      94
      95
      96
      97
      98
      99
      100
      101
      102
      103
      104
      105
      106
      107
      108
      109
      110
      111
      112
      113
      114
      115
      116
      117
      118
      119
      120
      121
      122
      123
      124
      125
      126
      127
      128
      129
      130
      131
      132
      133
      134
      135
      136
      137
      138
      139
      140
      141
      142
      143
      144
      145
      146
      147
      148
      149
      150
      151
      152
      153
      154
      155
      156
      157
      158
      159
      160
      161
      162
      163
      164
      165
      166
      167
      168
      169
      170
      171
      172
      173
      174
      175
      176
      177
      178
      179
      180
      181
      182
      183
      184
      185
      186
      187
      188
      189
      190
      191
      192
      193
      194
      195
      196
      197
      198
      199
      200
      201
      202
      203
      204
      205
      206
      207
      208
      209
      210
      211
      212
      213
      214
      215
      216
      217
      218
      219
      220
      221
      222
      223
      224
      225
      226
      227
      228
      229
      230
      231
      232
      233
      234
      235
      236
      237
      238
      239
      240
      241
      242
      243
      244
      245
      246
      247
      248
      249
      250
      251
      252
      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();
      }
      }

      石头的代码实现:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      55
      56
      57
      58
      59
      60
      61
      62
      63
      64
      65
      66
      67
      68
      69
      70
      71
      72
      73
      74
      75
      76
      77
      78
      79
      80
      81
      82
      83
      84
      85
      86
      87
      88
      89
      90
      91
      92
      93
      94
      95
      96
      97
      98
      99
      100
      101
      102
      103
      104
      105
      106
      107
      108
      109
      110
      111
      112
      113
      114
      115
      116
      117
      118
      119
      120
      121
      122
      123
      124
      125
      126
      127
      128
      129
      130
      131
      132
      133
      134
      135
      136
      137
      138
      139
      140
      141
      142
      143
      144
      145
      146
      147
      148
      149
      150
      151
      152
      153
      154
      155
      156
      157
      158
      159
      160
      161
      162
      163
      164
      165
      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;
      }
      }

      食物的代码实现:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      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代码:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      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);
      }

      }
      }

      蛇监听类代码:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      package snake.listener;

      import snake.entities.Snake;

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

      }

      控制类代码实现:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      55
      56
      57
      58
      59
      60
      61
      62
      63
      64
      65
      66
      67
      68
      69
      70
      71
      72
      73
      74
      75
      76
      77
      78
      79
      80
      81
      82
      83
      84
      85
      86
      87
      88
      89
      90
      91
      92
      93
      94
      95
      96
      97
      98
      99
      100
      101
      102
      103
      104
      105
      106
      107
      108
      109
      110
      111
      112
      113
      114
      115
      116
      117
      118
      119
      120
      121
      122
      123
      124
      125
      126
      127
      128
      129
      130
      131
      132
      133
      134
      135
      136
      137
      138
      139
      140
      141
      142
      143
      144
      145
      146
      147
      148
      149
      150
      151
      152
      153
      154
      155
      156
      157
      158
      159
      160
      161
      162
      163
      164
      165
      166
      167
      168
      169
      170
      171
      172
      173
      174
      175
      176
      177
      178
      179
      180
      181
      182
      183
      184
      185
      186
      187
      188
      189
      190
      191
      192
      193
      194
      195
      196
      197
      198
      199
      200
      201
      202
      203
      204
      205
      206
      207
      208
      209
      210
      211
      212
      213
      214
      215
      216
      217
      218
      219
      220
      221
      222
      223
      224
      225
      226
      227
      228
      229
      230
      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;
      }
      }

      工具类全局代码:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      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;

      }

      游戏主窗体代码:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      55
      56
      57
      58
      59
      60
      61
      62
      63
      64
      65
      66
      67
      68
      69
      70
      71
      72
      73
      74
      75
      76
      77
      78
      79
      80
      81
      82
      83
      84
      85
      86
      87
      88
      89
      90
      91
      92
      93
      94
      95
      96
      97
      98
      99
      100
      101
      102
      103
      104
      105
      106
      107
      108
      109
      110
      111
      112
      113
      114
      115
      116
      117
      118
      119
      120
      121
      122
      123
      124
      125
      126
      127
      128
      129
      130
      131
      132
      133
      134
      135
      136
      137
      138
      139
      140
      141
      142
      143
      144
      145
      146
      147
      148
      149
      150
      151
      152
      153
      154
      155
      156
      157
      158
      159
      160
      161
      162
      163
      164
      165
      166
      167
      168
      169
      170
      171
      172
      173
      174
      175
      176
      177
      178
      179
      180
      181
      182
      183
      184
      185
      186
      187
      188
      189
      190
      191
      192
      193
      194
      195
      196
      197
      198
      199
      200
      201
      202
      203
      204
      205
      206
      207
      208
      209
      210
      211
      212
      213
      214
      215
      216
      217
      218
      219
      220
      221
      222
      223
      224
      225
      226
      227
      228
      229
      230
      231
      232
      233
      234
      235
      236
      237
      238
      239
      240
      241
      242
      243
      244
      245
      246
      247
      248
      249
      250
      251
      252
      253
      254
      255
      256
      257
      258
      259
      260
      261
      262
      263
      264
      265
      266
      267
      268
      269
      270
      271
      272
      273
      274
      275
      276
      277
      278
      279
      280
      281
      282
      283
      284
      285
      286
      287
      288
      289
      290
      291
      292
      293
      294
      295
      296
      297
      298
      299
      300
      301
      302
      303
      304
      305
      306
      307
      308
      309
      310
      311
      312
      313
      314
      315
      316
      317
      318
      319
      320
      321
      322
      323
      324
      325
      326
      327
      328
      329
      330
      331
      332
      333
      334
      335
      336
      337
      338
      339
      340
      341
      342
      343
      344
      345
      346
      347
      348
      349
      350
      351
      352
      353
      354
      355
      356
      357
      358
      359
      360
      361
      362
      363
      364
      365
      366
      367
      368
      369
      370
      371
      372
      373
      374
      375
      376
      377
      378
      379
      380
      381
      382
      383
      384
      385
      386
      387
      388
      389
      390
      391
      392
      393
      394
      395
      396
      397
      398
      399
      400
      401
      402
      403
      404
      405
      406
      407
      408
      409
      410
      411
      412
      413
      414
      415
      416
      417
      418
      419
      420
      421
      422
      423
      424
      425
      426
      427
      428
      429
      430
      431
      432
      433
      434
      435
      436
      437
      438
      439
      440
      441
      442
      443
      444
      445
      446
      447
      448
      449
      450
      451
      452
      453
      454
      455
      456
      457
      458
      459
      460
      461
      462
      463
      464
      465
      466
      467
      468
      469
      470
      471
      472
      473
      474
      475
      476
      477
      478
      479
      480
      481
      482
      483
      484
      485
      486
      487
      488
      489
      490
      491
      492
      493
      494
      495
      496
      497
      498
      499
      500
      501
      502
      503
      504
      505
      506
      507
      508
      509
      510
      511
      512
      513
      514
      515
      516
      517
      518
      519
      520
      521
      522
      523
      524
      525
      526
      527
      528
      529
      530
      531
      532
      533
      534
      535
      536
      537
      538
      539
      540
      541
      542
      543
      544
      545
      546
      547
      548
      549
      550
      551
      552
      553
      554
      555
      556
      557
      558
      559
      560
      561
      562
      563
      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();
      }
      }
      }
      }
      }

      主方法启动程序:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      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分,并实时更新分数。以及有历史最高分。包括蛇的速度也会显示。

      图:分数实时更新

      蛇穿越边界:

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

      图:穿越边界

      蛇吃到石头:

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

      图:蛇撞墙

      蛇咬到自己:

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

      咬到自己

      图:蛇咬到自己

      游戏代码下载:

      可点击下载原代码包。

 
推荐文章