题外话:
今天中午做到电脑前没事干,就写个贪吃蛇游戏,写着写着就发现变量太多了,函数调用太乱了,逻辑越来越混乱,于是就想重构一下。但是有想一想,干脆把重构的过程记录下来,大家也分享分享,还能提高一下知名度,何乐而不为呢?
所以……所以……我硬着头皮在不重构的情况下写完了一个简单的贪吃蛇(WTF!)
/*--------------------------------------------无耻的分隔栏----------------------------------------------------- */
介绍下功能:
Swing组件完成的贪吃蛇游戏,通过键盘的方向键改变方向,吃到虫子增长长度,每吃掉一个虫子,移动速度会增长,撞到四周或者自己提示游戏结束。记录分数(但是没显示,因为不重构写不下去了),没有重新游戏(留到重构以后再写)。
界面:
(恐怖的)代码:
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.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
public class Game extends JPanel implements KeyListener {
/**
* 贪吃蛇行走的方向
*/
enum Direction {
UP {
@Override
public Point getNextPoint(Point p) {
return new Point(p.x, p.y - 1);
}
@Override
public Point getPreviousPoint(Point p) {
return new Point(p.x, p.y + 1);
}
@Override
public boolean isAvailable(Direction d) {
if (d == Direction.UP || d == Direction.DOWM) {
return false;
}
return true;
}
},
DOWM {
@Override
public Point getNextPoint(Point p) {
return new Point(p.x, p.y + 1);
}
@Override
public Point getPreviousPoint(Point p) {
return new Point(p.x, p.y - 1);
}
@Override
public boolean isAvailable(Direction d) {
if (d == Direction.UP || d == Direction.DOWM) {
return false;
}
return true;
}
},
LEFT {
@Override
public Point getNextPoint(Point p) {
return new Point(p.x - 1, p.y);
}
@Override
public Point getPreviousPoint(Point p) {
return new Point(p.x + 1, p.y);
}
@Override
public boolean isAvailable(Direction d) {
if (d == LEFT || d == RIGHT) {
return false;
}
return true;
}
},
RIGHT {
@Override
public Point getNextPoint(Point p) {
return new Point(p.x + 1, p.y);
}
@Override
public Point getPreviousPoint(Point p) {
return new Point(p.x - 1, p.y);
}
@Override
public boolean isAvailable(Direction d) {
if (d == LEFT || d == RIGHT) {
return false;
}
return true;
}
};
/**
* 沿此方向的下一个点
* @param p
* @return
*/
public abstract Point getNextPoint(Point p);
/**
* 沿此方向的上一个点
* @param p
* @return
*/
public abstract Point getPreviousPoint(Point p);
/**
* 可以转动的方向
* @param d
* @return
*/
public abstract boolean isAvailable(Direction d);
}
private static final long serialVersionUID = -7269846451378790762L;
private static final Random random = new Random();
public static void main(String[] args) {
JFrame j = new JFrame("贪吃蛇");
Game contentPane = new Game();
j.setContentPane(contentPane);
j.addKeyListener(contentPane);
j.setBounds(20, 20, 700, 500);
j.setVisible(true);
j.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
/**
* 分数
*/
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 = da[random.nextInt(4)];
/**
* 虫子的位置
*/
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 Game() {
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 (Game.class) {
moveSnake();
if (!checkSnack()) {
JOptionPane.showMessageDialog(null,
"Game Over!");
return;
}
}
repaint();
crrTime = System.currentTimeMillis();
}
}
};
}.start();
}
/**
* 判断贪吃蛇是否撞墙或撞到自己
* @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;
}
@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 (Game.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.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);
}
}
下节预告:拆分文件