`
Chris_bing
  • 浏览: 6800 次
  • 性别: Icon_minigender_1
  • 来自: 北京
最近访客 更多访客>>
社区版块
存档分类
最新评论

代码重构-以贪吃蛇为示例(二)-分离入口、内部类,抽离函数

阅读更多

 面对这么乱的代码,第一步就是把想关性不太大的部分抽离出去。具体操作:

 

  1. Direction类可以从原来文件中取出,放到同一个包下;
  2. 将main函数提出,放到GameLauncher.java中;
  3. 将原来的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

    在本篇博客“代码重构-以贪吃蛇为示例(五)-封装Scoring和SpeedManager”中,作者通过重构一个贪吃蛇游戏的代码,深入探讨了软件开发中的两个重要概念:分数管理(Scoring)和速度管理(SpeedManager)。...

    代码重构-以贪吃蛇为示例(三)-封装Snake

    在本篇博文中,我们将深入探讨“代码重构”的概念,并以经典的贪吃蛇游戏为例,进行实际的重构实践,特别是如何对游戏中的主角——Snake(贪吃蛇)进行封装。这个过程将涉及到软件工程中的模块化、面向对象设计原则...

    13--[贪吃蛇].zip源码scratch2.0 3.0编程项目源文件源码案例素材源代码

    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--[贪吃蛇].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--[贪吃蛇].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--[贪吃蛇].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--[贪吃蛇].zip源码scratch2.0 3.0编程项目源文件源码案例素材源代码1--...

    8--[贪吃蛇大作战].zip源码scratch2.0 3.0编程项目源文件源码案例素材源代码

    8--[贪吃蛇大作战].zip源码scratch2.0 3.0编程项目源文件源码案例素材源代码8--[贪吃蛇大作战].zip源码scratch2.0 3.0编程项目源文件源码案例素材源代码8--[贪吃蛇大作战].zip源码scratch2.0 3.0编程项目源文件源码...

    经典游戏--贪吃蛇的VC源代码

    它以其独特的游戏机制和无尽的趣味性,吸引了无数编程爱好者进行二次开发和学习。在这个资源中,我们拥有了用VC++实现的贪吃蛇游戏的源代码,这为我们深入理解游戏逻辑、掌握C++编程技术提供了一个宝贵的实践平台。 ...

    Android示例程序Snake贪吃蛇代码

    【Android 示例程序 Snake 贪吃蛇代码】是一款经典的Android平台上的游戏开发示例,它基于Java编程语言实现,展示了如何在Android环境中构建一个简单的2D游戏。此项目旨在帮助开发者理解和学习Android游戏开发的基本...

    C语言版源代码---贪吃蛇.rar

    【贪吃蛇游戏简介】 贪吃蛇是一款经典的电子游戏,起源于1976年的"Blockade",后来在诺基亚手机上广泛流行。在这个游戏中,玩家控制一条不断增长的蛇,目标是在屏幕上捕食食物,每吃掉一个食物,蛇的长度就会增加。...

    javaweb毕业设计-JAVA贪吃蛇游戏毕业设计(源代码+lw)(可做课程设计).rar

    javaweb毕业设计-JAVA贪吃蛇游戏毕业设计(源代码+lw)(可做课程设计).rarjavaweb毕业设计-JAVA贪吃蛇游戏毕业设计(源代码+lw)(可做课程设计).rarjavaweb毕业设计-JAVA贪吃蛇游戏毕业设计(源代码+lw)(可做课程设计)....

    简单思路实现游戏----贪吃蛇源码

    在本文中,我们将深入探讨如何使用Cocos2d-x框架实现一个简单的“贪吃蛇”游戏。Cocos2d-x是一个流行的开源游戏开发框架,它使用C++编写,支持跨平台开发,适用于iOS、Android、Windows等多个操作系统。下面,我们将...

    Cocos2d-x贪吃蛇代码

    【Cocos2d-x贪吃蛇代码】是一个基于Cocos2d-x 3.14版本和Visual Studio 2013开发的经典游戏项目。Cocos2d-x是一款开源的游戏开发框架,它允许开发者使用C++、Lua或者JavaScript进行游戏编程,并能跨平台运行在iOS、...

    51单片机项目实战---贪吃蛇(测试版)

    本文主要介绍了一个基于51单片机的贪吃蛇游戏项目的实现,包括硬件原理图和源代码。51单片机是一种广泛用于教学和工业控制领域的经典单片机,它的应用可以加深对微控制器编程和硬件接口的理解。本文中的贪吃蛇项目...

    cocos2d-x贪吃蛇

    1. **初始状态**:贪吃蛇的初始长度为4格,位于游戏区域的左下角,拥有3条生命值,初始等级为1,移动速度为20。 2. **蛇的移动**:贪吃蛇默认向右移动,也可以通过玩家控制改变方向(上、下、左、右)。 3. **食物...

    微信小程序---贪吃蛇

    此外,微信小程序还提供了生命周期函数,如`onLoad`, `onReady`, `onShow`, `onHide`等,开发者可以根据这些函数在不同的阶段执行相应的操作,如初始化游戏状态、加载资源、处理页面显示等。 总的来说,开发“微信...

    Java课程设计--贪吃蛇 东软05-3

    【标题】"Java课程设计--贪吃蛇 东软05-3" 是一个基于Java编程语言实现的经典游戏——贪吃蛇的项目。这个项目可能是大学课程中的一个实践任务,旨在帮助学生掌握Java编程基础,理解面向对象编程的概念,以及增强问题...

    C语言作业-C语言贪吃蛇小游戏

    C语言作业-C语言贪吃蛇小游戏C语言作业-C语言贪吃蛇小游戏C语言作业-C语言贪吃蛇小游戏C语言作业-C语言贪吃蛇小游戏C语言作业-C语言贪吃蛇小游戏C语言作业-C语言贪吃蛇小游戏C语言作业-C语言贪吃蛇小游戏C语言作业-...

Global site tag (gtag.js) - Google Analytics