对大多数人来说,要想自己编写出《贪吃蛇》游戏的程序代码,是有较大难度的。可是,很多初学者一直梦想和追求着自己亲手编写出一个《贪吃蛇》的游戏程序。为了帮助大家实现心愿,向大家讲解和展示了传智播客版《贪吃蛇游戏》的每一行代码的编写过程。
贪吃蛇游戏开发学习包括15小节,依次为:
01_游戏功能演示与说明
02_游戏中的面向对象分析与设计
03_使用传智播客提供的API类组装贪吃蛇游戏
04_编写贪吃蛇游戏中的各个类的主体框架性代码
05_编写Controler类与实现蛇移动的事件监听
06_编写对各个类进行测试的程序代码
07_蛇的数据结构设计与移动显示
08_测试与修正蛇的移动与显示问题
09_排除蛇的相反方向与无效方向的按键问题
10_编写与测试表示食物的类
11_完成蛇吃食物的相关代码
12_为游戏增加作为障碍物的石头
13_为蛇增加吃到石头而死掉的功能
14_解决食物与石头蛇身重叠的问题
15_为蛇增加吃到蛇身而死掉的功能
窗体界面如下
游戏入口:
package cn.itcast.snake.game;
import java.awt.Color;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.FocusAdapter;
import java.awt.event.FocusEvent;
import javax.swing.JColorChooser;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextArea;
import javax.swing.border.EtchedBorder;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
import cn.itcast.snake.controller.Controller;
import cn.itcast.snake.entities.Food;
import cn.itcast.snake.entities.Ground;
import cn.itcast.snake.entities.Snake;
import cn.itcast.snake.listener.GameListener;
import cn.itcast.snake.util.Global;
import cn.itcast.snake.view.GamePanel;
/**
* 主界面, 实现了 GameListener 接口
*
* @version 1.0, 01/01/08
*
* @author 汤阳光
*
*/
public class MainFrame extends JFrame implements GameListener {
/**
*
*/
private static final long serialVersionUID = 1L;
/**
* Launch the application
*
* @param args
*/
public static void main(String args[]) {
try {
MainFrame frame = new MainFrame(new Controller(new Snake(),
new Food(), new Ground(), new GamePanel(), new JLabel()));
frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
private final GameOptionPanel optionPanel;
private final GamePanel gamePanel;
private final Snake snake;
private final Ground ground;
private final Food food;
private final JLabel infoLabel;
private final Controller controller;
/**
* Create the frame
*/
public MainFrame(Controller c) {
super();
this.controller = c;
this.setTitle("贪吃蛇");
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setLayout(null);
this.setResizable(false);
int left = 10;
optionPanel = new GameOptionPanel();
/** =======* */
gamePanel = c.getGamePanel();
snake = c.getSnake();
ground = c.getGround();
food = c.getFood();
infoLabel = c.getGameInfoLabel() == null ? new JLabel() : c
.getGameInfoLabel();
c.setGameInfoLabel(infoLabel);
optionPanel.getButton_griddingColor().addActionListener(
new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
Color griddingColor = JColorChooser.showDialog(
MainFrame.this, "请选择网格的颜色", Color.LIGHT_GRAY);
if (griddingColor != null)
ground.setGriddingColor(griddingColor);
}
});
optionPanel.getButton_backgroundColor().addActionListener(
new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
Color backgroundColor = JColorChooser
.showDialog(MainFrame.this, "请选择背景的颜色",
new Color(0xcfcfcf));
if (backgroundColor != null)
gamePanel.setBackgroundColor(backgroundColor);
}
});
optionPanel.getButton_foodColor().addActionListener(
new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
Color foodColor = JColorChooser.showDialog(
MainFrame.this, "请选择食物的颜色", Color.DARK_GRAY);
if (foodColor != null)
food.setColor(foodColor);
}
});
optionPanel.getButton_headColor().addActionListener(
new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
Color headColor = JColorChooser
.showDialog(MainFrame.this, "请选择蛇头的颜色",
new Color(0xFF4500));
if (headColor != null)
snake.setHeadColor(headColor);
}
});
optionPanel.getButton_bodyColor().addActionListener(
new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
Color bodyColor = JColorChooser.showDialog(
MainFrame.this, "请选择蛇身体的颜色", Color.DARK_GRAY);
if (bodyColor != null)
snake.setBodyColor(bodyColor);
}
});
this.addFocusListener(new FocusAdapter() {
public void focusLost(FocusEvent arg0) {
controller.pauseGame();
if (optionPanel.getPauseButton().isEnabled())
optionPanel.getPauseButton().setText("继续游戏");
}
});
gamePanel.addFocusListener(new FocusAdapter() {
public void focusGained(FocusEvent arg0) {
// controller.continueGame();
}
public void focusLost(FocusEvent arg0) {
controller.pauseGame();
if (optionPanel.getPauseButton().isEnabled())
optionPanel.getPauseButton().setText("继续游戏");
}
});
optionPanel.getRadioButton_map2().addChangeListener(
new ChangeListener() {
public void stateChanged(ChangeEvent e) {
controller.setMap(optionPanel.getRadioButton_map2()
.isSelected() ? 2 : 1);
}
});
optionPanel.getNewGameButton().addActionListener(new ActionListener() {
/**
* 开始游戏的按钮
*/
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
if (controller.isPlaying()) {
return;
}
controller.newGame();
}
});
optionPanel.getStopGameButton().addActionListener(new ActionListener() {
/**
* 停止游戏的按钮
*/
public void actionPerformed(ActionEvent e) {
controller.stopGame();
}
});
optionPanel.getPauseButton().setEnabled(false);
optionPanel.getStopGameButton().setEnabled(false);
optionPanel.getPauseButton().addActionListener(new ActionListener() {
/**
* 暂停/继续游戏的按钮
*/
public void actionPerformed(ActionEvent e) {
if (controller.isPausingGame()) {
controller.continueGame();
} else {
controller.pauseGame();
}
if (controller.isPausingGame())
optionPanel.getPauseButton().setText("继续游戏");
else
optionPanel.getPauseButton().setText("暂停游戏");
}
});
optionPanel.getCheckBox_drawGridding().addChangeListener(
new ChangeListener() {
public void stateChanged(ChangeEvent arg0) {
optionPanel.getButton_griddingColor().setVisible(
optionPanel.getCheckBox_drawGridding()
.isSelected());
ground.setDrawGridding(optionPanel
.getCheckBox_drawGridding().isSelected());
}
});
optionPanel.getButton_default().addActionListener(new ActionListener() {
/**
* 恢复默认设置的按钮
*/
public void actionPerformed(ActionEvent e) {
gamePanel
.setBackgroundColor(GamePanel.DEFAULT_BACKGROUND_COLOR);
optionPanel.getCheckBox_drawGridding().setSelected(false);
ground.setGriddingColor(Ground.DEFAULT_GRIDDING_COLOR);
snake.setHeadColor(Snake.DEFAULT_HEAD_COLOR);
snake.setBodyColor(Snake.DEFAULT_BODY_COLOR);
optionPanel.getRadioButton_map1().setSelected(true);
}
});
/** ******************* */
infoLabel.setBounds(10, 0, infoLabel.getSize().width - 10, infoLabel
.getSize().height);
gamePanel.setBounds(0, infoLabel.getSize().height,
gamePanel.getSize().width, gamePanel.getSize().height);
/**
* subPanel
*/
JPanel subPanel = new JPanel();
subPanel.setLayout(null);
subPanel.setBorder(new EtchedBorder(EtchedBorder.LOWERED));
subPanel.setFocusable(false);
subPanel.setSize(gamePanel.getSize().width + 1,
infoLabel.getSize().height + gamePanel.getSize().height + 1);
subPanel.setBounds(left, 5, subPanel.getSize().width, subPanel
.getSize().height);
subPanel.add(infoLabel);
subPanel.add(gamePanel);
optionPanel.setBounds(left, subPanel.getSize().height + 10, optionPanel
.getSize().width, optionPanel.getSize().height);
/**
* 说明
*/
JPanel infoPanel = new JPanel();
infoPanel.setBorder(new EtchedBorder(EtchedBorder.LOWERED));
infoPanel.setLayout(null);
infoPanel.setBounds(left + optionPanel.getSize().width + 5, subPanel
.getSize().height + 10, gamePanel.getSize().width
- optionPanel.getSize().width - 5 + 1,
optionPanel.getSize().height);
final JLabel infoTitleLable = new JLabel();
infoTitleLable.setFont(new Font("宋体", Font.PLAIN, 12));
infoTitleLable.setText(Global.TITLE_LABEL_TEXT);
infoTitleLable.setBounds(10, 5, infoPanel.getSize().width - 10, 20);
final JTextArea infoTextArea = new JTextArea();
infoTextArea.setFont(new Font("宋体", Font.PLAIN, 12));
infoTextArea.setText(Global.INFO_LABEL_TEXT);
infoTextArea.setFocusable(false);
infoTextArea.setBackground(this.getBackground());
infoTextArea.setBounds(10, 25, infoPanel.getSize().width - 20,
infoPanel.getSize().height - 50);
infoPanel.add(infoTitleLable);
infoPanel.add(infoTextArea);
optionPanel.getCheckBox_drawGridding().setFocusable(false);
optionPanel.getRadioButton_map1().setFocusable(false);
optionPanel.getRadioButton_map2().setFocusable(false);
this
.setSize(
subPanel.getSize().width > optionPanel.getSize().width ? gamePanel
.getSize().width
+ 2 * left + 8
: optionPanel.getSize().width + 2 * left + 8,
subPanel.getSize().height + 20/* 边框 */
+ optionPanel.getSize().height + 30);
/* 让窗口居中 */
this.setLocation(this.getToolkit().getScreenSize().width / 2
- this.getWidth() / 2, this.getToolkit().getScreenSize().height
/ 2 - this.getHeight() / 2);
/* 添加监听器 */
gamePanel.addKeyListener(controller);
this.addKeyListener(controller);
controller.addGameListener(this);
this.getContentPane().add(subPanel);
this.getContentPane().add(optionPanel);
this.getContentPane().add(infoPanel);
}
public void gameOver() {
// TODO Auto-generated method stub
optionPanel.getPauseButton().setEnabled(false);
optionPanel.getStopGameButton().setEnabled(false);
optionPanel.getNewGameButton().setEnabled(true);
optionPanel.getPauseButton().setText("暂停/继续");
}
public void gameStart() {
// TODO Auto-generated method stub
optionPanel.getPauseButton().setEnabled(true);
optionPanel.getNewGameButton().setEnabled(false);
optionPanel.getStopGameButton().setEnabled(true);
}
public void gameContinue() {
// TODO Auto-generated method stub
optionPanel.getPauseButton().setText("暂停游戏");
}
public void gamePause() {
// TODO Auto-generated method stub
optionPanel.getPauseButton().setText("继续游戏");
}
}
package cn.itcast.snake.game;
import java.awt.Font;
import javax.swing.ButtonGroup;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.JSeparator;
import javax.swing.SwingConstants;
import javax.swing.border.EtchedBorder;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
/**
* 游戏的设置项面板
*
* @version 1.0, 01/01/08
*
* @author 汤阳光
*
*/
public class GameOptionPanel extends JPanel {
/**
*
*/
private static final long serialVersionUID = 1L;
private ButtonGroup buttonGroup = new ButtonGroup();
private ImageIcon czbkIcon = new ImageIcon(GameOptionPanel.class
.getResource("czbk.png"));
private JFrame frame;
private final JButton newGameButton = new JButton();
private final JButton stopGameButton = new JButton();
private final JButton pauseButton = new JButton();
private final JCheckBox checkBox_drawGridding = new JCheckBox();
private final JRadioButton radioButton_map1;
private final JRadioButton radioButton_map2;
private final JButton button_griddingColor;
private final JButton button_backgroundColor;
private final JButton button_foodColor;
private final JButton button_headColor;
private final JButton button_bodyColor;
private final JButton button_default;
/**
* Create the panel
*/
public GameOptionPanel() {
super();
setSize(450, 185);
setLayout(null);
setBorder(new EtchedBorder(EtchedBorder.LOWERED));
setFocusable(false);
final JSeparator separator = new JSeparator();
separator.setBounds(140, 55, 156, 50);
add(separator);
button_griddingColor = new JButton();
button_griddingColor.setBounds(85, 10, 60, 23);
separator.add(button_griddingColor);
button_griddingColor.setFont(new Font("宋体", Font.PLAIN, 12));
button_griddingColor.setFocusable(false);
button_griddingColor.setText("颜色");
button_griddingColor.setVisible(false);
checkBox_drawGridding.addChangeListener(new ChangeListener() {
public void stateChanged(ChangeEvent arg0) {
button_griddingColor.setVisible(checkBox_drawGridding
.isSelected());
}
});
checkBox_drawGridding.setBounds(5, 10, 78, 23);
separator.add(checkBox_drawGridding);
checkBox_drawGridding.setText("显示网格");
checkBox_drawGridding.setFont(new Font("宋体", Font.PLAIN, 12));
final JSeparator separator_6 = new JSeparator();
separator_6.setBounds(10, 55, 119, 33);
add(separator_6);
button_backgroundColor = new JButton();
button_backgroundColor.setBounds(5, 10, 110, 23);
separator_6.add(button_backgroundColor);
button_backgroundColor.setFont(new Font("宋体", Font.PLAIN, 12));
button_backgroundColor.setFocusable(false);
button_backgroundColor.setText("设置背景颜色");
final JSeparator separator_4 = new JSeparator();
separator_4.setBounds(10, 135, 286, 39);
add(separator_4);
button_foodColor = new JButton();
button_foodColor.setBounds(5, 10, 111, 23);
separator_4.add(button_foodColor);
button_foodColor.setFont(new Font("宋体", Font.PLAIN, 12));
button_foodColor.setFocusable(false);
button_foodColor.setText("设置食物颜色");
radioButton_map1 = new JRadioButton();
radioButton_map1.setFont(new Font("宋体", Font.PLAIN, 12));
radioButton_map1.setSelected(true);
buttonGroup.add(radioButton_map1);
radioButton_map1.setText("地图1");
radioButton_map1.setBounds(135, 10, 63, 23);
separator_4.add(radioButton_map1);
radioButton_map2 = new JRadioButton();
radioButton_map2.setFont(new Font("宋体", Font.PLAIN, 12));
buttonGroup.add(radioButton_map2);
radioButton_map2.setText("地图2");
radioButton_map2.setBounds(198, 10, 63, 23);
separator_4.add(radioButton_map2);
final JSeparator separator_1 = new JSeparator();
separator_1.setBounds(10, 95, 286, 39);
add(separator_1);
button_headColor = new JButton();
button_headColor.setBounds(5, 10, 110, 23);
separator_1.add(button_headColor);
button_headColor.setFont(new Font("宋体", Font.PLAIN, 12));
button_headColor.setFocusable(false);
button_headColor.setText("设置蛇头颜色");
button_bodyColor = new JButton();
button_bodyColor.setBounds(135, 10, 113, 23);
separator_1.add(button_bodyColor);
button_bodyColor.setFont(new Font("宋体", Font.PLAIN, 12));
button_bodyColor.setFocusable(false);
button_bodyColor.setText("设置蛇身颜色");
final JSeparator separator_2 = new JSeparator();
separator_2.setOrientation(SwingConstants.VERTICAL);
separator_2.setBounds(302, 10, 140, 165);
add(separator_2);
final JSeparator separator_5 = new JSeparator();
separator_5.setBounds(10, 70, 125, 95);
separator_2.add(separator_5);
stopGameButton.setText("停止游戏");
stopGameButton.setBounds(11, 10, 101, 23);
separator_5.add(stopGameButton);
stopGameButton.setFont(new Font("宋体", Font.PLAIN, 12));
stopGameButton.setFocusable(false);
pauseButton.setBounds(10, 40, 101, 23);
separator_5.add(pauseButton);
pauseButton.setText("暂停/继续");
pauseButton.setFont(new Font("宋体", Font.PLAIN, 12));
pauseButton.setFocusable(false);
newGameButton.setFont(new Font("宋体", Font.PLAIN, 12));
newGameButton.setBounds(11, 70, 101, 23);
separator_5.add(newGameButton);
newGameButton.setFocusable(false);
newGameButton.setText("开始新游戏");
final JLabel label_logo = new JLabel(czbkIcon);
label_logo.setBounds(10, 10, 125, 50);
separator_2.add(label_logo);
final JSeparator separator_3 = new JSeparator();
separator_3.setBounds(10, 20, 286, 34);
add(separator_3);
final JLabel label_2 = new JLabel();
label_2.setFont(new Font("宋体", Font.PLAIN, 12));
label_2.setText("选项:");
label_2.setBounds(10, 10, 60, 15);
separator_3.add(label_2);
button_default = new JButton();
button_default.setText("恢复默认设置");
button_default.setFont(new Font("宋体", Font.PLAIN, 12));
button_default.setBounds(139, 6, 137, 23);
button_default.setFocusable(false);
separator_3.add(button_default);
}
public JFrame getFrame() {
return frame;
}
public void setFrame(JFrame frame) {
this.frame = frame;
}
public JCheckBox getCheckBox_drawGridding() {
return checkBox_drawGridding;
}
public ImageIcon getCzbkIcon() {
return czbkIcon;
}
public void setCzbkIcon(ImageIcon czbkIcon) {
this.czbkIcon = czbkIcon;
}
public JButton getNewGameButton() {
return newGameButton;
}
public JButton getPauseButton() {
return pauseButton;
}
public JButton getStopGameButton() {
return stopGameButton;
}
public JButton getButton_griddingColor() {
return button_griddingColor;
}
public JButton getButton_backgroundColor() {
return button_backgroundColor;
}
public JButton getButton_foodColor() {
return button_foodColor;
}
public JButton getButton_headColor() {
return button_headColor;
}
public JButton getButton_bodyColor() {
return button_bodyColor;
}
public JRadioButton getRadioButton_map2() {
return radioButton_map2;
}
public JRadioButton getRadioButton_map1() {
return radioButton_map1;
}
public JButton getButton_default() {
return button_default;
}
public ButtonGroup getButtonGroup() {
return buttonGroup;
}
public void setButtonGroup(ButtonGroup buttonGroup) {
this.buttonGroup = buttonGroup;
}
}
游戏的显示界面:
package cn.itcast.snake.view;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Image;
import javax.swing.JPanel;
import javax.swing.border.EtchedBorder;
import cn.itcast.snake.entities.Food;
import cn.itcast.snake.entities.Ground;
import cn.itcast.snake.entities.Snake;
import cn.itcast.snake.util.Global;
/**
* 游戏的显示界面<BR>
* 可以用 setBackgroundColor() 设置游戏的背景色
*
* @version 1.0, 01/01/08
*
* @author 汤阳光
*
*/
public class GamePanel extends JPanel {
/**
*
*/
private static final long serialVersionUID = 1L;
private Image oimg;
private Graphics og;
public static final Color DEFAULT_BACKGROUND_COLOR = new Color(0xcfcfcf);
/**
* 背景颜色
*/
private Color backgroundColor = DEFAULT_BACKGROUND_COLOR;
public GamePanel() {
/* 设置大小和布局 */
this.setSize(Global.WIDTH * Global.CELL_WIDTH, Global.HEIGHT
* Global.CELL_HEIGHT);
this.setBorder(new EtchedBorder(EtchedBorder.LOWERED));
this.setFocusable(true);
}
/**
* 重新显示 Ground, Shape
*
* @param ground
* @param snake
*/
public synchronized void redisplay(Ground ground, Snake snake, Food food) {
/* 重新显示 */
if (og == null) {
oimg = createImage(getSize().width, getSize().height);
if (oimg != null)
og = oimg.getGraphics();
}
if (og != null) {
og.setColor(backgroundColor);
og.fillRect(0, 0, Global.WIDTH * Global.CELL_WIDTH, Global.HEIGHT
* Global.CELL_HEIGHT);
if (ground != null)
ground.drawMe(og);
snake.drawMe(og);
if (food != null)
food.drawMe(og);
this.paint(this.getGraphics());
}
}
@Override
public void paint(Graphics g) {
g.drawImage(oimg, 0, 0, this);
}
/**
* 得到当前的背景颜色
*
* @return
*/
public Color getBackgroundColor() {
return backgroundColor;
}
/**
* 设置当前的背景颜色
*
* @param backgroundColor
*/
public void setBackgroundColor(Color backgroundColor) {
this.backgroundColor = backgroundColor;
}
}
工具类:
package cn.itcast.snake.util;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;
/**
*
* 工具类<BR>
* <BR>
* 此类中存放了其他类中用到的一些常量<BR>
* 并且支持配置文件<BR>
* 配置文件的路径为游戏运行的目录, 文件名为 snake.ini<BR>
* <BR>
* 配置文件的写法请参见字段的注释<BR>
* <BR>
* 配置文件中设置项可以只写需要配置的, 没有写的设置项默认为缺省值<BR>
* 各配置项的缺省值请参见字段的注释<BR>
* <BR>
* 每个配置项都有设置值范围, 超出范围(无效)的设置值将不起作用<BR>
* 设置值的范围请参见字段的注释<BR>
*
* @version 1.0, 01/01/08
*
* @author 汤阳光
*
*/
public class Global {
private static Properties properties = new Properties();
/**
* 配置文件的路径(默认为当前目录下的 snake.ini文件)
*/
private static String CONFIG_FILE = "snake.ini";
/**
* 一个格子的宽度, 单位:像素 <BR>
* 对应的配置文件中的关键字为: cell_width, 或用 cell_size指定<BR>
* 范围1 至 100<BR>
* 缺省值为 23
*/
public static final int CELL_WIDTH;
/**
* 一个格子的高度, 单位:像素 <BR>
* 对应的配置文件中的关键字为: cell_width, 或用 cell_size指定<BR>
* 范围1 至 100<BR>
* 缺省值为 23
*/
public static final int CELL_HEIGHT;
/**
* 用格子表示的宽度, (宽度为多少个格子) 单位:格 <BR>
* 对应的配置文件中的关键字为: width<BR>
* 范围10 至 80<BR>
* 缺省值为 30
*/
public static final int WIDTH;
/**
* 用格子表示的高度, (高度为多少个格子), 单位:格)<BR>
* 对应的配置文件中的关键字为: height<BR>
* 范围10 至 60<BR>
* 缺省值为20
*/
public static final int HEIGHT;
/**
* 显示的像素宽度 (格子宽度度 * 每一格的宽度)
*/
public static final int CANVAS_WIDTH;
/**
* 显示的像素高度 (格子高度 * 每一格的高度)
*/
public static final int CANVAS_HEIGHT;
/**
* 蛇的初始长度, 对应的配置文件中的关键字为: init_length<BR>
* 范围2 至 最大宽度<BR>
* (单位:格) 缺省值为 2
*/
public static final int INIT_LENGTH;
/**
* 蛇的初始速度 (单位: 毫秒/格)<BR>
* 对应的配置文件中的关键字为: speed<BR>
* 范围10 至 无限大<BR>
* 缺省值为 200
*/
public static final int SPEED;
/**
* 蛇每次加速或减速的幅度 (单位: 毫秒/格)<BR>
* 对应的配置文件的关键字为: speed_step<BR>
* 范围1 至 无限大<BR>
* 缺省值为 25
*/
public static final int SPEED_STEP;
public static final String TITLE_LABEL_TEXT;
public static final String INFO_LABEL_TEXT;
/**
* 默认的构造器, 私有的, 不能生成这个类的实例
*/
private Global() {
}
/**
* 初始化常量
*/
static {
InputStream inputStream = null;
try {
inputStream = new FileInputStream(CONFIG_FILE);
properties.load(inputStream);
} catch (Exception e) {
System.out.println("没有配置文件");
} finally {
try {
if (inputStream != null)
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
Integer temp = null;
/* 没有设置或设置的无效时要有一个默认值 */
WIDTH = (temp = getIntValue("width")) != null && temp <= 80
&& temp >= 10 ? temp : 35;
HEIGHT = (temp = getIntValue("height")) != null && temp <= 60
&& temp >= 10 ? temp : 20;
INIT_LENGTH = (temp = getIntValue("init_length")) != null && temp > 1
&& temp < WIDTH ? temp : 2;
SPEED = (temp = getIntValue("speed")) != null && temp >= 10 ? temp
: 200;
SPEED_STEP = (temp = getIntValue("speed_step")) != null && temp >= 1 ? temp
: 25;
int defaultCellSize = (temp = getIntValue("cell_size")) != null
&& temp > 0 && temp <= 100 ? temp : 20;
CELL_WIDTH = (temp = getIntValue("cell_width")) != null && temp > 0
&& temp <= 100 ? temp : defaultCellSize;
CELL_HEIGHT = (temp = getIntValue("cell_height")) != null && temp > 0
&& temp <= 100 ? temp : defaultCellSize;
CANVAS_WIDTH = WIDTH * CELL_WIDTH;
CANVAS_HEIGHT = HEIGHT * CELL_HEIGHT;
String tempStr = null;
TITLE_LABEL_TEXT = (tempStr = getValue("title")) == null ? "说明:"
: tempStr;
INFO_LABEL_TEXT = (tempStr = getValue("info")) == null ? "方向键控制方向, 回车键暂停/继续\nPAGE UP, PAGE DOWN 加速或减速\n\n更多请看 www.itcast.cn "
: tempStr;
}
/**
* 要考虑多种情况<BR>
* 1. 没有这个key和value<BR>
* 2 有key 没有 value
*/
private static Integer getIntValue(String key) {
if (key == null)
throw new RuntimeException("key 不能为空");
try {
return new Integer(properties.getProperty(key));
} catch (NumberFormatException e) {
return null;
}
}
private static String getValue(String key) {
try {
return new String(properties.getProperty(key).getBytes("iso8859-1"));
} catch (Exception e) {
return null;
}
}
}
实物类:
package cn.itcast.snake.entities;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Point;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.Set;
import cn.itcast.snake.listener.SnakeListener;
import cn.itcast.snake.util.Global;
/**
*
* 蛇<BR>
* move()方法默认支持走到边以后从另一边出现<BR>
* <BR>
* 可以用setHeadColor(), 和 setBodyColor() 方法更改蛇头或蛇身体的颜色<BR>
* <BR>
* 也可以通过覆盖 drawHead(Graphics, int, int, int, int) 方法 改变蛇头的显示方式 和覆盖
* drawBody(Graphics, int, int, int, int) 方法 改变蛇身体的显示方式<BR>
* <BR>
* 用内部类MoveDriver 驱动蛇定时移动<BR>
* begin() 方法内部开启一个新的线程驱动蛇定时移动, 调用这个方法的时候要注意<BR>
*
* 蛇的身体的初始长度必须大于等于2
*
* @version 1.0, 01/01/08
*
* @author 汤阳光
*
*/
public class Snake {
/**
* 方向上
*/
public static final int UP = 1;
/**
* 方向下
*/
public static final int DOWN = -1;
/**
* 方向左
*/
public static final int LEFT = 2;
/**
* 方向右
*/
public static final int RIGHT = -2;
/* 蛇(多个节点) */
private LinkedList<Point> body = new LinkedList<Point>();
/* 上一次的移动方向 */
private int oldDirection;
/* 下一步的方向(有效方向) */
private int newDirection;
/* 临时存放蛇头的坐标 */
private Point head;
/* 临时存放蛇尾巴的坐标 */
private Point tail;
/* 移动速度 */
private int speed;
/* 生命, 是否活着 */
private boolean live;
/* 是否暂停 */
private boolean pause;
private Set<SnakeListener> listeners = new HashSet<SnakeListener>();
public static final Color DEFAULT_HEAD_COLOR = new Color(0xcc0033);
/* 蛇头的颜色 */
private Color headColor = DEFAULT_HEAD_COLOR;
public static final Color DEFAULT_BODY_COLOR = new Color(0xcc0033);
/* 蛇身体的颜色 */
private Color bodyColor = DEFAULT_BODY_COLOR;
/**
* 移动一步, 会忽略相反方向
*/
public void move() {
/* 忽略相反方向 */
if (oldDirection + newDirection != 0)
oldDirection = newDirection;
/* 把蛇尾巴拿出来重新设置坐标作为新蛇头 */
/* getLocation 将返回一个新的Point */
/* tail把尾巴坐标保存下来, 吃到食物时再加上 */
tail = (head = takeTail()).getLocation();
/* 根据蛇头的坐标再 上下左右 */
head.setLocation(getHead());
/* 根据方向让蛇移动 */
switch (oldDirection) {
case UP:
head.y--;
/* 到边上了可以从另一边出现 */
if (head.y < 0)
head.y = Global.HEIGHT - 1;
break;
case DOWN:
head.y++;
/* 到边上了可以从另一边出现 */
if (head.y == Global.HEIGHT)
head.y = 0;
break;
case LEFT:
head.x--;
/* 到边上了可以从另一边出现 */
if (head.x < 0)
head.x = Global.WIDTH - 1;
break;
case RIGHT:
head.x++;
/* 到边上了可以从另一边出现 */
if (head.x == Global.WIDTH)
head.x = 0;
break;
}
/* 添加到头上去 */
body.addFirst(head);
}
/**
* 一个内部类, 驱动蛇定时移动
*
* @author 汤阳光
*
*/
private class SnakeDriver implements Runnable {
public void run() {
// TODO Auto-generated method stub
while (live) {
if (!pause) {
move();
/* 触发 ControllerListener 的状态改变事件 */
for (SnakeListener l : listeners)
l.snakeMoved();
}
try {
Thread.sleep(speed);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
/**
* 在尾巴上增加一个节点
*/
public void eatFood() {
/* 把上一次移动拿掉的节点再加上 */
body.addLast(tail.getLocation());
/* 触发SnakeListener 的 snakeEatFood 事件 */
for (SnakeListener l : listeners)
l.snakeEatFood();
}
/**
* 改变方向
*
* @param direction
*/
public void changeDirection(int direction) {
this.newDirection = direction;
}
/**
* 得到蛇头节点
*
* @return
*/
public Point getHead() {
/* 自己约定哪个是蛇头 */
return body.getFirst();
}
/**
* 拿掉蛇尾巴节点
*
* @return
*/
public Point takeTail() {
/* 去掉蛇尾巴 */
return body.removeLast();
}
/**
* 得到蛇的长度
*
* @return
*/
public int getLength() {
return body.size();
}
/**
* 让蛇开始运动<BR>
* 开启一个新的线程
*/
public void begin() {
new Thread(new SnakeDriver()).start();
}
/**
* 让蛇复活, 并开始运动<BR>
* 将调用 begin() 方法
*/
public void reNew() {
init();
begin();
}
/**
* 初始化蛇的信息<BR>
* 长度, 位置, 方向, 速度, 生命和暂停状态
*/
public void init() {
body.clear();
/* 初始化位置在中间 */
int x = Global.WIDTH / 2 - Global.INIT_LENGTH / 2;
int y = Global.HEIGHT / 2;
for (int i = 0; i < Global.INIT_LENGTH; i++)
this.body.addFirst(new Point(x++, y));
/* 设置默认方向为向右 */
oldDirection = newDirection = RIGHT;
/* 初始化速度 */
speed = Global.SPEED;
/* 初始化生命和暂停状态 */
live = true;
pause = false;
}
/**
* 是否吃到自己的身体<BR>
*
* @return 蛇头的坐标是否和自己的身体的某一个坐标重合
*/
public boolean isEatBody() {
/* 要把蛇头排除, body.get(0) 是蛇头 */
for (int i = 1; i < body.size(); i++)
if (getHead().equals(body.get(i)))
return true;
return false;
}
/**
* 画自己<BR>
* 将调用 drawHead(Graphics, int, int, int, int) 方法 和 drawBody(Graphics, int,
* int, int, int) 方法
*
* @param g
*/
public void drawMe(Graphics g) {
for (Point p : body) {
/* 画蛇身体 */
g.setColor(bodyColor);
drawBody(g, p.x * Global.CELL_WIDTH, p.y * Global.CELL_HEIGHT,
Global.CELL_WIDTH, Global.CELL_HEIGHT);
}
/* 画蛇头 */
g.setColor(headColor);
drawHead(g, getHead().x * Global.CELL_WIDTH, getHead().y
* Global.CELL_HEIGHT, Global.CELL_WIDTH, Global.CELL_HEIGHT);
}
/**
* 画蛇头, 可以覆盖这个方法改变蛇头的显示
*
* @param g
* @param x
* 像素坐标 x
* @param y
* 像素坐标 y
* @param width
* 宽度(单位:像素)
* @param height
* 高度(单位:像素)
*/
public void drawHead(Graphics g, int x, int y, int width, int height) {
g.fill3DRect(x, y, width, height, true);
}
/**
* 画蛇的一节身体, 可以覆盖这个方法改变蛇的身体节点的显示
*
* @param g
* @param x
* 像素坐标 x
* @param y
* 像素坐标 y
* @param width
* 宽度(单位:像素)
* @param height
* 高度(单位:像素)
*/
public void drawBody(Graphics g, int x, int y, int width, int height) {
g.fill3DRect(x, y, width, height, true);
}
/**
* 得到蛇头的颜色
*
* @return
*/
public Color getHeadColor() {
return headColor;
}
/**
* 设置蛇头的颜色
*
* @param headColor
*/
public void setHeadColor(Color headColor) {
this.headColor = headColor;
}
/**
* 得到蛇身体的颜色
*
* @return
*/
public Color getBodyColor() {
return bodyColor;
}
/**
* 设置蛇身体的颜色
*
* @param bodyColor
*/
public void setBodyColor(Color bodyColor) {
this.bodyColor = bodyColor;
}
/**
* 添加监听器
*
* @param l
*/
public synchronized void addSnakeListener(SnakeListener l) {
if (l == null)
return;
this.listeners.add(l);
}
/**
* 移除监听器
*
* @param l
*/
public synchronized void removeSnakeListener(SnakeListener l) {
if (l == null)
return;
this.listeners.remove(l);
}
/**
* 加速, 幅度为 Global 中设置的 SPEED_STEP <BR>
* 在有效的速度范围之内(增加后速度大于 0毫秒/格)
*/
public void speedUp() {
if (speed > Global.SPEED_STEP)
speed -= Global.SPEED_STEP;
}
/**
* 减速, 幅度为 Global 中设置的 SPEED_STEP
*/
public void speedDown() {
speed += Global.SPEED_STEP;
}
/**
* 得到蛇的移动速度
*
* @return
*/
public int getSpeed() {
return speed;
}
/**
* 设置蛇的移动速度
*
* @param speed
*/
public void setSpeed(int speed) {
this.speed = speed;
}
/**
* 蛇是否死掉了
*
* @return
*/
public boolean isLive() {
return live;
}
/**
* 设置蛇是否死掉
*
* @param live
*/
public void setLive(boolean live) {
this.live = live;
}
/**
* 设置蛇死掉
*/
public void dead() {
this.live = false;
}
/**
* 是否是暂停状态
*
* @return
*/
public boolean isPause() {
return pause;
}
/**
* 设置暂停状态
*
* @param pause
*/
public void setPause(boolean pause) {
this.pause = pause;
}
/**
* 更改暂停状态<BR>
* 若是暂停状态, 则继续移动<BR>
* 若正在移动, 则暂停
*/
public void changePause() {
pause = !pause;
}
}
package cn.itcast.snake.entities;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Point;
import java.util.Random;
import cn.itcast.snake.util.Global;
/**
*
* 食物, 有x , y 坐标 和 颜色等属性<BR>
* 可以用setColor() 改变食物的颜色<BR>
* 也可以通过覆盖 drawFood(Graphics, int, int, int, int) 方法 改变食物的显示方式<BR>
*
* @version 1.0, 01/01/08
*
* @author 汤阳光
*
*/
public class Food extends Point {
/**
*
*/
private static final long serialVersionUID = 1L;
/* 食物的颜色 */
private Color color = new Color(0xcc0033);
private Random random = new Random();
/**
* 默认的构造器, 产生(0,0)的坐标
*/
public Food() {
super();
}
public Point getNew() {
Point p = new Point();
p.x = random.nextInt(Global.WIDTH);
p.y = random.nextInt(Global.HEIGHT);
return p;
}
/**
* 初始化坐标和指定坐标相同的构造器
*
* @param x
* @param y
*/
public Food(Point p) {
super(p);
}
/**
* 蛇是否吃到了食物
*
* @param p
* @return
*/
public boolean isSnakeEatFood(Snake snake) {
return this.equals(snake.getHead());
}
/**
* 画自己, 将调用 drawFood(Graphics, int, int, int, int) 方法
*
* @param g
*/
public void drawMe(Graphics g) {
g.setColor(color);
drawFood(g, x * Global.CELL_WIDTH, y * Global.CELL_HEIGHT,
Global.CELL_WIDTH, Global.CELL_HEIGHT);
}
/**
* 画食物, 可以覆盖这个方法改变食物的显示
*
* @param g
* @param x
* 像素坐标 x
* @param y
* 像素坐标 y
* @param width
* 宽度(单位:像素)
* @param height
* 高度(单位:像素)
*/
public void drawFood(Graphics g, int x, int y, int width, int height) {
g.fill3DRect(x, y, width, height, true);
}
/**
* 得到食物的颜色
*
* @return
*/
public Color getColor() {
return color;
}
/**
* 设置食物的颜色
*
* @param color
*/
public void setColor(Color color) {
this.color = color;
}
}
package cn.itcast.snake.entities;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Point;
import java.util.Random;
import cn.itcast.snake.util.Global;
/**
* 可以叫做地形, 或地面<BR>
* <BR>
* 可以通过setRockColor(), setGriddingColor()方法更改石头或网格的颜色<BR>
* 通过setDrawGridding() 方法设置是否画网格<BR>
* <BR>
* 可以覆盖 drawRock(Graphics, int, int, int, int) 方法以改变石头的显示方式<BR>
* <BR>
* 可以通过覆盖genernateRocks() 方法改变石头产生的布局<BR>
* 覆盖此方法时, 请使用addRock(int,int) 方法在指定的坐标添加石头<BR>
* <BR>
* 这个类提供了两种石头的布局<BR>
* 默认是使用第一种, 即一圈石头<BR>
* 如果想使用第二种, 则应该使用如下代码:<BR>
* <code><BR>
Ground ground = new Ground();<BR>
// 清空石头<BR>
ground.init();<BR>
// 使用提供的第二种石头的布局 <BR>
ground.generateRocks2();<BR>
</code> <BR>
*
* @version 1.0, 01/01/08
*
* @author 汤阳光
*
*/
public class Ground {
/* 存放石头的二维数组 */
private boolean rocks[][] = new boolean[Global.WIDTH][Global.HEIGHT];
/* 存放getFreePoint()方法生成的不是石头的随机的坐标 */
private Point freePoint = new Point();
public static final Color DEFAULT_ROCK_COLOR = new Color(0x666666);
/* 石头的颜色 */
private Color rockColor = DEFAULT_ROCK_COLOR;
public static final Color DEFAULT_GRIDDING_COLOR = Color.LIGHT_GRAY;
/* 网格的颜色 */
private Color griddingColor = DEFAULT_GRIDDING_COLOR;
private Random random = new Random();
/* 是否画网格的开关 */
private boolean drawGridding = false;
/**
* 默认的构造器, 将调用 init()方法和 generateRocks() 方法
*/
public Ground() {
init();
}
/**
* 初始化地面(清空石头)
*/
public void clear() {
for (int x = 0; x < Global.WIDTH; x++)
for (int y = 0; y < Global.HEIGHT; y++)
rocks[x][y] = false;
}
public void init() {
clear();
generateRocks();
}
/**
* 产生石头, 可以覆盖这个方法改变石头在地面上的布局
*/
public void generateRocks() {
for (int x = 0; x < Global.WIDTH; x++)
rocks[x][0] = rocks[x][Global.HEIGHT - 1] = true;
for (int y = 0; y < Global.HEIGHT; y++)
rocks[0][y] = rocks[Global.WIDTH - 1][y] = true;
}
/**
* 提供的第二种默认的石头布局<BR>
* 用这个可以测试蛇从另一边出现<BR>
* 使用时先调用init()方法清空石头, 再调用这个方法产生石头布局<BR>
* 详细使用请参见类介绍
*/
public void generateRocks2() {
for (int y = 0; y < 6; y++) {
rocks[0][y] = true;
rocks[Global.WIDTH - 1][y] = true;
rocks[0][Global.HEIGHT - 1 - y] = true;
rocks[Global.WIDTH - 1][Global.HEIGHT - 1 - y] = true;
}
for (int y = 6; y < Global.HEIGHT - 6; y++) {
rocks[6][y] = true;
rocks[Global.WIDTH - 7][y] = true;
}
}
/**
* 添加一块石头到指定格子坐标
*
* @param x
* 格子坐标 x
* @param y
* 格子坐标 y
*/
public void addRock(int x, int y) {
rocks[x][y] = true;
}
/**
* 蛇是否吃到了石头
*
* @param p
* @return
*/
public boolean isSnakeEatRock(Snake snake) {
return rocks[snake.getHead().x][snake.getHead().y];
}
/**
* 随机生成一个不是石头的坐标, 用于丢食物
*
* @return
*/
public Point getFreePoint() {
do {
freePoint.x = random.nextInt(Global.WIDTH);
freePoint.y = random.nextInt(Global.HEIGHT);
} while (rocks[freePoint.x][freePoint.y]);
return freePoint;
}
/**
* 得到石头的颜色
*
* @return
*/
public Color getRockColor() {
return rockColor;
}
/**
* 设置石头的颜色
*
* @param rockColor
*/
public void setRockColor(Color rockColor) {
this.rockColor = rockColor;
}
/**
* 画自己, 将调用drawRock(Graphics, int, int, int, int) 方法 和
* drawGridding(Graphics, int, int, int, int) 方法
*
* @param g
*/
public void drawMe(Graphics g) {
for (int x = 0; x < Global.WIDTH; x++)
for (int y = 0; y < Global.HEIGHT; y++) {
/* 画石头 */
if (rocks[x][y]) {
g.setColor(rockColor);
drawRock(g, x * Global.CELL_WIDTH, y * Global.CELL_HEIGHT,
Global.CELL_WIDTH, Global.CELL_HEIGHT);
} else if (drawGridding) {
/* 画网格(如果允许) */
g.setColor(griddingColor);
drawGridding(g, x * Global.CELL_WIDTH, y
* Global.CELL_HEIGHT, Global.CELL_WIDTH,
Global.CELL_HEIGHT);
}
}
}
/**
* 画一块石头, 可以覆盖这个方法改变石头的显示
*
* @param g
* @param x
* 像素坐标 x
* @param y
* 像素坐标 y
* @param width
* 宽度(单位:像素)
* @param height
* 高度(单位:像素)
*/
public void drawRock(Graphics g, int x, int y, int width, int height) {
g.fill3DRect(x, y, width, height, true);
}
/**
* 画网格, 可以覆盖这个方法改变网格的显示
*
* @param g
* @param x
* 像素坐标 x
* @param y
* 像素坐标 y
* @param width
* 宽度(单位:像素)
* @param height
* 高度(单位:像素)
*/
public void drawGridding(Graphics g, int x, int y, int width, int height) {
g.drawRect(x, y, width, height);
}
/**
* 得到网格的颜色
*
* @return
*/
public Color getGriddingColor() {
return griddingColor;
}
/**
* 设置网格的颜色
*
* @param griddingColor
*/
public void setGriddingColor(Color griddingColor) {
this.griddingColor = griddingColor;
}
/**
* 是否画网格
*
* @return
*/
public boolean isDrawGridding() {
return drawGridding;
}
/**
* 设置是否画网格
*
* @param drawGridding
*/
public void setDrawGridding(boolean drawGridding) {
this.drawGridding = drawGridding;
}
}
游戏监听器:
package cn.itcast.snake.listener;
/**
*
* 游戏监听器
*
* @version 1.0, 01/01/08
*
* @author 汤阳光
*
*/
public interface GameListener {
/**
* 游戏开始了
*/
void gameStart();
/**
* 游戏结束了
*/
void gameOver();
/**
* 游戏暂停了
*/
void gamePause();
/**
* 游戏继续
*/
void gameContinue();
}
package cn.itcast.snake.listener;
/**
*
* 蛇的监听器
*
* @version 1.0, 01/01/08
*
* @author 汤阳光
*
*/
public interface SnakeListener {
/**
* 蛇移动事件
*/
void snakeMoved();
/**
* 蛇吃到食物事件
*/
void snakeEatFood();
}
控制器:
package cn.itcast.snake.controller;
import java.awt.Font;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.util.HashSet;
import java.util.Set;
import javax.swing.JLabel;
import cn.itcast.snake.entities.Food;
import cn.itcast.snake.entities.Ground;
import cn.itcast.snake.entities.Snake;
import cn.itcast.snake.listener.GameListener;
import cn.itcast.snake.listener.SnakeListener;
import cn.itcast.snake.util.Global;
import cn.itcast.snake.view.GamePanel;
/**
* 控制器<BR>
* 控制Ground, Snake, Food<BR>
* 负责游戏的逻辑<BR>
* 处理按键事件<BR>
* <BR>
* 实现了SnakeListener接口, 可以处理Snake 触发的事件<BR>
* 方法 snakeEatFood() 处理蛇吃到食物后触发的 snakeEatFood事件 但什么也没做<BR>
* <BR>
*
* @version 1.0, 01/01/08
*
* @author 汤阳光
*
*/
public class Controller extends KeyAdapter implements SnakeListener {
/* 地形 */
private Ground ground;
/* 蛇 */
private Snake snake;
/* 食物 */
private Food food;
/* 显示 */
private GamePanel gamePanel;
/* 提示信息 */
private JLabel gameInfoLabel;
private boolean playing;
private int map;
/* 控制器监听器 */
private Set<GameListener> listeners = new HashSet<GameListener>();
/**
* 处理按键事件<BR>
* 接受按键, 根据按键不同, 发出不同的指令<BR>
* UP: 改变蛇的移动方向为向上<BR>
* DOWN: 改变蛇的移动方向为向下<BR>
* LEFT: 改变蛇的移动方向为向左 <BR>
* RIGHT: 改变蛇的移动方向为向右<BR>
* SPACE: 暂停/继续<BR>
* PAGE UP: 加快蛇的移动速度<BR>
* PAGE DOWN: 减慢蛇的移动速度<BR>
* Y: 重新开始游戏
*/
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() != KeyEvent.VK_Y && !playing)
return;
// TODO Auto-generated method stub
/* 根据按键不同, 让蛇改变不同的方向 */
switch (e.getKeyCode()) {
/* 方向键 上 */
case KeyEvent.VK_UP:
if (snake.isPause()) {
snake.changePause();
for (GameListener l : listeners)
l.gameContinue();
}
snake.changeDirection(Snake.UP);
break;
/* 方向键 下 */
case KeyEvent.VK_DOWN:
if (snake.isPause()) {
snake.changePause();
for (GameListener l : listeners)
l.gameContinue();
}
snake.changeDirection(Snake.DOWN);
break;
/* 方向键 左 */
case KeyEvent.VK_LEFT:
if (snake.isPause()) {
snake.changePause();
for (GameListener l : listeners)
l.gameContinue();
}
snake.changeDirection(Snake.LEFT);
break;
/* 方向键 右 */
case KeyEvent.VK_RIGHT:
if (snake.isPause()) {
snake.changePause();
for (GameListener l : listeners)
l.gameContinue();
}
snake.changeDirection(Snake.RIGHT);
break;
/* 回车或空格 (暂停) */
case KeyEvent.VK_ENTER:
case KeyEvent.VK_SPACE:
snake.changePause();
/* === */
for (GameListener l : listeners)
if (snake.isPause())
l.gamePause();
else
l.gameContinue();
break;
/* PAGE_UP 加速 */
case KeyEvent.VK_PAGE_UP:
snake.speedUp();
break;
/* PAGE_DOWN 减速 */
case KeyEvent.VK_PAGE_DOWN:
snake.speedDown();
break;
/* 字母键 Y (重新开始游戏) */
case KeyEvent.VK_Y:
if (!isPlaying())
newGame();
break;
}
/* 重新显示 */
if (gamePanel != null)
gamePanel.redisplay(ground, snake, food);
/* 更新提示 */
if (gameInfoLabel != null)
gameInfoLabel.setText(getNewInfo());
}
/**
* 处理Snake 触发的 snakeMoved 事件<BR>
*/
public void snakeMoved() {
/* 判断是否吃到食物 */
if (food != null && food.isSnakeEatFood(snake)) {
/* 吃到食物后, 蛇增加身体, 再重新丢一个食物 */
snake.eatFood();
food.setLocation(ground == null ? food.getNew() : ground
.getFreePoint());
}/* 如果吃到食物, 就肯定不会吃到石头 */
else if (ground != null && ground.isSnakeEatRock(snake)) {
/* 如果吃到的是石头, 或吃到自己的身体, 就让蛇死掉 */
stopGame();
}
if (snake.isEatBody())
stopGame();
if (gamePanel != null)
gamePanel.redisplay(ground, snake, food);
/* 更新提示 */
if (gameInfoLabel != null)
gameInfoLabel.setText(getNewInfo());
}
/**
* 开始一个新游戏
*/
public void newGame() {
if (ground != null) {
switch (map) {
case 2:
ground.clear();
ground.generateRocks2();
break;
default:
ground.init();
break;
}
}
playing = true;
snake.reNew();
for (GameListener l : listeners)
l.gameStart();
}
/**
* 结束游戏
*/
public void stopGame() {
if (playing) {
playing = false;
snake.dead();
for (GameListener l : listeners)
l.gameOver();
}
}
/**
* 暂停游戏
*/
public void pauseGame() {
snake.setPause(true);
for (GameListener l : listeners)
l.gamePause();
}
/**
* 继续游戏
*/
public void continueGame() {
snake.setPause(false);
for (GameListener l : listeners)
l.gameContinue();
}
/**
* 接受Snake,Food,Ground 的构造器<BR>
*
* @param snake
* @param food
* @param ground
*/
public Controller(Snake snake, Food food, Ground ground, GamePanel gamePanel) {
this.snake = snake;
this.food = food;
this.ground = ground;
this.gamePanel = gamePanel;
/* 先丢一个食物 */
if (ground != null && food != null)
food.setLocation(ground.getFreePoint());
/* 注册监听器 */
this.snake.addSnakeListener(this);
}
/**
* 多接受一个显示提示信息的JLabel
*
* @param snake
* @param food
* @param ground
* @param gameInfoLabel
*/
public Controller(Snake snake, Food food, Ground ground,
GamePanel gamePanel, JLabel gameInfoLabel) {
this(snake, food, ground, gamePanel);
this.setGameInfoLabel(gameInfoLabel);
if (gameInfoLabel != null)
gameInfoLabel.setText(getNewInfo());
}
/**
* 得到最新的提示信息
*
* @return 蛇的最新信息
*/
public String getNewInfo() {
if (!snake.isLive())
return " ";// " 提示: 按 Y 开始新游戏";
else
return new StringBuffer().append("提示: ").append("速度 ").append(
snake.getSpeed()).toString()
+ " 毫秒/格";
}
/**
* 添加监听器
*
* @param l
*/
public synchronized void addGameListener(GameListener l) {
if (l != null)
this.listeners.add(l);
}
/**
* 移除监听器
*
* @param l
*/
public synchronized void removeGameListener(GameListener l) {
if (l != null)
this.listeners.remove(l);
}
/**
* 得到蛇的引用
*
* @return
*/
public Snake getSnake() {
return this.snake;
}
/**
* 得到食物的引用
*
* @return
*/
public Food getFood() {
return this.food;
}
/**
* 得到地形的引用
*
* @return
*/
public Ground getGround() {
return this.ground;
}
/**
* 处理蛇吃到食物后触发的 snakeEatFood事件, 但什么也没做<BR>
* 可以覆盖这个方法增加功能, 例如, 增加记分功能
*/
public void snakeEatFood() {
// TODO Auto-generated method stub
System.out.println("吃到食物!");
}
public GamePanel getGamePanel() {
return gamePanel;
}
/**
* 设置GamePanel
*
* @param gamePanel
*/
public void setGamePanel(GamePanel gamePanel) {
this.gamePanel = gamePanel;
}
public JLabel getGameInfoLabel() {
return gameInfoLabel;
}
public void setGameInfoLabel(JLabel gameInfoLabel) {
this.gameInfoLabel = gameInfoLabel;
this.gameInfoLabel.setSize(Global.WIDTH * Global.CELL_WIDTH, 20);
this.gameInfoLabel.setFont(new Font("宋体", Font.PLAIN, 12));
gameInfoLabel.setText(this.getNewInfo());
}
public void setGround(Ground ground) {
this.ground = ground;
}
public void setSnake(Snake snake) {
this.snake = snake;
}
public void setFood(Food food) {
this.food = food;
}
public int getMap() {
return map;
}
public void setMap(int map) {
this.map = map;
}
public boolean isPlaying() {
return playing;
}
public void setPlaying(boolean playing) {
this.playing = playing;
}
public boolean isPausingGame() {
// TODO Auto-generated method stub
return snake.isPause();
}
}