public class Move {//小蛇和食物的位置
int move_x;
int move_y;
public Move(int move_x,int move_y){
this.move_x=move_x;
this.move_y=move_y;
}
}
import java.util.*;import javax.swing.JOptionPane;public class MoveOperate extends Observable implements Runnable {//小蛇和食物移动操作的类
public static final int LEFT = 1; // 小蛇向左移动的标记
public static final int UP = 2; // 小蛇向上移动的标记
public static final int RIGHT = 3; // 小蛇向右移动的标记
public static final int DOWN = 4; // 小蛇向下移动的标记
private LinkedList snake = new LinkedList(); // 声明小蛇的双链表集合
private boolean[][] isHave; // 指示位置上是否有小蛇或食物
private Move aliment; // 食物
private int move_Direction = LEFT; // 小蛇的初始移动方向
private boolean running = false; // 运行状态
private int spaceTime = 300; // 时间间隔
private double speedChange = 0.75; // 每次速度的变化率
private boolean paused = false; // 暂停
private int score = 0; // 得分
private int moveCount = 0; // 吃到食物前的移动次数
private int X; // 横坐标
private int Y; // 纵坐标 public MoveOperate(int X, int Y) {
this.X = X;
this.Y = Y;
resetGame();
} public void resetGame() { // 重置游戏
move_Direction = MoveOperate.LEFT;
spaceTime = 300;
paused = false;
moveCount = 0;
score=0;
isHave = new boolean[X][Y];
for (int i = 0; i < X; i++) {
isHave[i] = new boolean[Y];
Arrays.fill(isHave[i], false);
}
// 初始化小蛇,如果横向位置超过20,这长度为10,否则为横向位置的一半
int initLength = X > 20 ? 10 : X / 2;
snake.clear();
int x = X / 2;
int y = Y / 2;
for (int i = 0; i < initLength; i++) {
snake.addLast(new Move(x, y));
isHave[x][y] = true;
x++;
}
aliment = createAliment();
isHave[aliment.move_x][aliment.move_y] = true;
} @Override
public void run() { // 实现Runnable接口
// TODO Auto-generated method stub
running = true;
while (running) {
try {
Thread.sleep(spaceTime); // 休眠:表示蛇的移动速度
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
break;
}
if (!paused) {
if (Move()) {
setChanged();//更新界面数据(小蛇和食物位置)
notifyObservers();
} else {//弹出对话框表示游戏结束
JOptionPane.showMessageDialog(null, "你失败了!", "Game Over",
JOptionPane.INFORMATION_MESSAGE);
}
}
}
if (!running) {//游戏暂停或者结束
JOptionPane.showMessageDialog(null, "你停止了游戏!", "Game Over",
JOptionPane.INFORMATION_MESSAGE);
}
} public boolean Move() { // 蛇每移动一步判断
Move snakeHead = (Move) snake.getFirst(); // 获得蛇头位置
int head_x = snakeHead.move_x; // 蛇头的横坐标
int head_y = snakeHead.move_y; // 蛇头的纵坐标
switch (move_Direction) { // 判断蛇的运动方向
case LEFT: // 向左运动
head_x--;
break;
case UP: // 向上运动
head_y--;
break;
case RIGHT: // 向右运动
head_x++;
break;
case DOWN: // 向下运动
head_y++;
break;
} if ((0 <= head_x && head_x < X) && (0 <= head_y && head_y < Y)) {
if (isHave[head_x][head_y]) { // 如果指定位置有蛇或有食物
if (head_x == aliment.move_x && head_y == aliment.move_y) { // 小蛇吃到食物
snake.addFirst(aliment);// 蛇头增长表示吃到食物
// 分数与改变方向的次数和速度有关
int scoreGet = (10000 - 200 * moveCount) / spaceTime;
score += scoreGet > 0 ? scoreGet : 10;
moveCount = 0;
aliment = createAliment(); // 创建新的食物
isHave[aliment.move_x][aliment.move_y] = true;
return true;
} else {
return false; // 小蛇吃到自己
}
} else { // 指定位置没有食物,蛇移动的指定位置
snake.addFirst(new Move(head_x, head_y));
isHave[head_x][head_y] = true;
snakeHead = (Move) snake.removeLast();
isHave[snakeHead.move_x][snakeHead.move_y] = false;
moveCount++;
return true;
}
} else {
return false; // 碰到外壁失败
}
} private Move createAliment() { // 创建食物
int x = 0;
int y = 0;
do {
Random r = new Random(); // 随即获取位置
x = r.nextInt(X);
y = r.nextInt(Y);
} while (isHave[x][y]); return new Move(x, y);
} public void changeDirection(int Dir){//改变蛇的运动方向
if(move_Direction%2!=Dir%2){//改变的方向不与原来的方向相同或相反
move_Direction=Dir;
}
}

public void speedUp() { // 加速运行
spaceTime *= speedChange;
} public void speedDown() { // 减速运行
spaceTime /= speedChange;
} public void changePauseState() { // 改变暂停状态
paused = !paused;
} public boolean isRunning() { // 判断是否运动
return running;
} public void setRunning(boolean running) { // 设置运动标识
this.running = running;
} public LinkedList getMoveList() { // 获得蛇
return snake;
} public Move getAliment() { // 获得食物
return aliment;
} public int getScore() { // 获得得分
return score;
}
}import java.awt.*;
import java.util.*;import javax.swing.*;public class SnakeFrame extends JFrame implements Observer {//贪食蛇的界面
private static final int gridWidth=10; //格子的宽度
private static final int gridHeight=10; //格子的高度
private int gameWidth; //画面的宽度
private int gameHeight; //画面的高度
private int startX=0; //画面左上角横坐标
private int startY=0; //画面左上角纵坐标
JLabel score; //声明分数标签
Canvas canvas; //声明画布

public SnakeFrame(){
this(30,40,0,0);
}
public SnakeFrame(int X,int Y){
this(X,Y,0,0);
}

public SnakeFrame(int X,int Y,int startX,int startY){
this.gameWidth=X*gridWidth;
this.gameHeight=Y*gridHeight;
this.startX=startX;
this.startY=startY;
init();
}
private void init(){ //初始化游戏界面
this.setTitle("贪食蛇");
this.setLocation(startX, startY);
Container cp=this.getContentPane();
score=new JLabel("成绩:0");
cp.add(score,BorderLayout.SOUTH);
canvas=new Canvas();
canvas.setSize(gameWidth+1, gameHeight+1);
cp.add(score,BorderLayout.CENTER);
this.pack();
this.setResizable(false);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setVisible(true);
}
@Override
public void update(Observable observer, Object obj) {//实现Observer接口定义的update方法
// TODO Auto-generated method stub
MoveOperate operate=(MoveOperate) observer; //获取被监视的模型
Graphics graphics=canvas.getGraphics(); //获得画笔
graphics.setColor(Color.WHITE);
graphics.fillRect(0, 0, gameWidth, gameHeight);
graphics.setColor(Color.BLACK);
LinkedList list=operate.getMoveList(); //获得蛇
Iterator it=list.iterator();
while(it.hasNext()){
Move move=(Move)it.next();
drawMove(graphics,move); //画蛇
}
graphics.setColor(Color.RED);
Move move=operate.getAliment(); //画食物
drawMove(graphics,move);
this.updateScore(operate.getScore()); //更新得分
}

public void drawMove(Graphics g,Move n){ //根据移动路径画格子
g.fillRect(n.move_x*gridWidth, n.move_y*gridHeight, gridWidth-1, gridHeight-1);
}
public void updateScore(int score){ //更新成绩
String s="成绩:"+score;
this.score.setText(s);
}
}import java.awt.event.*;public class ControlSnake implements KeyListener {// 控制蛇的运动 private MoveOperate snake; // 贪吃蛇的模型
private SnakeFrame frame; // 视图对象
private int X; // 蛇移动区域横坐标
private int Y; // 蛇移动区域纵坐标 public ControlSnake() {
this.X = 40;
this.Y = 30;
} public ControlSnake(int X, int Y) {
this();
if ((10 < X) && (X < 200) && (10 < Y) && (Y < 200)) {
this.X = X;
this.Y = Y;
} else {
System.out.println("初始化参数出错!");
}
initSnake();
} private void initSnake() { // 初始化
snake = new MoveOperate(X, Y);
frame = new SnakeFrame(X, Y, 500, 200);
snake.addObserver(frame); // 添加观察者对象
frame.addKeyListener(this); // 添加键盘事件
(new Thread(snake)).start(); // 启动线程,蛇运动
} @Override
public void keyPressed(KeyEvent e) {// 键盘按下事件
// TODO Auto-generated method stub
int keyCode = e.getKeyCode();
// 只有在贪吃蛇处于运行状态下,才处理按键事件
if (snake.isRunning()) {
switch (keyCode) {
case KeyEvent.VK_PAGE_UP:
snake.speedUp();
break;
case KeyEvent.VK_PAGE_DOWN:
snake.speedDown();
break;
case KeyEvent.VK_P:
snake.changePauseState();
break;
case KeyEvent.VK_UP:
snake.changeDirection(snake.UP);
break;
case KeyEvent.VK_DOWN:
snake.changeDirection(snake.DOWN);
break;
case KeyEvent.VK_LEFT:
snake.changeDirection(snake.LEFT);
break;
case KeyEvent.VK_RIGHT:
snake.changeDirection(snake.RIGHT);
break;
default:
break;
}
}
if (keyCode == KeyEvent.VK_ENTER) {// 按下Enter键开始游戏
snake.resetGame();
} if (keyCode == KeyEvent.VK_S) {// 按下S键停止游戏
snake.setRunning(false);
}
} @Override
public void keyReleased(KeyEvent e) {// 键盘弹起事件
// TODO Auto-generated method stub } @Override
public void keyTyped(KeyEvent e) {// 有字符被输入事件
// TODO Auto-generated method stub }}
public class SnakeMain { /**
 * @param args
 */
public static void main(String[] args) {
// TODO Auto-generated method stub
new ControlSnake(40,30);
}}错误:
Exception in thread "Thread-3" java.lang.NullPointerException
at Snake.SnakeFrame.update(SnakeFrame.java:51)
at java.util.Observable.notifyObservers(Unknown Source)
at java.util.Observable.notifyObservers(Unknown Source)
at Snake.MoveOperate.run(MoveOperate.java:71)
at java.lang.Thread.run(Unknown Source)