貪吃蛇小遊戲

看到比較好的貪吃蛇代碼,全Java寫的,和大家分享一下。

1、算法

package mvcTest;

//SnakeModel.java
import javax.swing.*;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.Observable;
import java.util.Random;


public class SnakeModel extends Observable implements Runnable 
{
  boolean[][] matrix;                         // 指示位置上有沒蛇體或食物
  LinkedList nodeArray = new LinkedList();    // 蛇體
  Node food;
  int maxX;
  int maxY;
  int direction = 2;                          // 蛇運行的方向
  boolean running = false;                    // 運行狀態

  int timeInterval = 200;                     // 時間間隔,毫秒
  double speedChangeRate = 0.75;              // 每次得速度變化率
  boolean paused = false;                     // 暫停標誌

  int score = 0;                              // 得分
  int countMove = 0;                          // 吃到食物前移動的次數

  // 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;                          // 吃到食物前移動的次數

      // 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就變爲:
          //       [10,14]-[10,15]-[11,15]-[12,15]~~[19,15]
          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 % 2 != newDirection % 2) {
          direction = newDirection;
      }
  }

 
  public boolean moveOn() {
      Node n = (Node) nodeArray.getFirst();
      int x = n.x;
      int y = n.y;

      // 根據方向增減座標值
      switch (direction) {
          case UP:
              y--;
              break;
          case DOWN:
              y++;
              break;
          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;
                  score += scoreGet > 0 ? scoreGet : 10;
                  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() {
      running = true;
      while (running) {
          try {
              Thread.sleep(timeInterval);
          } catch (Exception e) {
              break;
          }

          if (!paused) {
              if (moveOn()) {
                  setChanged();           // Model通知View數據已經更新
                  notifyObservers();
              } else {
                  JOptionPane.showMessageDialog(null,
                          "you failed",
                          "Game Over",
                          JOptionPane.INFORMATION_MESSAGE);
                  break;
              }
          }
      }
      running = false;
  }

  private Node createFood() {
      int x = 0;
      int y = 0;
      // 隨機獲取一個有效區域內的與蛇體和食物不重疊的位置
      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;
  }
}

2、key鍵控制方向。

package mvcTest;

//SnakeControl.java
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;


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){                // 運行狀態下,處理的按鍵
          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;

//SnakeView.java
import javax.swing.*;
import java.awt.*;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.Observable;
import java.util.Observer;


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();

      // 創建頂部的分數顯示
      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 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();
  }
}

4、在main中運行。

package mvcTest;

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();
	   }
	}

希望大家看了有幫助。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章