最近要重新学习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
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();
}
}