最简单的4个java类就可以实现贪吃蛇:
main函数:
1 package tcs; 2 3 public class GreedSnake { 4 5 6 public static void main(String[] args) { 7 8 SnakeModel model = new SnakeModel(20,30); 9 SnakeControl control = new SnakeControl(model); 10 SnakeView view = new SnakeView(model,control); 11 //添加一个观察者,让view成为model的观察者 12 model.addObserver(view); 13 14 (new Thread(model)).start(); 15 16 } 17 18 }
控制蛇的移动类:
1 package tcs; 2 3 import java.awt.event.KeyEvent; 4 import java.awt.event.KeyListener; 5 6 7 public class SnakeControl implements KeyListener{ 8 SnakeModel model; 9 10 public SnakeControl(SnakeModel model){ 11 this.model = model; 12 } 13 14 public void keyPressed(KeyEvent e) { 15 int keyCode = e.getKeyCode(); 16 if (model.running){ // 运行状态下,处理的按键 17 switch (keyCode) { 18 case KeyEvent.VK_UP: 19 model.changeDirection(SnakeModel.UP); 20 break; 21 case KeyEvent.VK_DOWN: 22 model.changeDirection(SnakeModel.DOWN); 23 break; 24 case KeyEvent.VK_LEFT: 25 model.changeDirection(SnakeModel.LEFT); 26 break; 27 case KeyEvent.VK_RIGHT: 28 model.changeDirection(SnakeModel.RIGHT); 29 break; 30 case KeyEvent.VK_ADD: 31 case KeyEvent.VK_PAGE_UP: 32 model.speedUp(); 33 break; 34 case KeyEvent.VK_SUBTRACT: 35 case KeyEvent.VK_PAGE_DOWN: 36 model.speedDown(); 37 break; 38 case KeyEvent.VK_SPACE: 39 case KeyEvent.VK_P: 40 model.changePauseState(); 41 break; 42 default: 43 } 44 } 45 46 // 任何情况下处理的按键,按键导致重新启动游戏 47 if (keyCode == KeyEvent.VK_R || 48 keyCode == KeyEvent.VK_S || 49 keyCode == KeyEvent.VK_ENTER) { 50 model.reset(); 51 } 52 } 53 54 public void keyReleased(KeyEvent e) { 55 } 56 57 public void keyTyped(KeyEvent e) { 58 } 59 }
蛇实体类:
1 package tcs; 2 3 import javax.swing.*; 4 import java.util.Arrays; 5 import java.util.LinkedList; 6 import java.util.Observable; 7 import java.util.Random; 8 9 10 class SnakeModel extends Observable implements Runnable { 11 boolean[][] matrix; // 指示位置上有没蛇体或食物 12 @SuppressWarnings("rawtypes") 13 LinkedList nodeArray = new LinkedList(); // 蛇体 14 Node food; 15 int maxX; 16 int maxY; 17 int direction = 2; // 蛇运行的方向 18 boolean running = false; // 运行状态 19 20 int timeInterval = 50; // 时间间隔,毫秒 21 double speedChangeRate = 0.75; // 每次得速度变化率 22 boolean paused = false; // 暂停标志 23 24 int score = 0; // 得分 25 int countMove = 0; // 吃到食物前移动的次数 26 27 // UP and DOWN should be even 28 // RIGHT and LEFT should be odd 29 public static final int UP = 2; 30 public static final int DOWN = 4; 31 public static final int LEFT = 1; 32 public static final int RIGHT = 3; 33 34 public SnakeModel( int maxX, int maxY) { 35 this.maxX = maxX; 36 this.maxY = maxY; 37 38 reset(); 39 } 40 41 @SuppressWarnings("unchecked") 42 public void reset(){ 43 direction = SnakeModel.UP; // 蛇运行的方向 44 timeInterval = 200; // 时间间隔,毫秒 45 paused = false; // 暂停标志 46 score = 0; // 得分 47 countMove = 0; // 吃到食物前移动的次数 48 49 // initial matirx, 全部清0 50 matrix = new boolean[maxX][]; 51 for (int i = 0; i < maxX; ++i) { 52 matrix[i] = new boolean[maxY]; 53 Arrays.fill(matrix[i], false); 54 } 55 56 // initial the snake 57 // 初始化蛇体,如果横向位置超过20个,长度为10,否则为横向位置的一半 58 int initArrayLength = maxX > 20 ? 10 : maxX / 2; 59 nodeArray.clear(); 60 for (int i = 0; i < initArrayLength; ++i) { 61 int x = maxX / 2 + i;//maxX被初始化为20 62 int y = maxY / 2; //maxY被初始化为30 63 //nodeArray[x,y]: [10,15]-[11,15]-[12,15]~~[20,15] 64 //默认的运行方向向上,所以游戏一开始nodeArray就变为: 65 // [10,14]-[10,15]-[11,15]-[12,15]~~[19,15] 66 nodeArray.addLast(new Node(x, y)); 67 matrix[x][y] = true; 68 } 69 70 // 创建食物 71 food = createFood(); 72 matrix[food.x][food.y] = true; 73 } 74 75 public void changeDirection(int newDirection) { 76 // 改变的方向不能与原来方向同向或反向 77 if (direction % 2 != newDirection % 2) { 78 direction = newDirection; 79 } 80 } 81 82 83 @SuppressWarnings("unchecked") 84 public boolean moveOn() { 85 Node n = (Node) nodeArray.getFirst(); 86 int x = n.x; 87 int y = n.y; 88 89 // 根据方向增减坐标值 90 switch (direction) { 91 case UP: 92 y--; 93 break; 94 case DOWN: 95 y++; 96 break; 97 case LEFT: 98 x--; 99 break; 100 case RIGHT: 101 x++; 102 break; 103 } 104 105 // 如果新坐标落在有效范围内,则进行处理 106 if ((0 <= x && x < maxX) && (0 <= y && y < maxY)) { 107 108 if (matrix[x][y]) { // 如果新坐标的点上有东西(蛇体或者食物) 109 if (x == food.x && y == food.y) { // 吃到食物,成功 110 nodeArray.addFirst(food); // 从蛇头赠长 111 112 // 分数规则,与移动改变方向的次数和速度两个元素有关 113 int scoreGet = (10000 - 200 * countMove) / timeInterval; 114 score += scoreGet > 0 ? scoreGet : 10; 115 countMove = 0; 116 117 food = createFood(); // 创建新的食物 118 matrix[food.x][food.y] = true; // 设置食物所在位置 119 return true; 120 } else // 吃到蛇体自身,失败 121 return false; 122 123 } else { // 如果新坐标的点上没有东西(蛇体),移动蛇体 124 nodeArray.addFirst(new Node(x, y)); 125 matrix[x][y] = true; 126 n = (Node) nodeArray.removeLast(); 127 matrix[n.x][n.y] = false; 128 countMove++; 129 return true; 130 } 131 } 132 return false; // 触到边线,失败 133 } 134 135 public void run() { 136 running = true; 137 while (running) { 138 try { 139 Thread.sleep(timeInterval); 140 } catch (Exception e) { 141 break; 142 } 143 144 if (!paused) { 145 if (moveOn()) { 146 setChanged(); // Model通知View数据已经更新 147 notifyObservers(); 148 } else { 149 JOptionPane.showMessageDialog(null, 150 "you failed", 151 "Game Over", 152 JOptionPane.INFORMATION_MESSAGE); 153 break; 154 } 155 } 156 } 157 running = false; 158 } 159 160 private Node createFood() { 161 int x = 0; 162 int y = 0; 163 // 随机获取一个有效区域内的与蛇体和食物不重叠的位置 164 do { 165 Random r = new Random(); 166 x = r.nextInt(maxX); 167 y = r.nextInt(maxY); 168 } while (matrix[x][y]); 169 170 return new Node(x, y); 171 } 172 173 public void speedUp() { 174 timeInterval *= speedChangeRate; 175 } 176 177 public void speedDown() { 178 timeInterval /= speedChangeRate; 179 } 180 181 public void changePauseState() { 182 paused = !paused; 183 } 184 185 public String toString() { 186 String result = ""; 187 for (int i = 0; i < nodeArray.size(); ++i) { 188 Node n = (Node) nodeArray.get(i); 189 result += "[" + n.x + "," + n.y + "]"; 190 } 191 return result; 192 } 193 } 194 195 class Node { 196 int x; 197 int y; 198 199 Node(int x, int y) { 200 this.x = x; 201 this.y = y; 202 } 203 }
图形界面:
1 package tcs; 2 import javax.swing.*; 3 import java.awt.*; 4 import java.util.Iterator; 5 import java.util.LinkedList; 6 import java.util.Observable; 7 import java.util.Observer; 8 9 10 public class SnakeView implements Observer { 11 SnakeControl control = null; 12 SnakeModel model = null; 13 14 JFrame mainFrame; 15 Canvas paintCanvas; 16 JLabel labelScore; 17 18 public static final int canvasWidth = 200; 19 public static final int canvasHeight = 300; 20 21 public static final int nodeWidth = 10; 22 public static final int nodeHeight = 10; 23 24 public SnakeView(SnakeModel model, SnakeControl control) { 25 this.model = model; 26 this.control = control; 27 28 mainFrame = new JFrame("GreedSnake"); 29 30 Container cp = mainFrame.getContentPane(); 31 32 // 创建顶部的分数显示 33 labelScore = new JLabel("Score:"); 34 cp.add(labelScore, BorderLayout.NORTH); 35 36 // 创建中间的游戏显示区域 37 paintCanvas = new Canvas(); 38 paintCanvas.setSize(canvasWidth + 1, canvasHeight + 1); 39 paintCanvas.addKeyListener(control); 40 cp.add(paintCanvas, BorderLayout.CENTER); 41 42 // 创建底下的帮助栏 43 JPanel panelButtom = new JPanel(); 44 panelButtom.setLayout(new BorderLayout()); 45 JLabel labelHelp; 46 labelHelp = new JLabel("PageUp, PageDown for speed;", JLabel.CENTER); 47 panelButtom.add(labelHelp, BorderLayout.NORTH); 48 labelHelp = new JLabel("ENTER or R or S for start;", JLabel.CENTER); 49 panelButtom.add(labelHelp, BorderLayout.CENTER); 50 labelHelp = new JLabel("SPACE or P for pause", JLabel.CENTER); 51 panelButtom.add(labelHelp, BorderLayout.SOUTH); 52 cp.add(panelButtom, BorderLayout.SOUTH); 53 54 mainFrame.addKeyListener(control); 55 mainFrame.pack(); 56 mainFrame.setResizable(false); 57 mainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 58 mainFrame.setVisible(true); 59 } 60 61 @SuppressWarnings("rawtypes") 62 void repaint() { 63 Graphics g = paintCanvas.getGraphics(); 64 65 //draw background 66 g.setColor(Color.WHITE); 67 g.fillRect(0, 0, canvasWidth, canvasHeight); 68 69 // draw the snake 70 g.setColor(Color.BLACK); 71 LinkedList na = model.nodeArray; 72 Iterator it = na.iterator(); 73 while (it.hasNext()) { 74 Node n = (Node) it.next(); 75 drawNode(g, n); 76 } 77 78 // draw the food 79 g.setColor(Color.RED); 80 Node n = model.food; 81 drawNode(g, n); 82 83 updateScore(); 84 } 85 86 private void drawNode(Graphics g, Node n) { 87 g.fillRect(n.x * nodeWidth, 88 n.y * nodeHeight, 89 nodeWidth - 1, 90 nodeHeight - 1); 91 } 92 93 public void updateScore() { 94 String s = "Score: " + model.score; 95 labelScore.setText(s); 96 } 97 98 public void update(Observable o, Object arg) { 99 repaint(); 100 } 101 }