最近要重新学习Java,找个例子来实践,在网上找了很多例程来看,自己重新编写了一个贪食蛇的例子,记在这里。
1.SnakeModel.java
import java.util.Observable; import java.util.LinkedList; import java.util.Arrays; import java.util.Random; import javax.swing.*; //Observable类一般代表数据,实现接口Runnable表明创建一个进程 public class SnakeModel extends Observable implements Runnable { boolean[][] matrix; //指示位置上是否有蛇体或食物; LinkedList nodeArray = new LinkedList(); //蛇体 Node food; int maxX; //X轴边线,即为画布中X轴个数 int maxY; //y轴边线,即为画布中Y轴个数 int direction = UP; //蛇运行的方向,初始为向上(UP) boolean isRunning = false; //运行状态 int timeInterval = 200; //时间间隔,毫秒级 double speedChangeRate = 0.75; //每次的速度变化率 boolean isPaused = false ;//暂停标志 int score = 0; //得分 int countMove = 0; //吃到食物前移动的次数 public static final int UP = 2; public static final int DOWN =4; public static final int LEFT= 1; public static final int RIGHT =3; public SnakeModel(int maxX, int maxY){ this.maxX = maxX; this.maxY = maxY; reset(); } public void reset(){ direction = SnakeModel.UP; timeInterval = 200; isPaused = false; score = 0; countMove = 0; //初始化矩阵 matrix = new boolean[maxX][]; for(int i = 0; i < maxX; ++i){ //关于数组的新建 matrix[i] = new boolean[maxY]; Arrays.fill(matrix[i],false); } //初始化蛇体 //初始蛇体长度按照横轴的长度,若横轴>20,则蛇体长度为10,否则为横轴一半 int initArrayLength = maxX > 20 ? 10 : maxX / 2; nodeArray.clear(); for(int i = 0; i < initArrayLength; ++i){ int x = maxX / 2; int y = maxY / 2; nodeArray.addLast(new Node(x, y)); matrix[x][y] = true; } //创建食物 food = createFood(); matrix[food.x][food.y] = true; } public void changeDirection(int newDirection){ //改变方向不能与原方向同向或反向 if((direction & 1) != (newDirection & 1)){ direction = newDirection; } } //移动一格 public boolean moveOn(){ Node n = (Node) nodeArray.getFirst(); int x = n.x; int y = n.y; //根据方向增减坐标,这里的增减是以数组平面的,而不是普通的坐标轴 switch(direction){ case UP: y--;break; //向上时,数组的y变小 case DOWN: y++;break; //向下时,数组的y变大 case LEFT: x--;break; case RIGHT: x++;break; } //如果新坐标落在有效范围内,则进行处理 if((0<=x && x<maxX) && (0<=y && y<maxY)){ if(matrix[x][y]){//如果新坐标的点上有东西(蛇体或食物) if( x==food.x && y==food.y){//吃到食物,成功 nodeArray.addFirst(food);//从蛇头增长 int scoreGet = (10000 - 200 * countMove) / timeInterval; //计分标准为最低10分 score += (scoreGet > 10) ? scoreGet : 10; //countMove清0,重新计算,因为countMove越大,得到的分数越低 //countMove的涵义是吃到食物前移动的次数 countMove = 0; food = createFood(); //创建新的食物 matrix[food.x][food.y] = true; return true; }else{ //吃到蛇体本身,失败 return false; } }else{//如果坐标上没有东西,移动蛇体 nodeArray.addFirst(new Node(x, y)); matrix[x][y] = true; n = (Node) nodeArray.removeLast(); matrix[n.x][n.y] = false; countMove++; return true; } } return false; //触到边线,失败 } //作为线程运行的主体,测试时的入口 public void run(){ isRunning = true; while(isRunning){ try{ Thread.sleep(timeInterval); }catch(Exception e){ break; } if(!isPaused){ if(moveOn()){ //没有触碰到蛇体或者墙壁 //标记此 Observable 对象为已改变的对象;现在 hasChanged 方法将返回 true。 //这时,Observer会自动调用update方法 setChanged(); //则通知其所有观察者,并调用 clearChanged 方法来指示此对象不再改变。 notifyObservers(); }else{ //挂掉了 JOptionPane.showMessageDialog(null, "","You failed。Game Over!", JOptionPane.INFORMATION_MESSAGE); break; } } } isRunning = false; } private Node createFood(){ int x = 0; int y = 0; //随机获取一个有效区域内的蛇体和食物不重叠的位置 do{ Random r = new Random(); // 返回一个伪随机数,它是取自此随机数生成器序列的、在 0(包括) // 和指定值(不包括)之间均匀分布的 int 值。 x = r.nextInt(maxX); y = r.nextInt(maxY); }while(matrix[x][y]); return new Node(x, y); } public void speedUp(){ timeInterval *= speedChangeRate; } public void speedDown(){ timeInterval /= speedChangeRate; } public void changePauseState(){ isPaused = !isPaused; } public String toString(){ String result = ""; for(int i = 0 ; i < nodeArray.size(); ++i ){ Node n = (Node) nodeArray.get(i); result += "[" + n.x + "," + n.y + "]"; } return result; } } class Node{ int x; int y; Node(int x, int y){ this.x = x; this.y = y; } }
2.SnakeControl.java
import java.awt.event.*; import java.awt.event.KeyEvent.*; public class SnakeControl implements KeyListener{ SnakeModel model; public SnakeControl(SnakeModel model){ this.model = model; } public void keyPressed(KeyEvent e){ int keyCode = e.getKeyCode(); if(model.isRunning){ //运行状态下,处理按键 switch(keyCode){ case KeyEvent.VK_UP: model.changeDirection(SnakeModel.UP); break; case KeyEvent.VK_DOWN: model.changeDirection(SnakeModel.DOWN); break; case KeyEvent.VK_LEFT: model.changeDirection(SnakeModel.LEFT); break; case KeyEvent.VK_RIGHT: model.changeDirection(SnakeModel.RIGHT); break; case KeyEvent.VK_ADD: case KeyEvent.VK_PAGE_UP: model.speedUp(); break; case KeyEvent.VK_SUBTRACT: case KeyEvent.VK_PAGE_DOWN: model.speedDown(); break; case KeyEvent.VK_SPACE: case KeyEvent.VK_P: model.changePauseState(); break; case KeyEvent.VK_R: case KeyEvent.VK_ENTER: model.reset(); break; default: } } } public void keyTyped(KeyEvent e){ } public void keyReleased(KeyEvent e){ } }
3.SnakeView.java
import javax.swing.*; import java.awt.*; import java.util.*; public class SnakeView implements Observer{ SnakeControl control = null; SnakeModel model = null; JFrame mainFrame; Canvas paintCanvas; JLabel labelScore; public static final int canvasWidth = 300; public static final int canvasHeight = 300; public static final int nodeWidth = 10; public static final int nodeHeight = 10; public SnakeView(SnakeModel model, SnakeControl control){ this.model = model; this.control = control; mainFrame = new JFrame("Snake"); Container cp = mainFrame.getContentPane(); //创建顶部的分数显示 labelScore = new JLabel("Score:"); cp.add(labelScore, BorderLayout.NORTH); //创建中间的游戏显示区域、 paintCanvas = new Canvas(); paintCanvas.setSize(canvasWidth + 1, canvasHeight+1); paintCanvas.addKeyListener(control); cp.add(paintCanvas, BorderLayout.CENTER); //创建地下的帮助栏 JPanel panelButton = new JPanel(); panelButton.setLayout(new BorderLayout()); JLabel labelHelp; labelHelp = new JLabel("PageUp, PageDown for speed;", JLabel.CENTER); panelButton.add(labelHelp, BorderLayout.NORTH); labelHelp = new JLabel("ENTER or R or S for start;", JLabel.CENTER); panelButton.add(labelHelp, BorderLayout.CENTER); labelHelp = new JLabel("SPACE or P for pause", JLabel.CENTER); panelButton.add(labelHelp, BorderLayout.SOUTH); cp.add(panelButton, BorderLayout.SOUTH); mainFrame.addKeyListener(control); mainFrame.pack(); mainFrame.setResizable(false); mainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); mainFrame.setVisible(true); } void repaint(){ Graphics g = paintCanvas.getGraphics(); //设置背景 g.setColor(Color.WHITE); g.fillRect(0, 0, canvasWidth, canvasHeight); //在画布上画蛇 g.setColor(Color.BLACK); LinkedList na = model.nodeArray; Iterator it = na.iterator(); while(it.hasNext()){ Node n = (Node)it.next(); drawNode(g, n); } //画食物 g.setColor(Color.RED); Node n = model.food; drawNode(g, n); //更新分数 updateScore(); } private void drawNode(Graphics g, Node n){ //矩形周围留空,提高视觉效果 g.fillRect(n.x*nodeWidth, n.y*nodeHeight, nodeWidth-1, nodeHeight-1); } public void updateScore(){ String s = "Score: " + model.score; labelScore.setText(s); } public void update(Observable o, Object arg){ repaint(); } }
4.Snake.java
public class Snake { public static void main(String[] args) { SnakeModel model = new SnakeModel(30, 30); SnakeControl control = new SnakeControl(model); SnakeView view = new SnakeView(model, control); //添加一个观察者,让view成为model的观察者 model.addObserver(view); //Thread的start方法会自动调用该线程的run()方法 (new Thread(model)).start(); } }