- 浏览: 6800 次
- 性别:
- 来自: 北京
最新评论
-
yanhuadesan:
写的相当不错,正好今天我也在写这个来着,学习了
代码重构-以贪吃蛇为示例(五)-封装Scoring和SpeedManager
面对这么乱的代码,第一步就是把想关性不太大的部分抽离出去。具体操作:
- Direction类可以从原来文件中取出,放到同一个包下;
- 将main函数提出,放到GameLauncher.java中;
- 将原来的Game类改为GamePanel,作为游戏的面板;
那么现在我们有三个文件:GameLauncher(启动程序),GamePanel(程序面板),Direction(方向类,作为工具)。
接下来我们要抽离函数。
首先构造函数:
public GamePanel () { keyMap.put(KeyEvent.VK_UP, Direction.UP); keyMap.put(KeyEvent.VK_DOWN, Direction.DOWM); keyMap.put(KeyEvent.VK_LEFT, Direction.LEFT); keyMap.put(KeyEvent.VK_RIGHT, Direction.RIGHT); Point p = new Point(random.nextInt(tableWidth - initsnakeLenght >> 1) + initsnakeLenght, random.nextInt(tableHeight - initsnakeLenght >> 1) + initsnakeLenght); snake.add(p); for (int i = 0; i < initsnakeLenght - 1; ++i) { p = direction.getPreviousPoint(p); snake.add(p); } /** * 游戏主循环线程 */ new Thread() { @Override public void run() { while (true) { if (System.currentTimeMillis() - crrTime > 500 / speed) { synchronized (GamePanel.class) { moveSnake(); if (!checkSnack()) { JOptionPane.showMessageDialog(null, "Game Over!"); return; } } repaint(); crrTime = System.currentTimeMillis(); } } }; }.start(); }
这里做了三个事情:初始化按键和方向的映射,初始化蛇的链表,开启游戏线程。其实还有一件事应该放在这里,就是初始化蛇的方向,我们作为第四件事。按照这个方式我们抽取4个函数:initKeyMap,initSnake,initGameLoop,getRandomDirection。
现在代码是这样:
public GamePanel () { initSnakeDirection(); initKeyMap(); initSnake(); initGameLoop(); } /** * 初始化蛇运行方向 */ private void initSnakeDirection() { direction = getRandomDirection(); } /** * 随机生成方向 * * @return 方向 */ private Direction getRandomDirection() { return da[random.nextInt(4)]; } /** * 初始化游戏线程 */ private void initGameLoop() { /** * 游戏主循环线程 */ new Thread() { @Override public void run() { while (true) { if (System.currentTimeMillis() - crrTime > 500 / speed) { synchronized (GamePanel.class) { moveSnake(); if (!checkSnack()) { JOptionPane.showMessageDialog(null, "Game Over!"); return; } } repaint(); crrTime = System.currentTimeMillis(); } } }; }.start(); } /** * 初始化蛇链表 */ private void initSnake() { Point p = new Point(random.nextInt(tableWidth - initsnakeLenght >> 1) + initsnakeLenght, random.nextInt(tableHeight - initsnakeLenght >> 1) + initsnakeLenght); snake.add(p); for (int i = 0; i < initsnakeLenght - 1; ++i) { p = direction.getPreviousPoint(p); snake.add(p); } } /** * 初始化按键和方向的映射 */ private void initKeyMap() { keyMap.put(KeyEvent.VK_UP, Direction.UP); keyMap.put(KeyEvent.VK_DOWN, Direction.DOWM); keyMap.put(KeyEvent.VK_LEFT, Direction.LEFT); keyMap.put(KeyEvent.VK_RIGHT, Direction.RIGHT); }
接下来是checkSnake
/** * 判断贪吃蛇是否撞墙或撞到自己 * * @return */ protected boolean checkSnack() { Point p = snake.getFirst(); int x = p.x, y = p.y; if (x < 0 || x >= tableWidth || y < 0 || y >= tableHeight) { return false; } Iterator<Point> it = snake.iterator(); it.next(); while (it.hasNext()) { Point pBody = it.next(); if (p.equals(pBody)) { return false; } } return true; }
这个函数做了两件事:检查是否碰到墙壁,检查是否和自己相撞。可以抽取两个函数 isAgainstWall和isAgainstSelf:
/** * 判断贪吃蛇是否撞墙或撞到自己 * * @return */ protected boolean checkSnack() { return !isAgainstWall() && !isAgainstSelf(); } /** * 判断蛇头是否撞到自己的身体,是则返回true,否返回false * @return */ private boolean isAgainstSelf() { Point p = snake.getFirst(); Iterator<Point> it = snake.iterator(); it.next(); while (it.hasNext()) { Point pBody = it.next(); if (p.equals(pBody)) { return true; } } return false; } /** * 判断蛇头是否撞到墙壁,是则返回true,否返回false * @return */ private boolean isAgainstWall() { Point p = snake.getFirst(); int x = p.x, y = p.y; return x < 0 || x >= tableWidth || y < 0 || y >= tableHeight; }
最后看一下paintComponent这复杂的大函数:
/** * 绘制图形 */ @Override protected void paintComponent(Graphics g) { g.setColor(new Color(0x555555)); g.clearRect(0, 0, tableWidth * sellSize, tableHeight * sellSize); for (int i = 0; i < tableWidth; i++) { for (int j = 0; j < tableHeight; ++j) { g.drawRect(i * sellSize, j * sellSize, sellSize, sellSize); } } g.setColor(new Color(0x3399cc)); for (Point p : snake) { g.fillRect(p.x * sellSize, p.y * sellSize, sellSize, sellSize); } g.setColor(new Color(0x115599)); Point p = snake.peek(); g.fillRect(p.x * sellSize, p.y * sellSize, sellSize, sellSize); g.setColor(new Color(0xdd7744)); g.fillRect(target.x * sellSize, target.y * sellSize, sellSize, sellSize); }
首先清空一下画布,然后画了整个的表格(可以说是地图),花蛇身和蛇头,最后画目标点(也就是虫子)。
/** * 绘制图形 */ @Override protected void paintComponent(Graphics g) { g.clearRect(0, 0, tableWidth * sellSize, tableHeight * sellSize); drawMap(g); drawSnake(g); drawTarget(g); } /** * 绘制目标点(虫子) * * @param g * 画布 */ private void drawTarget(Graphics g) { g.setColor(new Color(0xdd7744)); g.fillRect(target.x * sellSize, target.y * sellSize, sellSize, sellSize); } /** * 绘制蛇 * * @param g * 画布 */ private void drawSnake(Graphics g) { drawSnakeBody(g); drawSnakeHead(g); } /** * 绘制蛇头 * * @param g * 画布 */ private void drawSnakeHead(Graphics g) { g.setColor(new Color(0x115599)); Point p = snake.peek(); g.fillRect(p.x * sellSize, p.y * sellSize, sellSize, sellSize); } /** * 绘制蛇身 * * @param g * 画布 */ private void drawSnakeBody(Graphics g) { g.setColor(new Color(0x3399cc)); for (Point p : snake) { g.fillRect(p.x * sellSize, p.y * sellSize, sellSize, sellSize); } } /** * 绘制地图 * * @param g * 画布 */ private void drawMap(Graphics g) { g.setColor(new Color(0x555555)); for (int i = 0; i < tableWidth; i++) { for (int j = 0; j < tableHeight; ++j) { g.drawRect(i * sellSize, j * sellSize, sellSize, sellSize); } } }
总体代码预览:
package snakes; import java.awt.Color; import java.awt.Graphics; import java.awt.Point; import java.awt.event.KeyEvent; import java.awt.event.KeyListener; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedList; import java.util.Map; import java.util.Random; import javax.swing.JOptionPane; import javax.swing.JPanel; public class GamePanel extends JPanel implements KeyListener { private static final long serialVersionUID = -7269846451378790762L; private static final Random random = new Random(); /** * 分数 */ private int score = 0; /** * 每一个单元格的尺寸,像素 */ private final int sellSize = 20; /** * 地图横向包含的单元格数 */ private final int tableWidth = 30; /** * 地图纵向包含的单元格数 */ private final int tableHeight = 20; /** * 贪吃蛇的点链表 */ private final LinkedList<Point> snake = new LinkedList<Point>(); private final Direction[] da = { Direction.UP, Direction.DOWM, Direction.LEFT, Direction.RIGHT }; private Direction direction; /** * 虫子的位置 */ private Point target = new Point(random.nextInt(tableWidth), random.nextInt(tableHeight)); /** * 贪吃蛇初始长度 */ private final int initsnakeLenght = 3; private final Map<Integer, Direction> keyMap = new HashMap<Integer, Direction>(); /** * 移动速度 */ private volatile long speed = 1; private volatile long crrTime = System.currentTimeMillis(); public GamePanel () { initSnakeDirection(); initKeyMap(); initSnake(); initGameLoop(); } /** * 初始化蛇运行方向 */ private void initSnakeDirection() { direction = getRandomDirection(); } /** * 随机生成方向 * * @return 方向 */ private Direction getRandomDirection() { return da[random.nextInt(4)]; } /** * 初始化游戏线程 */ private void initGameLoop() { /** * 游戏主循环线程 */ new Thread() { @Override public void run() { while (true) { if (System.currentTimeMillis() - crrTime > 500 / speed) { synchronized (GamePanel.class) { moveSnake(); if (!checkSnack()) { JOptionPane.showMessageDialog(null, "Game Over!"); return; } } repaint(); crrTime = System.currentTimeMillis(); } } }; }.start(); } /** * 初始化蛇链表 */ private void initSnake() { Point p = new Point(random.nextInt(tableWidth - initsnakeLenght >> 1) + initsnakeLenght, random.nextInt(tableHeight - initsnakeLenght >> 1) + initsnakeLenght); snake.add(p); for (int i = 0; i < initsnakeLenght - 1; ++i) { p = direction.getPreviousPoint(p); snake.add(p); } } /** * 初始化按键和方向的映射 */ private void initKeyMap() { keyMap.put(KeyEvent.VK_UP, Direction.UP); keyMap.put(KeyEvent.VK_DOWN, Direction.DOWM); keyMap.put(KeyEvent.VK_LEFT, Direction.LEFT); keyMap.put(KeyEvent.VK_RIGHT, Direction.RIGHT); } /** * 判断贪吃蛇是否撞墙或撞到自己 * * @return */ protected boolean checkSnack() { return !isAgainstWall() && !isAgainstSelf(); } /** * 判断蛇头是否撞到自己的身体,是则返回true,否返回false * * @return */ private boolean isAgainstSelf() { Point p = snake.getFirst(); Iterator<Point> it = snake.iterator(); it.next(); while (it.hasNext()) { Point pBody = it.next(); if (p.equals(pBody)) { return true; } } return false; } /** * 判断蛇头是否撞到墙壁,是则返回true,否返回false * * @return */ private boolean isAgainstWall() { Point p = snake.getFirst(); int x = p.x, y = p.y; return x < 0 || x >= tableWidth || y < 0 || y >= tableHeight; } @Override public void keyPressed(KeyEvent e) {} @Override public void keyReleased(KeyEvent e) { Direction newd = keyMap.get(e.getKeyCode()); if (newd != null && direction.isAvailable(newd)) { direction = newd; synchronized (GamePanel.class) { moveSnake(); if (!checkSnack()) { JOptionPane.showMessageDialog(null, "Game Over!"); return; } } repaint(); crrTime = System.currentTimeMillis(); } } @Override public void keyTyped(KeyEvent e) {} /** * 移动贪吃蛇,包括吃虫 */ private void moveSnake() { snake.addFirst(direction.getNextPoint(snake.getFirst())); if (snake.getFirst().equals(target)) { target = new Point(random.nextInt(tableWidth), random.nextInt(tableHeight)); ++speed; ++score; } else { snake.removeLast(); } } /** * 绘制图形 */ @Override protected void paintComponent(Graphics g) { g.clearRect(0, 0, tableWidth * sellSize, tableHeight * sellSize); drawMap(g); drawSnake(g); drawTarget(g); } /** * 绘制目标点(虫子) * * @param g * 画布 */ private void drawTarget(Graphics g) { g.setColor(new Color(0xdd7744)); g.fillRect(target.x * sellSize, target.y * sellSize, sellSize, sellSize); } /** * 绘制蛇 * * @param g * 画布 */ private void drawSnake(Graphics g) { drawSnakeBody(g); drawSnakeHead(g); } /** * 绘制蛇头 * * @param g * 画布 */ private void drawSnakeHead(Graphics g) { g.setColor(new Color(0x115599)); Point p = snake.peek(); g.fillRect(p.x * sellSize, p.y * sellSize, sellSize, sellSize); } /** * 绘制蛇身 * * @param g * 画布 */ private void drawSnakeBody(Graphics g) { g.setColor(new Color(0x3399cc)); for (Point p : snake) { g.fillRect(p.x * sellSize, p.y * sellSize, sellSize, sellSize); } } /** * 绘制地图 * * @param g * 画布 */ private void drawMap(Graphics g) { g.setColor(new Color(0x555555)); for (int i = 0; i < tableWidth; i++) { for (int j = 0; j < tableHeight; ++j) { g.drawRect(i * sellSize, j * sellSize, sellSize, sellSize); } } } }
下节预告:抽离Snake类
发表评论
-
代码重构-以贪吃蛇为示例(五)-封装Scoring和SpeedManager
2012-10-21 22:50 1068继续题外话: 上一篇忘记发布到博客频道了,都没有人看到,这次 ... -
代码重构-以贪吃蛇为示例(四)-继续封装
2012-10-19 10:55 780题外话:本人第一次写技术文章,希望寻求鼓励啊,发了四篇了一个评 ... -
代码重构-以贪吃蛇为示例(三)-封装Snake
2012-10-17 15:44 1711通过上一节的分离我们可以使程序的流程更清楚,但是这些功能还是冗 ... -
代码重构-以贪吃蛇为示例(一)-重构之前
2012-10-16 15:56 1026题外话: 今天中午做到电脑前没事干,就写个贪吃蛇游戏,写着写 ... -
代码重构-以贪吃蛇为示例(序)
2012-10-16 15:22 972在正文之前,先说点题外话。 首先,作为一名学习了2年Ja ...
相关推荐
在本篇博客“代码重构-以贪吃蛇为示例(四)-继续封装”中,作者通过实现贪吃蛇游戏来阐述代码重构的重要性和具体实践。贪吃蛇游戏是许多程序员学习编程时的入门项目,它包含了基本的逻辑控制、用户输入处理以及游戏...
在本篇博客“代码重构-以贪吃蛇为示例(五)-封装Scoring和SpeedManager”中,作者通过重构一个贪吃蛇游戏的代码,深入探讨了软件开发中的两个重要概念:分数管理(Scoring)和速度管理(SpeedManager)。...
在本篇博文中,我们将深入探讨“代码重构”的概念,并以经典的贪吃蛇游戏为例,进行实际的重构实践,特别是如何对游戏中的主角——Snake(贪吃蛇)进行封装。这个过程将涉及到软件工程中的模块化、面向对象设计原则...
13--[贪吃蛇].zip源码scratch2.0 3.0编程项目源文件源码案例素材源代码13--[贪吃蛇].zip源码scratch2.0 3.0编程项目源文件源码案例素材源代码13--[贪吃蛇].zip源码scratch2.0 3.0编程项目源文件源码案例素材源代码13...
2--[贪吃蛇].zip源码scratch2.0 3.0编程项目源文件源码案例素材源代码2--[贪吃蛇].zip源码scratch2.0 3.0编程项目源文件源码案例素材源代码2--[贪吃蛇].zip源码scratch2.0 3.0编程项目源文件源码案例素材源代码2--...
41--[贪吃蛇].zip源码scratch2.0 3.0编程项目源文件源码案例素材源代码41--[贪吃蛇].zip源码scratch2.0 3.0编程项目源文件源码案例素材源代码41--[贪吃蛇].zip源码scratch2.0 3.0编程项目源文件源码案例素材源代码41...
54--[贪吃蛇].zip源码scratch2.0 3.0编程项目源文件源码案例素材源代码54--[贪吃蛇].zip源码scratch2.0 3.0编程项目源文件源码案例素材源代码54--[贪吃蛇].zip源码scratch2.0 3.0编程项目源文件源码案例素材源代码54...
1--[贪吃蛇].zip源码scratch2.0 3.0编程项目源文件源码案例素材源代码1--[贪吃蛇].zip源码scratch2.0 3.0编程项目源文件源码案例素材源代码1--[贪吃蛇].zip源码scratch2.0 3.0编程项目源文件源码案例素材源代码1--...
8--[贪吃蛇大作战].zip源码scratch2.0 3.0编程项目源文件源码案例素材源代码8--[贪吃蛇大作战].zip源码scratch2.0 3.0编程项目源文件源码案例素材源代码8--[贪吃蛇大作战].zip源码scratch2.0 3.0编程项目源文件源码...
它以其独特的游戏机制和无尽的趣味性,吸引了无数编程爱好者进行二次开发和学习。在这个资源中,我们拥有了用VC++实现的贪吃蛇游戏的源代码,这为我们深入理解游戏逻辑、掌握C++编程技术提供了一个宝贵的实践平台。 ...
【Android 示例程序 Snake 贪吃蛇代码】是一款经典的Android平台上的游戏开发示例,它基于Java编程语言实现,展示了如何在Android环境中构建一个简单的2D游戏。此项目旨在帮助开发者理解和学习Android游戏开发的基本...
【贪吃蛇游戏简介】 贪吃蛇是一款经典的电子游戏,起源于1976年的"Blockade",后来在诺基亚手机上广泛流行。在这个游戏中,玩家控制一条不断增长的蛇,目标是在屏幕上捕食食物,每吃掉一个食物,蛇的长度就会增加。...
javaweb毕业设计-JAVA贪吃蛇游戏毕业设计(源代码+lw)(可做课程设计).rarjavaweb毕业设计-JAVA贪吃蛇游戏毕业设计(源代码+lw)(可做课程设计).rarjavaweb毕业设计-JAVA贪吃蛇游戏毕业设计(源代码+lw)(可做课程设计)....
在本文中,我们将深入探讨如何使用Cocos2d-x框架实现一个简单的“贪吃蛇”游戏。Cocos2d-x是一个流行的开源游戏开发框架,它使用C++编写,支持跨平台开发,适用于iOS、Android、Windows等多个操作系统。下面,我们将...
【Cocos2d-x贪吃蛇代码】是一个基于Cocos2d-x 3.14版本和Visual Studio 2013开发的经典游戏项目。Cocos2d-x是一款开源的游戏开发框架,它允许开发者使用C++、Lua或者JavaScript进行游戏编程,并能跨平台运行在iOS、...
本文主要介绍了一个基于51单片机的贪吃蛇游戏项目的实现,包括硬件原理图和源代码。51单片机是一种广泛用于教学和工业控制领域的经典单片机,它的应用可以加深对微控制器编程和硬件接口的理解。本文中的贪吃蛇项目...
1. **初始状态**:贪吃蛇的初始长度为4格,位于游戏区域的左下角,拥有3条生命值,初始等级为1,移动速度为20。 2. **蛇的移动**:贪吃蛇默认向右移动,也可以通过玩家控制改变方向(上、下、左、右)。 3. **食物...
此外,微信小程序还提供了生命周期函数,如`onLoad`, `onReady`, `onShow`, `onHide`等,开发者可以根据这些函数在不同的阶段执行相应的操作,如初始化游戏状态、加载资源、处理页面显示等。 总的来说,开发“微信...
【标题】"Java课程设计--贪吃蛇 东软05-3" 是一个基于Java编程语言实现的经典游戏——贪吃蛇的项目。这个项目可能是大学课程中的一个实践任务,旨在帮助学生掌握Java编程基础,理解面向对象编程的概念,以及增强问题...
C语言作业-C语言贪吃蛇小游戏C语言作业-C语言贪吃蛇小游戏C语言作业-C语言贪吃蛇小游戏C语言作业-C语言贪吃蛇小游戏C语言作业-C语言贪吃蛇小游戏C语言作业-C语言贪吃蛇小游戏C语言作业-C语言贪吃蛇小游戏C语言作业-...