`

贪吃蛇游戏

    博客分类:
  • J2SE
阅读更多
      对大多数人来说,要想自己编写出《贪吃蛇》游戏的程序代码,是有较大难度的。可是,很多初学者一直梦想和追求着自己亲手编写出一个《贪吃蛇》的游戏程序。为了帮助大家实现心愿,向大家讲解和展示了传智播客版《贪吃蛇游戏》的每一行代码的编写过程。
       
        贪吃蛇游戏开发学习包括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();
	}

}
  • 大小: 45 KB
分享到:
评论

相关推荐

    java 贪吃蛇游戏.zip

    java 贪吃蛇游戏.zipjava 贪吃蛇游戏.zipjava 贪吃蛇游戏.zipjava 贪吃蛇游戏.zip java 贪吃蛇游戏.zipjava 贪吃蛇游戏.zipjava 贪吃蛇游戏.zipjava 贪吃蛇游戏.zip java 贪吃蛇游戏.zipjava 贪吃蛇游戏.zipjava ...

    贪吃蛇游戏源代码

    【贪吃蛇游戏源代码】是一个适合初学者深入理解Java编程的项目,它结合了基本的编程概念和游戏逻辑,让学习者在实践中提升技能。贪吃蛇游戏是一款经典的计算机游戏,玩家通过控制蛇的移动,使其吃到食物,每吃一颗...

    最新单片机仿真 8x8点阵做的贪吃蛇游戏

    最新单片机仿真 8x8点阵做的贪吃蛇游戏最新单片机仿真 8x8点阵做的贪吃蛇游戏最新单片机仿真 8x8点阵做的贪吃蛇游戏最新单片机仿真 8x8点阵做的贪吃蛇游戏最新单片机仿真 8x8点阵做的贪吃蛇游戏最新单片机仿真 8x8...

    贪吃蛇游戏java版源码

    贪吃蛇游戏是一款经典的计算机游戏,它源自早期的诺基亚手机,并且在全球范围内广受欢迎。这个Java版本的贪吃蛇游戏源码是为那些希望深入理解游戏开发和Java GUI编程,特别是AWT(Abstract Window Toolkit)和Swing...

    嵌入式ARM的贪吃蛇游戏课程设计

    嵌入式ARM的贪吃蛇游戏课程设计 嵌入式ARM的贪吃蛇游戏课程设计是基于ARM架构的贪吃蛇游戏设计实践项目。该项目的目的是设计和实现一个基于ARM的贪吃蛇游戏机,使用飞利浦公司的LPC2124芯片板作为平台。 该项目的...

    基于Labview的贪吃蛇游戏设计报告.pdf

    根据提供的文件信息,内容涉及到LabVIEW这一图形化编程语言,以及利用该语言开发的贪吃蛇游戏。为了生成相关知识点,我们可以从LabVIEW的基本概念出发,讲述其在游戏开发中的应用,并尝试将文档中的片段整理为有意义...

    基于arm板的贪吃蛇游戏

    【基于ARM板的贪吃蛇游戏】是一种在嵌入式系统平台上实现的经典小游戏,它能够帮助开发者深入理解和掌握ARM架构的处理器以及相关的嵌入式系统开发技术。ARM(Advanced RISC Machines)处理器以其低功耗、高性能的...

    基于C++利用OpenGL设计贪吃蛇游戏【100011960】

    贪吃蛇游戏:玩家控制贪吃蛇在游戏区域里驰骋,避免碰到自己或障碍物,尽可能地吃更多的食物以生长! 游戏玩法: WASD控制蛇的移动 游戏开始,每隔一定时间会在地图空闲位置刷新一个食物,蛇触碰到食物后食物消失,...

    基于Arm-6818开发板的贪吃蛇游戏c++源码+项目说明.zip

    基于Arm-6818开发板的贪吃蛇游戏c++源码+项目说明.zip 基于Arm-6818开发板的贪吃蛇游戏c++源码+项目说明.zip 基于Arm-6818开发板的贪吃蛇游戏c++源码+项目说明.zip 基于Arm-6818开发板的贪吃蛇游戏c++源码+项目说明....

    简单贪吃蛇 游戏开发

    贪吃蛇游戏是一种经典的计算机程序,它源自早期的电子游戏,至今仍深受玩家喜爱。它的基本玩法是控制一条蛇在有限的空间内移动,通过吃食物来增长身体长度,同时避免碰撞到自己的身体或边界。这款“简单贪吃蛇”游戏...

    VB.NET贪吃蛇游戏

    【VB.NET贪吃蛇游戏】是一款使用微软的Visual Basic .NET编程语言开发的经典小游戏,它基于Windows Forms平台,利用GDI+图形库实现图形界面和游戏逻辑。在本项目中,我们将探讨VB.NET如何实现一个功能完备、交互性强...

    用Verilog实现的贪吃蛇游戏(基于FPGA开发板) fpga开发.pdf

    FPGA开发中的Verilog实现贪吃蛇游戏 FPGA(Field-Programmable Gate Array)是一种可编程逻辑器件,它可以根据需要进行配置和重配置。FPGA的开发需要使用Hardware Description Language(HDL),如Verilog或VHDL。...

    基于嵌入式开发的贪吃蛇游戏

    《基于嵌入式开发的贪吃蛇游戏》 在当今科技日新月异的时代,嵌入式系统在各个领域都发挥着重要的作用,从智能家居到工业自动化,无处不在。而作为学习和实践嵌入式系统的一个经典项目,贪吃蛇游戏的开发不仅能够...

    基于STM32的贪吃蛇游戏设计(程序、PCB)

    在本项目中,我们探讨的是一个基于STM32微控制器的贪吃蛇游戏设计。STM32是一款广泛应用的32位微处理器,以其高性能、低功耗和丰富的外设接口而受到开发者的青睐。贪吃蛇游戏是一款经典的电子游戏,玩家通过控制蛇的...

    简单的贪吃蛇游戏,VC++源代码

    【贪吃蛇游戏详解】 贪吃蛇是一款经典的电子游戏,起源于早期的计算机时代,深受玩家喜爱。在本文中,我们将深入探讨一个基于VC++编写的简单贪吃蛇游戏,特别是其不死版特性以及速度可调功能。 首先,我们要了解...

    贪吃蛇游戏(java实现)

    贪吃蛇游戏是一种经典的计算机游戏,它源自早期的电子游戏,并在各种平台上广泛流行。本项目采用Java编程语言实现,适合用作课程设计或毕业设计,以加深对Java编程和游戏开发的理解。以下是对这个Java实现贪吃蛇游戏...

    贪吃蛇游戏开题报告

    【开题报告】基于Android的贪吃蛇游戏 在当今科技快速发展的时代,移动设备的普及率不断提高,尤其是Android系统,已经成为全球范围内广泛使用的智能设备操作系统。鉴于Android平台的广泛影响力,开发基于Android的...

    JAVA贪吃蛇游戏毕业设计(源代码+论文)

    【标题】"JAVA贪吃蛇游戏毕业设计(源代码+论文)"揭示了这是一份基于Java编程语言实现的贪吃蛇游戏项目,适用于学生作为毕业设计。贪吃蛇游戏是一款经典的电子游戏,玩家通过控制蛇的移动来吃食物,每吃一颗食物,蛇...

    C++贪吃蛇游戏源码

    《C++实现贪吃蛇游戏详解》 贪吃蛇游戏,作为一款经典的休闲游戏,深受玩家喜爱。在本文中,我们将深入探讨如何使用C++编程语言来实现这一趣味盎然的游戏。C++以其强大的功能和灵活性,成为实现游戏程序的理想选择...

Global site tag (gtag.js) - Google Analytics