<span style="font-size:18px;">package com.lovo; /** * 方向(枚举类) * @author hechao * */ public enum Direction { UP, DOWN, LEFT, RIGHT }</span>
<span style="font-size:18px;">package com.lovo; import java.awt.Color; import java.awt.Graphics; public class Egg { private int x, y; private int size = GameContext.EGG_SIZE; public Egg() { int minX = GameContext.WALL_X / 20; int maxX = (GameContext.SQUARE_SIZE - GameContext.EGG_SIZE) / 20; x = (int) (Math.random() * maxX + minX) * 20; int minY = GameContext.WALL_Y / 20; int maxY = maxX; y = (int) (Math.random() * maxY + minY) * 20; } public int getX() { return x; } public int getY() { return y; } public void draw(Graphics g) { g.setColor(Color.ORANGE); g.fillOval(x, y, size, size); } } </span>
<span style="font-size:18px;">package com.lovo; public class GameContext { /** * 蛇的长度 */ public static final int SNAKE_LENGTH = 1; /** * 蛇头初始位置的横坐标 */ public static final int SNAKE_X = 260; /** * 蛇头初始位置的纵坐标 */ public static final int SNAKE_Y = 280; /** * 蛇节点的大小 */ public static final int SNAKE_SIZE = 20; /** * 游戏窗口的大小 */ public static final int GAME_SIZE = 580; /** * 游戏场地的大小 */ public static final int SQUARE_SIZE = 500; /** * 刷新周期 */ public static final int INTERVAL = 200; /** * 围墙起点的横坐标 */ public static final int WALL_X = 40; /** * 围墙起点的纵坐标 */ public static final int WALL_Y = 40; /** * 围墙厚度 */ public static final int WALL_WIDTH = 5; /** * 蛋的大小 */ public static final int EGG_SIZE = SNAKE_SIZE; } </span>
<pre name="code" class="java">package com.lovo; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Image; import java.awt.Stroke; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; import java.awt.image.BufferedImage; import java.util.LinkedList; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.Timer; @SuppressWarnings("serial") public class GameFrame extends JFrame { private Image offImage = new BufferedImage(GameContext.GAME_SIZE, GameContext.GAME_SIZE, 1); private Timer timer = null; private int score; private Snake snake = new Snake(); // 创建蛇的对象 private Egg egg = new Egg(); // 创建蛋的对象 private JLabel scoreLable; private LinkedList<SnakeNode> list = snake.getList(); public GameFrame() { this.setTitle("贪吃蛇"); this.setSize(GameContext.GAME_SIZE, GameContext.GAME_SIZE); this.setResizable(false); this.setLocationRelativeTo(null); this.setDefaultCloseOperation(EXIT_ON_CLOSE); this.setLayout(null); this.addKeyListener(new KeyAdapter() { @Override public void keyPressed(KeyEvent e) { Direction newDir = snake.getDir(); switch(e.getKeyCode()) { case KeyEvent.VK_W: //上 newDir = Direction.UP; break; case KeyEvent.VK_S: // 下 newDir = Direction.DOWN; break; case KeyEvent.VK_A: // 左 newDir = Direction.LEFT; break; case KeyEvent.VK_D: // 右 newDir = Direction.RIGHT; break; case KeyEvent.VK_F2: resetGame(); break; } if(snake.getDir() != newDir) { // 如果新方向不同于蛇原来的方向 snake.changeDir(newDir); // 调用蛇的changeDir方法修改蛇的方向 } } /** * 重置游戏 */ public void resetGame(){ score = 0; list.clear(); snake.resetSnake(); timer.start(); egg = new Egg(); repaint(); } }); timer = new Timer(GameContext.INTERVAL, new ActionListener() { @Override public void actionPerformed(ActionEvent e) { snake.move(); // 调用蛇的move方法让蛇移动 if(snake.eatEgg(egg)) { // 吃到蛋 snake.grow(); // 调用蛇的grow方法让蛇变长 score++; egg = new Egg(); // 重新创建一颗蛋 } SnakeNode head = snake.getHead(); int x = head.getX(); int y = head.getY(); for(int i = 1; i < list.size(); i++){ SnakeNode sn = list.get(i); if(sn.getX() == x && sn.getY() == y){ timer.stop(); } } if(x < GameContext.WALL_X || x > GameContext.WALL_X + GameContext.SQUARE_SIZE - GameContext.SNAKE_SIZE || y < GameContext.WALL_Y || y > GameContext.WALL_Y + GameContext.SQUARE_SIZE - GameContext.SNAKE_SIZE) { timer.stop(); } else { repaint(); } } }); scoreLable = new JLabel(); scoreLable.setBounds(50, 530, 100, 15); GameFrame.this.add(scoreLable); timer.start(); } @Override public void paint(Graphics g) { // 使用双缓冲方式消除窗口闪烁(见下面步骤1-2-3) // 1. 获得内存中图像的画笔 Graphics offG = offImage.getGraphics(); // 2. 用offG在内存中绘制窗口上的所有内容 super.paint(offG); // 消除残影 Graphics2D g2d = (Graphics2D) offG; // 将Graphics转成其子类型Graphics2D并设置粗细 Stroke oldStroke = g2d.getStroke(); // 记录画笔粗细(保存现场) g2d.setStroke(new BasicStroke(GameContext.WALL_WIDTH)); // 将画笔加粗 offG.setColor(Color.BLACK); // 设置画笔为黑色 int x = GameContext.WALL_X, y = GameContext.WALL_Y; offG.drawRect(x, y, GameContext.SQUARE_SIZE, GameContext.SQUARE_SIZE); // 绘制围墙 g2d.setStroke(oldStroke); // 还原画笔粗细(恢复现场) snake.draw(offG); // 调用蛇的draw方法绘制蛇 egg.draw(offG); // 调用蛋的draw方法绘制蛋 // 3. 将内存中绘制好的图直接画到窗口上(双缓冲) g.drawImage(offImage, 0, 0, null); // 第二个和第三个参数是绘图的位置 scoreLable.setText("得分:" + score); } }
<span style="font-size:18px;">package com.lovo; class GameRunner { public static void main(String[] args) { new GameFrame().setVisible(true); } } </span>
<pre name="code" class="java">package com.lovo; import java.awt.Graphics; import java.util.LinkedList; /** * 蛇 * @author 何超 * */ public class Snake { private Direction dir = Direction.LEFT; // 方向 private LinkedList<SnakeNode> list = new LinkedList<SnakeNode>(); // 装蛇的所有节点的容器(链表) /** * 构造器 */ public Snake() { // 初始化5个节点 for (int i = 0; i < GameContext.SNAKE_LENGTH; i++) { list.add(new SnakeNode(GameContext.SNAKE_X + i * GameContext.SNAKE_SIZE, GameContext.SNAKE_Y)); } } /** * 获得蛇结点容器 * @return 蛇结点容器 */ public LinkedList<SnakeNode> getList(){ return list; } /** * 改变方向 * @param newDir 新的方向 */ public void changeDir(Direction newDir) { if(!((dir == Direction.LEFT && newDir == Direction.RIGHT) || (dir == Direction.RIGHT && newDir == Direction.LEFT) || (dir == Direction.UP && newDir == Direction.DOWN) || (dir == Direction.DOWN && newDir == Direction.UP))) { dir = newDir; } } /** * 获得蛇前进的方向 */ public Direction getDir() { return dir; } /** * 获得蛇头 */ public SnakeNode getHead() { return list.get(0); // 容器中的第一个节点就是蛇头 } /** * 移动 */ public void move() { grow(); list.removeLast(); // 尾巴删一个节点 } /** * 吃蛋 * @param egg 蛋 * @return 吃到蛋返回true否则返回false */ public boolean eatEgg(Egg egg) { SnakeNode head = getHead(); return head.getX() == egg.getX() && head.getY() == egg.getY(); } /** * 长长一节 */ public void grow() { SnakeNode head = getHead(); int x = head.getX(), y = head.getY(); int size = GameContext.SNAKE_SIZE; switch(dir) { case UP: y -= size; break; case DOWN: y += size; break; case LEFT: x -= size; break; case RIGHT: x += size; break; } SnakeNode newHead = new SnakeNode(x, y); // 创建新的蛇头节点 list.addFirst(newHead); // 头上加一个节点 } /** * 重置 */ public void resetSnake() { // 初始化5个节点 for (int i = 0; i < GameContext.SNAKE_LENGTH; i++) { list.add(new SnakeNode(GameContext.SNAKE_X + i * GameContext.SNAKE_SIZE, GameContext.SNAKE_Y)); } } /** * 绘制蛇 */ public void draw(Graphics g) { // 绘制蛇身上的每个节点 for(SnakeNode node : list) { node.draw(g); } } }
<span style="font-size:18px;">package com.lovo; import java.awt.Color; import java.awt.Graphics; /** * 蛇身上的节点 * @author 骆昊 * */ public class SnakeNode { private int x, y; private int size = GameContext.SNAKE_SIZE; /** * 构造器 */ public SnakeNode(int x, int y) { this.x = x; this.y = y; } /** * 蛇节点的横坐标 */ public int getX() { return x; } /** * 蛇节点的纵坐标 */ public int getY() { return y; } /** * 绘制 */ public void draw(Graphics g) { g.setColor(Color.GREEN); g.fillRect(x, y, size, size); g.setColor(Color.BLACK); g.drawRect(x, y, size, size); } } </span>