简单粗暴直接上代码吧
1、 首先就是蛇类了
package snakefun; import java.awt.Color; import java.awt.Graphics; import java.awt.Point; import java.util.HashSet; import java.util.LinkedList; import java.util.Set; public class Snake implements Draw,Runnable { //蛇的监听器 监听蛇的每次移动是否吃了食物、碰墙、碰障碍、碰自己 Set<SnakeListener> snakeListener = new HashSet<SnakeListener>(); //用链表保存蛇的身体节点 因为需要蛇的身体是需要不停的修改首尾节点 所以从效率上考虑可以使用LinkedList LinkedList<Point> body = new LinkedList<Point>(); //最后的尾巴节点记录 在贪吃蛇吃了食物时添加此节点增长蛇的长度 private Point lastTail = null; //蛇的初始长度为7 private int INITLENGTH = 7; //默认速度为400ms 通过进程的sleep来进行速度的控制 private int speed = 400; //私有属性蛇的生存标识 private boolean life = true; //私有属性游戏的暂停标识 private boolean pause = false; //定义各个方向 因为考虑到相反方向不能直接转向 所以在定义方向时相反方向互为相反数 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 newDirection = RIGHT; //记录蛇的上次方向避免蛇直接反向行走 private int oldDirection = newDirection; //默认构造器初始化蛇的身体 public Snake() { initial(); } //初始化蛇 public void initial() { //清空蛇的身体全部节点 body.removeAll(body); //设置蛇的期待在中心 int x = Constant.WIDTH / 2; int y = Constant.HIGHT / 2; //依次向右记录蛇的身体 默认长度定为7 for (int i = 0; i < INITLENGTH; i++) { //从右到左依次展开 body.add(new Point(x--, y)); } } //速度 生命标识 停止标识的get set方法 public int getSpeed() { return speed; } public void setSpeed(int speed) { this.speed = speed; } public boolean isLife() { return life; } public void setLife(boolean life) { this.life = life; } public boolean isPause() { return pause; } public void setPause(boolean pause) { this.pause = pause; } //返回蛇的首节点 用于判断蛇是否碰撞其他物体 public Point getHead() { return body.getFirst(); } //返回蛇的所有身体节点 public LinkedList<Point> getSnakeBody() { return this.body; } //标识蛇的死亡 游戏结束 public boolean died() { this.life = false; return true; } //蛇的移动实现方式为:删除最后一个节点,舌头增加一个节点; public void move() { //转向设置不能直接掉头 相反方向的数字相加为0 if((oldDirection + newDirection) != 0) { oldDirection = newDirection; } //移除尾节点并记录,蛇吃到食物时添加最后节点 lastTail = body.removeLast(); int x = body.getFirst().x; int y = body.getFirst().y; switch (oldDirection) { case UP: y--; break; case DOWN: y++; break; case LEFT: x--; break; case RIGHT: x++; break; default: break; } Point newHead = new Point(x, y); //根据方向变更重置首节点 body.addFirst(newHead); } //蛇吃食物 蛇吃到食物时 添加之前最后一位的坐标 增加一位长度 public void eatFood() { body.addLast(lastTail); } //当蛇碰到自己时 死亡 public boolean isEatItSelf() { //通过蛇头是否能碰到第三个节点之后的身体节点来判断蛇是否触碰了自己 蛇头不会碰见自己 也不可能碰见第二位 所以从第三个节点开始 for (int i = 2; i < body.size(); i++) { if(body.getFirst().equals(body.get(i))) { return true; } } return false; } @Override public void drawme(Graphics g) { //循环打印蛇的身体 for (Point p : body) { //蛇头橘黄色 身体黑色 if(p.equals(body.getFirst())) { g.setColor(Color.orange); } else { g.setColor(Color.black); } //打印方形节点 g.fill3DRect(p.x*Constant.CELL, p.y*Constant.CELL, Constant.CELL, Constant.CELL, true); } } //改变方向 记录新方向 public void changeDirection(int direction) { this.newDirection = direction; } @Override public void run() { //重写Thread run()方法 如果设存活不停的执行 while (life) { //如果不停止 蛇持续移动 if(!pause) { move(); } //监听蛇每次移动的情况 for (SnakeListener listener : snakeListener) { listener.snakeMove(this); } try { //通过进行睡眠来控制蛇移动的速度 Thread.sleep(speed); } catch (Exception e) { e.printStackTrace(); } } } //蛇开始移动 进程开始 public void startMove() { new Thread(this).start(); } //增加监听器 public void addSnakeListener(SnakeListener listener) { if(listener != null) { snakeListener.add(listener); } } }
2、 食物类
package snakefun; import java.awt.Color; import java.awt.Graphics; import java.awt.Point; public class Food implements Draw { //食物坐标 private int x = 0; private int y = 0; //设置食物的默认颜色为红色 private Color color = Color.red; //获得新的食物坐标 通过随机方法获取一个随机的坐标节点 public void newFood(Point p) { x = p.x; y = p.y; } //判断是否被吃掉 public boolean isAte(Snake snake) { //判断Snake的首节点是否与食物坐标重合 if(snake.getHead().equals(new Point(x, y))) { return true; } return false; } @Override public void drawme(Graphics g) { g.setColor(this.color); g.fill3DRect(x*Constant.CELL, y*Constant.CELL, Constant.CELL, Constant.CELL, true); } }
3、 围墙类
package snakefun; import java.awt.Color; import java.awt.Graphics; import java.awt.Point; import java.util.LinkedList; import java.util.Random; public class Groud implements Draw { //设置一个二维数组 记录游戏范围内全部坐标 private int[][] rock = new int[Constant.WIDTH][Constant.HIGHT]; //砖块标识 为1 private static final int Stone = 1; //打印网格标识 private boolean drawLine = false; public boolean isDrawLine() { return drawLine; } public void setDrawLine(boolean drawLine) { this.drawLine = drawLine; } //默认构造函数 二维数组最边缘一圈为砖块 public Groud() { for (int x = 0; x < Constant.WIDTH; x++) { for (int y = 0; y < Constant.HIGHT; y++) { rock[0][y] = Stone; rock[x][0] = Stone; rock[Constant.WIDTH - 1][y] = Stone; rock[x][Constant.HIGHT -1] = Stone; } } } //打印游戏的网格 public void drawGrid(Graphics g) { for (int x = 2; x < Constant.WIDTH; x++) { g.drawLine(x * Constant.CELL, 0, x * Constant.CELL, Constant.CELL * Constant.HIGHT); } for (int y = 2; y < Constant.HIGHT; y++) { g.drawLine(0, y * Constant.CELL, Constant.CELL * Constant.WIDTH, y * Constant.CELL); } } @Override public void drawme(Graphics g) { //绘制围墙 设置围墙的颜色为黑色 g.setColor(Color.black); for (int x = 0; x < Constant.WIDTH; x++) { for (int y = 0; y < Constant.WIDTH; y++) { if(rock[x][y] == 1) { g.fill3DRect(x * Constant.CELL, y * Constant.CELL, Constant.CELL, Constant.CELL, true); } } } //判断是否打印围墙 if(isDrawLine()) { //网格线设置为黄色 g.setColor(Color.yellow); this.drawGrid(g); } } // 排除蛇的身体 围墙 障碍物外获得一个随机数 public Point getRandomPoint(Snake snake, Thorn thorn) { Random random = new Random(); boolean isUnderSnakebody = false; boolean isUnderThorn = false; int x = 0; int y = 0; Point newPoint = null; LinkedList<Point> snakeBody = snake.getSnakeBody(); LinkedList<Point> thorns = thorn.getThorns(); do { isUnderSnakebody = false; isUnderThorn = false; x = random.nextInt(Constant.WIDTH); y = random.nextInt(Constant.HIGHT); newPoint = new Point(x, y); for (Point p : thorns) { if(p.equals(newPoint)) { isUnderThorn = true; } } for (Point p : snakeBody) { if(p.equals(newPoint)) { isUnderSnakebody = true; } } } while (rock[x][y] == 1 || isUnderThorn || isUnderSnakebody); return newPoint; } //判断蛇是否撞墙 public boolean isSnakeHitRock(Snake snake) { Point head = snake.getHead(); if(rock[head.x][head.y] == 1) { return true; } return false; } }
4、 障碍物类
package snakefun; import java.awt.Color; import java.awt.Graphics; import java.awt.Point; import java.util.LinkedList; public class Thorn implements Draw { //记录所有障碍物的坐标 private LinkedList<Point> thorns = new LinkedList<Point>(); //所有障碍物的坐标的get方法 public LinkedList<Point> getThorns() { return thorns; } //记录新的障碍物坐标 public void newThorn(Point point) { thorns.add(point); } @Override public void drawme(Graphics g) { //通过9个点绘制相应图形 for (Point p : thorns) { int[] xb = {p.x * Constant.CELL, (p.x * Constant.CELL + Constant.CELL / 2), (p.x * Constant.CELL + Constant.CELL), (p.x * Constant.CELL + Constant.CELL / 4 * 3), (p.x * Constant.CELL + Constant.CELL), (p.x * Constant.CELL + Constant.CELL / 2), p.x * Constant.CELL, (p.x * Constant.CELL + Constant.CELL / 4), p.x * Constant.CELL}; int[] yb = {p.y * Constant.CELL, (p.y * Constant.CELL + Constant.CELL / 4), p.y * Constant.CELL, (p.y * Constant.CELL + Constant.CELL / 2), (p.y * Constant.CELL + Constant.CELL), (p.y * Constant.CELL + Constant.CELL / 4 * 3), (p.y * Constant.CELL + Constant.CELL), (p.y * Constant.CELL + Constant.CELL / 2), p.y * Constant.CELL}; g.setColor(Color.darkGray); g.fillPolygon(xb, yb, 8); } } //判断蛇是否碰撞了障碍物 public boolean isSnakeHitThorn(Snake snake) { for (Point p : thorns) { if(snake.getHead().equals(p)) { return true; } } return false; } }
5、 枚举类
package snakefun; public class Constant { //格子大小 定义 public static final int CELL = 20; //宽度 public static final int WIDTH = 20; //高度 public static final int HIGHT = 20; }
6、 面板类
package snakefun; import java.awt.Color; import java.awt.Graphics; import javax.swing.JPanel; //面板构建 public class GamePanel extends JPanel { //实现序列化 private static final long serialVersionUID = 1L; private Snake snake; private Food food; private Thorn thorn; private Groud groud; //组件重置 public void display(Snake snake, Food food, Thorn thorn, Groud groud) { this.snake = snake; this.food = food; this.thorn = thorn; this.groud = groud; this.repaint(); } //绘制组件 protected void paintComponent(Graphics g) { if(snake != null && food != null && groud != null && thorn != null) { g.setColor(Color.lightGray); g.fillRect(0, 0, Constant.CELL * Constant.WIDTH, Constant.CELL * Constant.HIGHT); snake.drawme(g); food.drawme(g); groud.drawme(g); thorn.drawme(g); } else if(snake == null) { System.out.println("snake component is null!!!"); } else if(food == null) { System.out.println("food component is null!!!"); } else if(groud == null) { System.out.println("groud component is null!!!"); } else if(thorn == null) { System.out.println("thorn component is null!!!"); } else { System.out.println("component is null!!!"); } } }
7、 绘制接口类
package snakefun; import java.awt.Graphics; //绘制自身接口 蛇 食物 障碍 墙等对象皆需要实现此接口 public interface Draw { public void drawme(Graphics g); }
8、 监听接口类
package snakefun; //蛇的监听接口 public interface SnakeListener { public void snakeMove(Snake snake); }
9、 整体控制器
package snakefun; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; /* * 监听控制器 * 整体监听蛇的生存状况 * 监听键盘输入情况 * */ public class Controller extends KeyAdapter implements SnakeListener { private Snake snake; private Food food; private Thorn thorn; private Groud groud; private GamePanel gamePanel; public Controller() { super(); } //整体监控所有对象的运行状态 public Controller(Snake snake, Food food, Thorn thorn, Groud groud, GamePanel gamePanel) { super(); this.snake = snake; this.food = food; this.thorn = thorn; this.groud = groud; this.gamePanel = gamePanel; } //键盘输入情况监控收集 public void keyPressed(KeyEvent e) { switch (e.getKeyCode()) { case KeyEvent.VK_UP: snake.changeDirection(Snake.UP); snake.setSpeed(150); break; case KeyEvent.VK_DOWN: snake.changeDirection(Snake.DOWN); snake.setSpeed(150); break; case KeyEvent.VK_LEFT: snake.changeDirection(Snake.LEFT); snake.setSpeed(150); break; case KeyEvent.VK_RIGHT: snake.changeDirection(Snake.RIGHT); snake.setSpeed(150); break; case KeyEvent.VK_ENTER: if (!snake.isPause() && snake.isLife()) { //游戏未暂停切蛇未死亡时暂停游戏 snake.setPause(true); } else if(!snake.isLife()) { //蛇死亡时 重新开始游戏 snake.setLife(true); snake.initial(); snake.changeDirection(Snake.UP); thorn.getThorns().removeAll(thorn.getThorns()); this.startGame(); } else { snake.setPause(false); } break; case KeyEvent.VK_L: //当按下L按钮时,是否显示游戏网格 if(groud.isDrawLine()) { groud.setDrawLine(false); }else { groud.setDrawLine(true); } break; default: break; } } public void keyReleased(KeyEvent e) { switch (e.getKeyCode()) { case KeyEvent.VK_UP: case KeyEvent.VK_DOWN: case KeyEvent.VK_LEFT: case KeyEvent.VK_RIGHT: snake.setSpeed(400); break; default: break; } } @Override //蛇的状态监控方法 监听蛇的每次移动是否吃了食物、碰墙、碰障碍、碰自己 public void snakeMove(Snake snake) { gamePanel.display(snake, food, thorn, groud); if(food.isAte(snake)) { snake.eatFood(); thorn.newThorn(groud.getRandomPoint(snake, thorn)); food.newFood(groud.getRandomPoint(snake, thorn)); } if(snake.isEatItSelf()) { snake.died(); } if(groud.isSnakeHitRock(snake)) { snake.died(); } if(thorn.isSnakeHitThorn(snake)) { snake.died(); } } //游戏开始 初始化蛇 食物及障碍物 public void startGame() { snake.startMove();//这个将启动一个新的线程 thorn.newThorn(groud.getRandomPoint(snake, thorn)); food.newFood(groud.getRandomPoint(snake, thorn)); } }
10、 游戏管理类
package snakefun; import java.awt.BorderLayout; import java.awt.Color; import javax.swing.JFrame; import javax.swing.JLabel; public class Game { public static void main(String[] args) { //初始化各类对象 Snake snake = new Snake(); Food food = new Food(); Groud groud = new Groud(); Thorn thorn = new Thorn(); //继承JPanel 面板控件 GamePanel gamePanel = new GamePanel(); //controller整体监控 Controller controller = new Controller(snake, food, thorn, groud, gamePanel); //初始化窗体控件 JFrame frame = new JFrame("贪吃蛇"); //设置窗体背景色 frame.setBackground(Color.magenta); //设置窗体大小比绘制面板稍大一点 frame.setSize(Constant.CELL * (Constant.WIDTH + 1), Constant.CELL * (Constant.HIGHT + 3)); //设置面板大小 gamePanel.setSize(Constant.CELL * Constant.WIDTH, Constant.CELL * Constant.HIGHT); //将面板添加至窗体里 frame.add(gamePanel); //设置窗体大小不可变化 frame.setResizable(false); //设置窗体关闭方式, 窗口关闭同时进程结束 frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); //设置窗体可视化 frame.setVisible(true); //窗体和面板增加键盘输入监控, 蛇添加生存状态监控 gamePanel.addKeyListener(controller); snake.addSnakeListener(controller); frame.addKeyListener(controller); //label标签添加说明 JLabel label = new JLabel(); //设置位置宽度高度 label.setBounds(0, 500, 500, 100); //设置内容 label.setText("回车=》暂停或者重新开始; L=》网格线开关"); //设置字体颜色 label.setForeground(Color.BLACK); //添加入窗体 frame.add(label, BorderLayout.SOUTH); //游戏开始 controller.startGame(); } }
以上就是简单贪吃蛇的代码实现,相关函数参数的作用注释中也一一说明了。
实际效果如图
如果之后有想法的少年们,可以实现以下的一些操作:
1、 穿墙
2、 计分
3、 增加高分的神奇果实
4、 根据蛇的长度变速
5、 增添难度等级调整
等等