用MVC方式實現(xiàn)的貪吃蛇游戲,共有4個類。運行GreedSnake運行即可。主要是觀察者模式的使用,我已經(jīng)添加了很多注釋了。
創(chuàng)新互聯(lián)專業(yè)為企業(yè)提供埇橋區(qū)網(wǎng)站建設(shè)、埇橋區(qū)做網(wǎng)站、埇橋區(qū)網(wǎng)站設(shè)計、埇橋區(qū)網(wǎng)站制作等企業(yè)網(wǎng)站建設(shè)、網(wǎng)頁設(shè)計與制作、埇橋區(qū)企業(yè)網(wǎng)站模板建站服務(wù),10余年埇橋區(qū)做網(wǎng)站經(jīng)驗,不只是建網(wǎng)站,更提供有價值的思路和整體網(wǎng)絡(luò)服務(wù)。
1、
/*
* 程序名稱:貪食蛇
* 原作者:BigF
* 修改者:algo
* 說明:我以前也用C寫過這個程序,現(xiàn)在看到BigF用Java寫的這個,發(fā)現(xiàn)雖然作者自稱是Java的初學者,
* 但是明顯編寫程序的素養(yǎng)不錯,程序結(jié)構(gòu)寫得很清晰,有些細微得地方也寫得很簡潔,一時興起之
* 下,我認真解讀了這個程序,發(fā)現(xiàn)數(shù)據(jù)和表現(xiàn)分開得很好,而我近日正在學習MVC設(shè)計模式,
* 因此嘗試把程序得結(jié)構(gòu)改了一下,用MVC模式來實現(xiàn),對源程序得改動不多。
* 我同時也為程序增加了一些自己理解得注釋,希望對大家閱讀有幫助。
*/
package mvcTest;
/**
* @author WangYu
* @version 1.0
* Description:
* /pre
* Create on :Date :2005-6-13 Time:15:57:16
* LastModified:
* History:
*/
public class GreedSnake {
public static void main(String[] args) {
SnakeModel model = new SnakeModel(20,30);
SnakeControl control = new SnakeControl(model);
SnakeView view = new SnakeView(model,control);
//添加一個觀察者,讓view成為model的觀察者
model.addObserver(view);
(new Thread(model)).start();
}
}
-------------------------------------------------------------
2、
package mvcTest;
//SnakeControl.java
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
/**
* MVC中的Controler,負責接收用戶的操作,并把用戶操作通知Model
*/
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.running){ // 運行狀態(tài)下,處理的按鍵
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;
default:
}
}
// 任何情況下處理的按鍵,按鍵導致重新啟動游戲
if (keyCode == KeyEvent.VK_R ||
keyCode == KeyEvent.VK_S ||
keyCode == KeyEvent.VK_ENTER) {
model.reset();
}
}
public void keyReleased(KeyEvent e) {
}
public void keyTyped(KeyEvent e) {
}
}
-------------------------------------------------------------
3、
/*
*
*/
package mvcTest;
/**
* 游戲的Model類,負責所有游戲相關(guān)數(shù)據(jù)及運行
* @author WangYu
* @version 1.0
* Description:
* /pre
* Create on :Date :2005-6-13 Time:15:58:33
* LastModified:
* History:
*/
//SnakeModel.java
import javax.swing.*;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.Observable;
import java.util.Random;
/**
* 游戲的Model類,負責所有游戲相關(guān)數(shù)據(jù)及運行
*/
class SnakeModel extends Observable implements Runnable {
boolean[][] matrix; // 指示位置上有沒蛇體或食物
LinkedList nodeArray = new LinkedList(); // 蛇體
Node food;
int maxX;
int maxY;
int direction = 2; // 蛇運行的方向
boolean running = false; // 運行狀態(tài)
int timeInterval = 200; // 時間間隔,毫秒
double speedChangeRate = 0.75; // 每次得速度變化率
boolean paused = false; // 暫停標志
int score = 0; // 得分
int countMove = 0; // 吃到食物前移動的次數(shù)
// UP and DOWN should be even
// RIGHT and LEFT should be odd
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; // 時間間隔,毫秒
paused = false; // 暫停標志
score = 0; // 得分
countMove = 0; // 吃到食物前移動的次數(shù)
// initial matirx, 全部清0
matrix = new boolean[maxX][];
for (int i = 0; i maxX; ++i) {
matrix[i] = new boolean[maxY];
Arrays.fill(matrix[i], false);
}
// initial the snake
// 初始化蛇體,如果橫向位置超過20個,長度為10,否則為橫向位置的一半
int initArrayLength = maxX 20 ? 10 : maxX / 2;
nodeArray.clear();
for (int i = 0; i initArrayLength; ++i) {
int x = maxX / 2 + i;//maxX被初始化為20
int y = maxY / 2; //maxY被初始化為30
//nodeArray[x,y]: [10,15]-[11,15]-[12,15]~~[20,15]
//默認的運行方向向上,所以游戲一開始nodeArray就變?yōu)椋?/p>
// [10,14]-[10,15]-[11,15]-[12,15]~~[19,15]
nodeArray.addLast(new Node(x, y));
matrix[x][y] = true;
}
// 創(chuàng)建食物
food = createFood();
matrix[food.x][food.y] = true;
}
public void changeDirection(int newDirection) {
// 改變的方向不能與原來方向同向或反向
if (direction % 2 != newDirection % 2) {
direction = newDirection;
}
}
/**
* 運行一次
* @return
*/
public boolean moveOn() {
Node n = (Node) nodeArray.getFirst();
int x = n.x;
int y = n.y;
// 根據(jù)方向增減坐標值
switch (direction) {
case UP:
y--;
break;
case DOWN:
y++;
break;
case LEFT:
x--;
break;
case RIGHT:
x++;
break;
}
// 如果新坐標落在有效范圍內(nèi),則進行處理
if ((0 = x x maxX) (0 = y y maxY)) {
if (matrix[x][y]) { // 如果新坐標的點上有東西(蛇體或者食物)
if (x == food.x y == food.y) { // 吃到食物,成功
nodeArray.addFirst(food); // 從蛇頭贈長
// 分數(shù)規(guī)則,與移動改變方向的次數(shù)和速度兩個元素有關(guān)
int scoreGet = (10000 - 200 * countMove) / timeInterval;
score += scoreGet 0 ? scoreGet : 10;
countMove = 0;
food = createFood(); // 創(chuàng)建新的食物
matrix[food.x][food.y] = true; // 設(shè)置食物所在位置
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() {
running = true;
while (running) {
try {
Thread.sleep(timeInterval);
} catch (Exception e) {
break;
}
if (!paused) {
if (moveOn()) {
setChanged(); // Model通知View數(shù)據(jù)已經(jīng)更新
notifyObservers();
} else {
JOptionPane.showMessageDialog(null,
"you failed",
"Game Over",
JOptionPane.INFORMATION_MESSAGE);
break;
}
}
}
running = false;
}
private Node createFood() {
int x = 0;
int y = 0;
// 隨機獲取一個有效區(qū)域內(nèi)的與蛇體和食物不重疊的位置
do {
Random r = new Random();
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() {
paused = !paused;
}
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;
}
}
------------------------------------------------------------
4、
package mvcTest;
//SnakeView.java
import javax.swing.*;
import java.awt.*;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Observable;
import java.util.Observer;
/**
* MVC模式中得Viewer,只負責對數(shù)據(jù)的顯示,而不用理會游戲的控制邏輯
*/
public class SnakeView implements Observer {
SnakeControl control = null;
SnakeModel model = null;
JFrame mainFrame;
Canvas paintCanvas;
JLabel labelScore;
public static final int canvasWidth = 200;
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("GreedSnake");
Container cp = mainFrame.getContentPane();
// 創(chuàng)建頂部的分數(shù)顯示
labelScore = new JLabel("Score:");
cp.add(labelScore, BorderLayout.NORTH);
// 創(chuàng)建中間的游戲顯示區(qū)域
paintCanvas = new Canvas();
paintCanvas.setSize(canvasWidth + 1, canvasHeight + 1);
paintCanvas.addKeyListener(control);
cp.add(paintCanvas, BorderLayout.CENTER);
// 創(chuàng)建底下的幫助欄
JPanel panelButtom = new JPanel();
panelButtom.setLayout(new BorderLayout());
JLabel labelHelp;
labelHelp = new JLabel("PageUp, PageDown for speed;", JLabel.CENTER);
panelButtom.add(labelHelp, BorderLayout.NORTH);
labelHelp = new JLabel("ENTER or R or S for start;", JLabel.CENTER);
panelButtom.add(labelHelp, BorderLayout.CENTER);
labelHelp = new JLabel("SPACE or P for pause", JLabel.CENTER);
panelButtom.add(labelHelp, BorderLayout.SOUTH);
cp.add(panelButtom, 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();
//draw background
g.setColor(Color.WHITE);
g.fillRect(0, 0, canvasWidth, canvasHeight);
// draw the snake
g.setColor(Color.BLACK);
LinkedList na = model.nodeArray;
Iterator it = na.iterator();
while (it.hasNext()) {
Node n = (Node) it.next();
drawNode(g, n);
}
// draw the food
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();
}
}
希望采納
J2ME貪吃蛇源代碼——200行左右,包含詳細注釋 package snake;import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;public class SnakeMIDlet extends MIDlet {
SnakeCanvas displayable = new SnakeCanvas();
public SnakeMIDlet() {
Display.getDisplay(this).setCurrent(displayable);
}public void startApp() {}public void pauseApp() {}public void destroyApp(boolean unconditional) {}}//文件名:SnakeCanvas.javapackage snake;import java.util.*;
import javax.microedition.lcdui.*;/**
* 貪吃蛇游戲
*/
public class SnakeCanvas extends Canvas implements Runnable{
/**存儲貪吃蛇節(jié)點坐標,其中第二維下標為0的代表x坐標,第二維下標是1的代表y坐標*/
int[][] snake = new int[200][2];
/**已經(jīng)使用的節(jié)點數(shù)量*/
int snakeNum;
/**貪吃蛇運動方向,0代表向上,1代表向下,2代表向左,3代表向右*/
int direction;
/*移動方向*/
/**向上*/
private final int DIRECTION_UP = 0;
/**向下*/
private final int DIRECTION_DOWN = 1;
/**向左*/
private final int DIRECTION_LEFT = 2;
/**向右*/
private final int DIRECTION_RIGHT = 3;/**游戲區(qū)域?qū)挾?/
int width;
/**游戲區(qū)域高度*/
int height;/**蛇身單元寬度*/
private final byte SNAKEWIDTH = 4;/**是否處于暫停狀態(tài),true代表暫停*/
boolean isPaused = false;
/**是否處于運行狀態(tài),true代表運行*/
boolean isRun = true;/**時間間隔*/
private final int SLEEP_TIME = 300;
/**食物的X坐標*/
int foodX;
/**食物的Y坐標*/
int foodY;
/**食物的閃爍控制*/
boolean b = true;
/**Random對象*/
Random random = new Random();
public SnakeCanvas() {
//初始化
init();
width = this.getWidth();
height = this.getHeight();
//啟動線程
new Thread(this).start();
}/**
* 初始化開始數(shù)據(jù)
*/
private void init(){
//初始化節(jié)點數(shù)量
snakeNum = 7;
//初始化節(jié)點數(shù)據(jù)
for(int i = 0;i snakeNum;i++){
snake[i][0] = 100 - SNAKEWIDTH * i;
snake[i][1] = 40;
}
//初始化移動方向
direction = DIRECTION_RIGHT;
//初始化食物坐標
foodX = 100;
foodY = 100;
}protected void paint(Graphics g) {
//清屏
g.setColor(0xffffff);
g.fillRect(0,0,width,height);
g.setColor(0);//繪制蛇身
for(int i = 0;i snakeNum;i++){
g.fillRect(snake[i][0],snake[i][1],SNAKEWIDTH,SNAKEWIDTH);
}
//繪制食物
if(b){
g.fillRect(foodX,foodY,SNAKEWIDTH,SNAKEWIDTH);
}
}private void move(int direction){
//蛇身移動
for(int i = snakeNum - 1;i 0;i--){
snake[i][0] = snake[i - 1][0];
snake[i][1] = snake[i - 1][1];
}//第一個單元格移動
switch(direction){
case DIRECTION_UP:
snake[0][1] = snake[0][1] - SNAKEWIDTH;
break;
case DIRECTION_DOWN:
snake[0][1] = snake[0][1] + SNAKEWIDTH;
break;
case DIRECTION_LEFT:
snake[0][0] = snake[0][0] - SNAKEWIDTH;
break;
case DIRECTION_RIGHT:
snake[0][0] = snake[0][0] + SNAKEWIDTH;
break;
}
}
/**
* 吃掉食物,自身增長
*/
private void eatFood(){
//判別蛇頭是否和食物重疊
if(snake[0][0] == foodX snake[0][1] == foodY){
snakeNum++;
generateFood();
}
}
/**
* 產(chǎn)生食物
* 說明:食物的坐標必須位于屏幕內(nèi),且不能和蛇身重合
*/
private void generateFood(){
while(true){
foodX = Math.abs(random.nextInt() % (width - SNAKEWIDTH + 1))
/ SNAKEWIDTH * SNAKEWIDTH;
foodY = Math.abs(random.nextInt() % (height - SNAKEWIDTH + 1))
/ SNAKEWIDTH * SNAKEWIDTH;
boolean b = true;
for(int i = 0;i snakeNum;i++){
if(foodX == snake[i][0] snake[i][1] == foodY){
b = false;
break;
}
}
if(b){
break;
}
}
}
/**
* 判斷游戲是否結(jié)束
* 結(jié)束條件:
* 1、蛇頭超出邊界
* 2、蛇頭碰到自身
*/
private boolean isGameOver(){
//邊界判別
if(snake[0][0] 0 || snake[0][0] (width - SNAKEWIDTH) ||
snake[0][1] 0 || snake[0][1] (height - SNAKEWIDTH)){
return true;
}
//碰到自身
for(int i = 4;i snakeNum;i++){
if(snake[0][0] == snake[i][0]
snake[0][1] == snake[i][1]){
return true;
}
}
return false;
}/**
* 事件處理
*/
public void keyPressed(int keyCode){
int action = this.getGameAction(keyCode);
//改變方向
switch(action){
case UP:
if(direction != DIRECTION_DOWN){
direction = DIRECTION_UP;
}
break;
case DOWN:
if(direction != DIRECTION_UP){
direction = DIRECTION_DOWN;
}
break;
case LEFT:
if(direction != DIRECTION_RIGHT){
direction = DIRECTION_LEFT;
}
break;
case RIGHT:
if(direction != DIRECTION_LEFT){
direction = DIRECTION_RIGHT;
}
break;
case FIRE:
//暫停和繼續(xù)
isPaused = !isPaused;
break;
}
}/**
* 線程方法
* 使用精確延時
*/
public void run(){
try{
while (isRun) {
//開始時間
long start = System.currentTimeMillis();
if(!isPaused){
//吃食物
eatFood();
//移動
move(direction);
//結(jié)束游戲
if(isGameOver()){
break;
}
//控制閃爍
b = !b;
}
//重新繪制
repaint();
long end = System.currentTimeMillis();
//延時
if(end - start SLEEP_TIME){
Thread.sleep(SLEEP_TIME - (end - start));
}
}
}catch(Exception e){}
}
}
貪吃蛇
import java.awt.*;
import java.awt.event.*;
public class GreedSnake //主類
{
/**
* @param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
new MyWindow();
}
}
class MyPanel extends Panel implements KeyListener,Runnable//自定義面板類,繼承了鍵盤和線程接口
{
Button snake[]; //定義蛇按鈕
int shu=0; //蛇的節(jié)數(shù)
int food[]; //食物數(shù)組
boolean result=true; //判定結(jié)果是輸 還是贏
Thread thread; //定義線程
static int weix,weiy; //食物位置
boolean t=true; //判定游戲是否結(jié)束
int fangxiang=0; //蛇移動方向
int x=0,y=0; //蛇頭位置
MyPanel()
{
setLayout(null);
snake=new Button[20];
food=new int [20];
thread=new Thread(this);
for(int j=0;j20;j++)
{
food[j]=(int)(Math.random()*99);//定義20個隨機食物
}
weix=(int)(food[0]*0.1)*60; //十位*60為橫坐標
weiy=(int)(food[0]%10)*40; //個位*40為縱坐標
for(int i=0;i20;i++)
{
snake[i]=new Button();
}
add(snake[0]);
snake[0].setBackground(Color.black);
snake[0].addKeyListener(this); //為蛇頭添加鍵盤監(jiān)視器
snake[0].setBounds(0,0,10,10);
setBackground(Color.cyan);
}
public void run() //接收線程
{
while(t)
{
if(fangxiang==0)//向右
{
try
{
x+=10;
snake[0].setLocation(x, y);//設(shè)置蛇頭位置
if(x==weixy==weiy) //吃到食物
{
shu++;
weix=(int)(food[shu]*0.1)*60;
weiy=(int)(food[shu]%10)*40;
repaint(); //重繪下一個食物
add(snake[shu]); //增加蛇節(jié)數(shù)和位置
snake[shu].setBounds(snake[shu-1].getBounds());
}
thread.sleep(100); //睡眠100ms
}
catch(Exception e){}
}
else if(fangxiang==1)//向左
{
try
{
x-=10;
snake[0].setLocation(x, y);
if(x==weixy==weiy)
{
shu++;
weix=(int)(food[shu]*0.1)*60;
weiy=(int)(food[shu]%10)*40;
repaint();
add(snake[shu]);
snake[shu].setBounds(snake[shu-1].getBounds());
}
thread.sleep(100);
}
catch(Exception e){}
}
else if(fangxiang==2)//向上
{
try
{
y-=10;
snake[0].setLocation(x, y);
if(x==weixy==weiy)
{
shu++;
weix=(int)(food[shu]*0.1)*60;
weiy=(int)(food[shu]%10)*40;
repaint();
add(snake[shu]);
snake[shu].setBounds(snake[shu-1].getBounds());
}
thread.sleep(100);
}
catch(Exception e){}
}
else if(fangxiang==3)//向下
{
try
{
y+=10;
snake[0].setLocation(x, y);
if(x==weixy==weiy)
{
shu++;
weix=(int)(food[shu]*0.1)*60;
weiy=(int)(food[shu]%10)*40;
repaint();
add(snake[shu]);
snake[shu].setBounds(snake[shu-1].getBounds());
}
thread.sleep(100);
}
catch(Exception e){}
}
int num1=shu;
while(num11)//判斷是否咬自己的尾巴
{
if(snake[num1].getBounds().x==snake[0].getBounds().xsnake[num1].getBounds().y==snake[0].getBounds().y)
{
t=false;
result=false;
repaint();
}
num1--;
}
if(x0||x=this.getWidth()||y0||y=this.getHeight())//判斷是否撞墻
{
t=false;
result=false;
repaint();
}
int num=shu;
while(num0) //設(shè)置蛇節(jié)位置
{
snake[num].setBounds(snake[num-1].getBounds());
num--;
}
if(shu==15) //如果蛇節(jié)數(shù)等于15則勝利
{
t=false;
result=true;
repaint();
}
}
}
public void keyPressed(KeyEvent e) //按下鍵盤方向鍵
{
if(e.getKeyCode()==KeyEvent.VK_RIGHT)//右鍵
{
if(fangxiang!=1)//如果先前方向不為左
fangxiang=0;
}
else if(e.getKeyCode()==KeyEvent.VK_LEFT)
{ if(fangxiang!=0)
fangxiang=1;
}
else if(e.getKeyCode()==KeyEvent.VK_UP)
{ if(fangxiang!=3)
fangxiang=2;
}
else if(e.getKeyCode()==KeyEvent.VK_DOWN)
{ if(fangxiang!=2)
fangxiang=3;
}
}
public void keyTyped(KeyEvent e)
{
}
public void keyReleased(KeyEvent e)
{
}
public void paint(Graphics g) //在面板上繪圖
{
int x1=this.getWidth()-1;
int y1=this.getHeight()-1;
g.setColor(Color.red);
g.fillOval(weix, weiy, 10, 10);//食物
g.drawRect(0, 0, x1, y1); //墻
if(t==falseresult==false)
g.drawString("GAME OVER!", 250, 200);//輸出游戲失敗
else if(t==falseresult==true)
g.drawString("YOU WIN!", 250, 200);//輸出游戲成功
}
}
class MyWindow extends Frame implements ActionListener//自定義窗口類
{
MyPanel my;
Button btn;
Panel panel;
MyWindow()
{
super("GreedSnake");
my=new MyPanel();
btn=new Button("begin");
panel=new Panel();
btn.addActionListener(this);
panel.add(new Label("begin后請按Tab鍵選定蛇"));
panel.add(btn);
panel.add(new Label("按上下左右鍵控制蛇行動"));
add(panel,BorderLayout.NORTH);
add(my,BorderLayout.CENTER);
setBounds(100,100,610,500);
setVisible(true);
validate();
addWindowListener(new WindowAdapter()
{
public void windowClosing(WindowEvent e)
{
System.exit(0);
}
});
}
public void actionPerformed(ActionEvent e)//按下begin按鈕
{
if(e.getSource()==btn)
{
try
{
my.thread.start(); //開始線程
my.validate();
}
catch(Exception ee){}
}
}
}
/**
* MVC模式中得Viewer,只負責對數(shù)據(jù)的顯示,而不用理會游戲的控制邏輯
*/
class SnakeView implements Observer {
SnakeControl control = null; //實例話一個SnakeControl對象是control ..SnakeControl在jdk中不存在可能是第3方的或者是自己編寫的一個類吧
SnakeModel model = null;//如上
JFrame mainFrame; //創(chuàng)建一個面板類jframe
Canvas paintCanvas;
/**
*Canvas 組件表示屏幕上一個空白矩形區(qū)域,應(yīng)用程序可以在該區(qū)域內(nèi)繪圖,或者可以從該區(qū)域捕獲用戶的輸入事件。
*應(yīng)用程序必須為 Canvas 類創(chuàng)建子類,以獲得有用的功能(如創(chuàng)建自定義組件)。必須重寫 paint 方法,以便在 canvas 上執(zhí)行自定義圖形。
*/
JLabel labelScore;//....不說了吧..
public static final int canvasWidth = 200; //常量寬度200
public static final int canvasHeight = 300;//常量高300
public static final int nodeWidth = 10;//常量寬度10
public static final int nodeHeight = 10;//常量高度10
public SnakeView(SnakeModel model, SnakeControl control) {
this.model = model;
this.control = control;
mainFrame = new JFrame("GreedSnake");//創(chuàng)建jframe 標題是GreedSnake
Container cp = mainFrame.getContentPane(); //得到j(luò)fram的容器
labelScore = new JLabel("Score:"); //創(chuàng)建jlabel 標簽內(nèi)容是"Score:"
cp.add(labelScore, BorderLayout.NORTH);/將jlabel添加到j(luò)fram的容器中去
paintCanvas = new Canvas(); //創(chuàng)建新繪圖區(qū)
paintCanvas.setSize(canvasWidth + 1, canvasHeight + 1); //設(shè)置繪圖區(qū)大小
paintCanvas.addKeyListener(control);//添加鍵盤監(jiān)聽器control
cp.add(paintCanvas, BorderLayout.CENTER);//將繪圖區(qū)添加到j(luò)fram容器中去.布局是BorderLayout的CENTER位置.就是東西南北中的中間
JPanel panelButtom = new JPanel();//創(chuàng)建一個panel
panelButtom.setLayout(new BorderLayout());//設(shè)置布局是BorderLayout
JLabel labelHelp;//標簽的創(chuàng)建和添加開始了.......new了3遍就是實例化了3個都加到了panle的中間 ,標簽的內(nèi)容都是""中間的
labelHelp = new JLabel("PageUp, PageDown for speed;", JLabel.CENTER);
panelButtom.add(labelHelp, BorderLayout.NORTH);
labelHelp = new JLabel("ENTER or R or S for start;", JLabel.CENTER);
panelButtom.add(labelHelp, BorderLayout.CENTER);
labelHelp = new JLabel("SPACE or P for pause", JLabel.CENTER);
panelButtom.add(labelHelp, BorderLayout.SOUTH);
cp.add(panelButtom, BorderLayout.SOUTH);//把這個panel添加到j(luò)frame的容器里面去
mainFrame.addKeyListener(control);//為jframe添加鍵盤監(jiān)聽器
mainFrame.pack();//調(diào)整此窗口的大小,以適合其子組件的首選大小和布局。如果該窗口和/或其所有者仍不可顯示,則兩者在計算首選大小之前變得可顯示。在計算首選大小之后,將會驗證該 Window。
mainFrame.setResizable(false);//設(shè)置此 frame 是否可由用戶調(diào)整大小。false就是不能
mainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//點擊又上角的就是關(guān)閉
mainFrame.setVisible(true);//設(shè)置jfram為能看到
}
void repaint() {
Graphics g = paintCanvas.getGraphics();//Graphics詳細看jdk文檔..我就知道是一個所有圖形上下文的抽象基類..這里應(yīng)該是那個畫圖面板
g.setColor(Color.WHITE);//設(shè)置這個顏色
g.fillRect(0, 0, canvasWidth, canvasHeight);//畫一個矩形在x,y處畫一個寬是200,高是300的矩形,
g.setColor(Color.BLACK);//顏色是黑的
LinkedList na = model.nodeArray;//LinkedList 類 List 接口的鏈接列表實現(xiàn)就是一個集合對象了,因為不知道SnakeModel具體是一個什么類這里就當作這個類能得到一個集合
Iterator it = na.iterator();//得到迭代器去迭代這個集合
while (it.hasNext()) {//開始用while迭代
Node n = (Node) it.next();//得到一個集合中元素.是一個Node....這里的Node不知道是什么對象..有待于研究
drawNode(g, n);//調(diào)用另外方法
}
g.setColor(Color.RED);//設(shè)置顏色是紅的
Node n = model.food;//得到一個新node..看來Node這里應(yīng)該是一個坐標..這里的坐標就是蛇吃的那個東西
drawNode(g, n);//畫這個東西..
updateScore();//調(diào)用..
}
private void drawNode(Graphics g, Node n) {//這是一個畫方的方法..動態(tài)改變方型的位置
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) {//這個就是Observer監(jiān)聽類必須實現(xiàn)的方法..這里是去調(diào)用repaint()方法..repaint方法就是不斷去改變畫圖版里面的內(nèi)容的
repaint();
}
}
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JFrame;
public class InterFace extends JFrame {
/**
* WIDTH:寬
* HEIGHT:高
* SLEEPTIME:可以看作蛇運動的速度
* L = 1,R = 2, U = 3, D = 4 左右上下代碼
*/
public static final int WIDTH = 800, HEIGHT = 600, SLEEPTIME = 200, L = 1,R = 2, U = 3, D = 4;
BufferedImage offersetImage= new BufferedImage(WIDTH, HEIGHT,BufferedImage.TYPE_3BYTE_BGR);;
Rectangle rect = new Rectangle(20, 40, 15 * 50, 15 * 35);
Snake snake;
Node node;
public InterFace() {
//創(chuàng)建"蛇"對象
snake = new Snake(this);
//創(chuàng)建"食物"對象
createNode();
this.setBounds(100, 100, WIDTH, HEIGHT);
//添加鍵盤監(jiān)聽器
this.addKeyListener(new KeyAdapter() {
public void keyPressed(KeyEvent arg0) {
System.out.println(arg0.getKeyCode());
switch (arg0.getKeyCode()) {
//映射上下左右4個鍵位
case KeyEvent.VK_LEFT:
snake.dir = L;
break;
case KeyEvent.VK_RIGHT:
snake.dir = R;
break;
case KeyEvent.VK_UP:
snake.dir = U;
break;
case KeyEvent.VK_DOWN:
snake.dir = D;
}
}
});
this.setTitle("貪吃蛇 0.1 By : Easy");
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
this.setVisible(true);
//啟動線程,開始執(zhí)行
new Thread(new ThreadUpadte()).start();
}
public void paint(Graphics g) {
Graphics2D g2d = (Graphics2D) offersetImage.getGraphics();
g2d.setColor(Color.white);
g2d.fillRect(0, 0, WIDTH, HEIGHT);
g2d.setColor(Color.black);
g2d.drawRect(rect.x, rect.y, rect.width, rect.height);
//如果蛇碰撞(吃)到食物,則創(chuàng)建新食物
if (snake.hit(node)) {
createNode();
}
snake.draw(g2d);
node.draw(g2d);
g.drawImage(offersetImage, 0, 0, null);
}
class ThreadUpadte implements Runnable {
public void run() {
//無限重繪畫面
while (true) {
try {
Thread.sleep(SLEEPTIME);
repaint(); //
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
/**
* 創(chuàng)建食物
*/
public void createNode() {
//隨機食物的出現(xiàn)位置
int x = (int) (Math.random() * 650) + 50,y = (int) (Math.random() * 500) + 50;
Color color = Color.blue;
node = new Node(x, y, color);
}
public static void main(String args[]) {
new InterFace();
}
}
/**
* 節(jié)點類(包括食物和蛇的身軀組成節(jié)點)
*/
class Node {
int x, y, width = 15, height = 15;
Color color;
public Node(int x, int y, Color color) {
this(x, y);
this.color = color;
}
public Node(int x, int y) {
this.x = x;
this.y = y;
this.color = color.black;
}
public void draw(Graphics2D g2d) {
g2d.setColor(color);
g2d.drawRect(x, y, width, height);
}
public Rectangle getRect() {
return new Rectangle(x, y, width, height);
}
}
/**
* 蛇
*/
class Snake {
public ListNode nodes = new ArrayListNode();
InterFace interFace;
int dir=InterFace.R;
public Snake(InterFace interFace) {
this.interFace = interFace;
nodes.add(new Node(20 + 150, 40 + 150));
addNode();
}
/**
* 是否碰撞到食物
* @return true 是 false 否
*/
public boolean hit(Node node) {
//遍歷整個蛇體是否與食物碰撞
for (int i = 0; i nodes.size(); i++) {
if (nodes.get(i).getRect().intersects(node.getRect())) {
addNode();
return true;
}
}
return false;
}
public void draw(Graphics2D g2d) {
for (int i = 0; i nodes.size(); i++) {
nodes.get(i).draw(g2d);
}
move();
}
public void move() {
nodes.remove((nodes.size() - 1));
addNode();
}
public synchronized void addNode() {
Node nodeTempNode = nodes.get(0);
//如果方向
switch (dir) {
case InterFace.L:
//判斷是否會撞墻
if (nodeTempNode.x = 20) {
nodeTempNode = new Node(20 + 15 * 50, nodeTempNode.y);
}
nodes.add(0, new Node(nodeTempNode.x - nodeTempNode.width,
nodeTempNode.y));
break;
case InterFace.R:
//判斷是否會撞墻
if (nodeTempNode.x = 20 + 15 * 50 - nodeTempNode.width) {
nodeTempNode = new Node(20 - nodeTempNode.width, nodeTempNode.y);
}
nodes.add(0, new Node(nodeTempNode.x + nodeTempNode.width,
nodeTempNode.y));
break;
case InterFace.U:
//判斷是否會撞墻
if (nodeTempNode.y = 40) {
nodeTempNode = new Node(nodeTempNode.x, 40 + 15 * 35);
}
nodes.add(0, new Node(nodeTempNode.x, nodeTempNode.y - nodeTempNode.height));
break;
case InterFace.D:
//判斷是否會撞墻
if (nodeTempNode.y = 40 + 15 * 35 - nodeTempNode.height) {
nodeTempNode = new Node(nodeTempNode.x,40 - nodeTempNode.height);
}
nodes.add(0, new Node(nodeTempNode.x, nodeTempNode.y + nodeTempNode.height));
break;
}
}
}
public
class
Snake
implements
Data//蛇類實現(xiàn)了data接口
{
public
Snake()
{
array.add(new
Point(10,
10));//
array.add(new
Point(10,
11));//
array.add(new
Point(10,
12));//
array.add(new
Point(10,
13));//
array.add(new
Point(10,
14));//這五句話是構(gòu)造一個蛇,這條蛇由五個坐標點連成
currentFlag
=UPFLAG
;//當前的運動方向設(shè)為向上
lifeFlag
=
true;//當前蛇的生命設(shè)為活著
}
public
void
move()//蛇的運動函數(shù)
{
tair
=
(Point)array.get(array.size()
-
1);//得到蛇的尾巴
int
len
=
array.size()
-
2;
for(int
i
=
len;
i
=
0;
i--)//這個for循環(huán)把蛇的每一個點都坐標偏移即運動了
{
Point
leftPoint
=
(Point)array.get(i);
Point
rightPoint
=
(Point)array.get(i
+
1);
rightPoint.x
=
leftPoint.x;
rightPoint.y
=
leftPoint.y;//每一個右邊的點等于其左邊的點,像蛇那樣的一縮一張的運動
}
}
public
void
moveHeadLeft()//把蛇的頭部轉(zhuǎn)向左邊
{
if(currentFlag
==
RIGHTFLAG
||
currentFlag
==
LEFTFLAG)//如果運動方向設(shè)為向左或向右則不執(zhí)行
{
return;
}
move();
Point
point
=
(Point)(array.get(0));
//得到蛇頭部
point.x
=
point.x
-
1;//蛇頭部橫坐標減一,縱坐標不變
point.y
=
point.y;
if(point.x
LEFT)//如果蛇頭部不能再左,就不執(zhí)行
{
lifeFlag
=
false;
}
currentFlag
=
LEFTFLAG;//將蛇運動方向改為左
}