目标:

制作使用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();}//蛇死亡,生命改为falsepublic 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(); //地图2getScoksCount();break;case 3:map3(); //随机地图getScoksCount();break;default :map1(); //默认地图1getScoksCount();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();}@Overrideprotected 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 事件@Overridepublic 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() {@Overridepublic 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() {@Overridepublic 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() {@Overridepublic 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() {@Overridepublic 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() {@Overridepublic 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() {@Overridepublic void mouseClicked(MouseEvent e) {//点击鼠标后,设置为地图2ground.MAP = 2;//重新初始化地图ground.init();}});radioButton_map2.setFocusable(false);JRadioButton radioButton_map3 = new JRadioButton("\u968F\u673A\u5730\u56FE");radioButton_map3.addMouseListener(new MouseAdapter() {@Overridepublic 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() {@Overridepublic 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() {@Overridepublic 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{@Overridepublic 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编程---贪吃蛇游戏相关推荐

  1. JAVA简易贪吃蛇游戏实现

    JAVA简易贪吃蛇游戏实现 自学java不久,最近看了些GUI编程,就拿贪吃蛇练个手,很基础 刚学的 也是最简易的版本.纯粹就想通过博客记录来巩固自己的学习效果. 游戏介绍 玩家通过按键控制蛇身的移动 ...

  2. java贪吃蛇不能回头,儿时回忆!泪流满面,Java 实现贪吃蛇游戏的示例(附代码)...

    image.png image.png java实现贪吃蛇游戏需要创建一个桌面窗口出来,此时就需要使用java中的swing控件 创建一个新窗口 JFrame frame = new JFrame(& ...

  3. 基于java的贪吃蛇游戏

    贪吃蛇的游戏开发 实验内容: 1)实现贪吃蛇游戏基本功能,屏幕上随机出现一个"食物",称为豆子,上下左右 控制"蛇"的移动,吃到"豆子"以后 ...

  4. python趣味编程-贪吃蛇游戏

    在上一期我们用Python实现了一个扫雷游戏的游戏,这一期我们继续使用Python实现一个简单的游戏,让我们开始今天的旅程吧~ 在Python中使用Turtle的贪吃蛇游戏免费源代码 介绍 这 在py ...

  5. Java项目:贪吃蛇游戏(java+swing)

    源码获取:博客首页 "资源" 里下载! 功能简介: 贪吃蛇游戏 大嘴鱼洁面类.完成大嘴鱼的界面的绘制: /*** 大嘴鱼洁面类.完成大嘴鱼的界面的绘制.*/ public clas ...

  6. Java的GUI编程---贪吃蛇游戏实现

    绘制静态窗口 代码实现 package com.akita.snake;import javax.swing.*;public class StartGames {public static void ...

  7. Java版贪吃蛇游戏

    实现代码: public class Main {public static void main(String[] args) {WelcomeJFrame jg=new WelcomeJFrame( ...

  8. win32游戏编程——贪吃蛇游戏

    1.写此贴的意图 刚开始学c++,编的是win32 console application,在黑框框里进行输入和输出,觉得和自己想象的程序差好多,有点失望,后来接触到win32程序,感觉和我们平时见到 ...

  9. c语言编程贪吃蛇游戏 下载,C语言游戏编程 贪吃蛇游戏

    #include dos.h DOS接口函数 #include math.h 数学函数的定义 #include conio.h 屏幕操作函数 #include stdio.h IO函数 #includ ...

最新文章

  1. Python Django开发案例:GET方式实现登录功能
  2. 武汉自考本科计算机专业哪个好,2020年武汉自考本科专业有哪些?
  3. 那些年,我的数据结构课设,现在满满的回忆!(现如今身处内卷之中,已经很难出现当初那份乐趣了)
  4. c语言结果九位数,C语言实例:九位累进可除数
  5. php mysql html 转义字符_php, html, javascript, mysql 之间的特殊字符处理
  6. Session的原理,大型网站中Session方面应注意什么?
  7. js判断时间是早上还是下午_牛奶早上喝好,还是晚上喝好?没想到“最佳时间”是这个点,颠覆了!...
  8. golang 数组组合成最小的整数_golang数组-----寻找数组中缺失的整数方法
  9. 测试中如何管理外包质量_【项目管理】资深游戏测试总监详解质量管理工作的9个步骤...
  10. 检测 邮箱地址 是否存在[C#]
  11. 【线性代数(2)】n阶行列式三种定义
  12. 大学c语言基础知识必备大全,大学 C语言基础知识.ppt
  13. 工信部华为鸿蒙,九零科技日报 华为鸿蒙系统疑似被曝光 工信部向三大运营商颁发5G牌照​...
  14. PhotoShop一键修改4的倍数图片工具
  15. 在与SQL Server建立连接时出现于网络相关的或特定于实例的错误。
  16. vue3+ts import引用报错Could not find a declaration file for module ‘three-obj-mtl-loader‘.
  17. 霍尔电流传感器CH701/CH701W应用于电动汽车模式2充电的装置
  18. 80386为32位机
  19. 使用python画出五角星
  20. jfinal框架教程-学习笔记(一)

热门文章

  1. python 自相关函数_用Python的statsmodels库计算时间序列的自相关函数和画图
  2. 了解聚类是什么。聚类方法:k-means、核聚类、层次聚类、谱聚类
  3. Mongodb Connection Error [java.lang.IllegalArgumentException: Prohibited character at position 0]
  4. 用工单位使用劳动派遣时需注意这几点
  5. CentOS安装OpenResty
  6. linux认证视频教程,Linux RHCE认证视频教程(羽飞老师出品104集全)
  7. GPRS使用AT命令拨接电话,发收中英文短信
  8. 图文并茂之AES加密
  9. Linux服务器系统自动巡检脚本生成html报告
  10. 想让JS弹出窗口显示在第二个(辅助)显示器上?