java實現貪吃蛇小遊戲
阿新 • • 發佈:2020-07-28
本文例項為大家分享了java實現貪吃蛇小遊戲的具體程式碼,供大家參考,具體內容如下
這是MVC模式的完整Java專案,編譯執行SnakeApp.java即可開始遊戲。
可擴充套件功能:
1、積分功能:可以建立得分規則的類(模型類的一部分), 在GameController的run()方法中計算得分
2、變速功能:比如加速功能,減速功能,可以在GameController的keyPressed()方法中針對特定的按鍵設定每一次移動之間的時間間隔,將Thread.sleep(Settings.DEFAULT_MOVE_INTERVAL);替換為動態的時間間隔即可
3、更漂亮的遊戲介面:修改GameView中的drawXXX方法,比如可以將食物渲染為一張圖片,Graphics有drawImage方法
View
SnakeApp.java
/* * 屬於View,用來根據相應的類展示出對應的遊戲主介面,也是接收控制資訊的第一線。 */ public class SnakeApp { public void init() { //建立遊戲窗體 JFrame window = new JFrame("一隻長不大的蛇"); //初始化500X500的棋盤,用來維持各種遊戲元素的狀態,遊戲的主要邏輯部分 Grid grid = new Grid(50*Settings.DEFAULT_NODE_SIZE,50*Settings.DEFAULT_NODE_SIZE); //傳入grid引數,新建介面元素物件 GameView gameView = new GameView(grid);//繪製遊戲元素的物件 //初始化面板 gameView.initCanvas(); //根據棋盤資訊建立控制器物件 GameController gameController = new GameController(grid); //設定視窗大小 window.setPreferredSize(new Dimension(526,548)); //往視窗中新增元素,面板物件被加入到視窗時,自動呼叫其中的paintComponent方法。 window.add(gameView.getCanvas(),BorderLayout.CENTER); //畫出蛇和棋盤和食物 GameView.draw(); //註冊視窗監聽器 window.addKeyListener((KeyListener)gameController); //啟動執行緒 new Thread(gameController).start(); //視窗關閉的行為 window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); //設定視窗大小不可變化 window.setResizable(false); //渲染和顯示視窗 window.pack(); window.setVisible(true); } //可以忽略,以後每個類中都有這麼一個測試模組 public static void main(String[] args) { SnakeApp snakeApp = new SnakeApp(); snakeApp.init(); } }
GameView.java
/* * 屬於View,用於繪製地圖、蛇、食物 */ /* Graphics 相當於一個畫筆。物件封裝了 Java 支援的基本呈現操作所需的狀態資訊。此狀態資訊包括以下屬性: 要在其上繪製的 Component 物件。 呈現和剪貼座標的轉換原點。 當前剪貼區。 當前顏色。 當前字型。 當前邏輯畫素操作函式(XOR 或 Paint)。 當前 XOR 交替顏色 */ /* java.awt.Component的repaint()方法 作用:更新元件。 如果此元件不是輕量級元件,則為了響應對 repaint 的呼叫,AWT 呼叫 update 方法。可以假定未清除背景。 Component 的 update 方法呼叫此元件的 paint 方法來重繪此元件。為響應對 repaint 的呼叫而需要其他工作的子類通常重寫此方法。重寫此方法的 Component 子類應該呼叫 super.update(g),或者直接從其 update 方法中呼叫 paint(g)。 圖形上下文的原點,即它的(0,0)座標點是此元件的左上角。圖形上下文的剪貼區域是此元件的邊界矩形。 */ public class GameView { private final Grid grid; private static JPanel canvas;//畫板,用於在這上面製作畫面,然後返回。 public GameView(Grid grid) { this.grid = grid; } //重新繪製遊戲介面元素,不斷重新呼叫paintComponent方法覆蓋原本的面板。 public static void draw() { canvas.repaint(); } //獲取畫板物件的介面 public JPanel getCanvas() { return canvas; } //對畫板進行初始化 public void initCanvas() { canvas = new JPanel() { //指向一個方法被覆蓋的新面板子類物件 //paintComponent()繪製此容器中的每個元件,Swing會在合適的時機去呼叫這個方法,展示出合適的介面,這就是典型的回撥(callback)的概念。 public void paintComponent(Graphics graphics) { super.paintComponent(graphics); //這裡必須呼叫一下父類 也就是 container的重繪方法,否則表現為之前的繪圖不會覆蓋 drawGridBackground(graphics);//畫出背景網格線 drawSnake(graphics,grid.getSnake());//畫蛇 drawFood(graphics,grid.getFood());//畫食物 } }; } //畫蛇 public void drawSnake(Graphics graphics,Snake snake) { for(Iterator<Node> i = snake.body.iterator();i.hasNext();) { Node bodyNode = (Node)i.next(); drawSquare(graphics,bodyNode,Color.BLUE); } } //畫食物 public void drawFood(Graphics graphics,Node food) { drawCircle(graphics,food,Color.ORANGE); } //畫格子背景,方便定位Snake運動軌跡,橫豎各以10為單位的50個線。 public void drawGridBackground(Graphics graphics) { graphics.setColor(Color.GRAY); canvas.setBackground(Color.BLACK); for(int i=0 ; i < 50 ; i++) { graphics.drawLine(0,i*Settings.DEFAULT_NODE_SIZE,this.grid.getWidth(),i*Settings.DEFAULT_NODE_SIZE); } for(int i=0 ; i <50 ; i++) { graphics.drawLine(i*Settings.DEFAULT_NODE_SIZE,this.grid.getHeight()); } graphics.setColor(Color.red); graphics.fillRect(0,this.grid.width,Settings.DEFAULT_NODE_SIZE); graphics.fillRect(0,Settings.DEFAULT_NODE_SIZE,this.grid.height); graphics.fillRect(this.grid.width,this.grid.height); graphics.fillRect(0,this.grid.height,this.grid.width+10,Settings.DEFAULT_NODE_SIZE); } /* * public abstract void drawLine(int x1,int y1,int x2,int y2) 在此圖形上下文的座標系中,使用當前顏色在點 (x1,y1) 和 (x2,y2) 之間畫一條線。 引數: x1 - 第一個點的 x 座標。 y1 - 第一個點的 y 座標。 x2 - 第二個點的 x 座標。 y2 - 第二個點的 y 座標。 */ //提供直接出現遊戲結束的選項框的功能。 public static void showGameOverMessage() { JOptionPane.showMessageDialog(null,"遊戲結束","短暫的蛇生到此結束",JOptionPane.INFORMATION_MESSAGE); } //畫圖形的具體方法: private void drawSquare(Graphics graphics,Node squareArea,Color color) { graphics.setColor(color); int size = Settings.DEFAULT_NODE_SIZE; graphics.fillRect(squareArea.getX(),squareArea.getY(),size - 1,size - 1); } private void drawCircle(Graphics graphics,Color color) { graphics.setColor(color); int size = Settings.DEFAULT_NODE_SIZE; graphics.fillOval(squareArea.getX(),size,size); } }
Controller
GameController
/* * 接收窗體SnakeApp傳遞過來的有意義的事件,然後傳遞給Grid,讓Grid即時的更新狀態。 * 同時根據最新狀態渲染出遊戲介面讓SnakeApp顯示 * */ public class GameController implements KeyListener,Runnable{ private Grid grid; private boolean running; public GameController(Grid grid){ this.grid = grid; this.running = true; } @Override public void keyPressed(KeyEvent e) { int keyCode = e.getKeyCode(); switch(keyCode) { case KeyEvent.VK_UP: grid.changeDirection(Direction.UP); break; case KeyEvent.VK_DOWN: grid.changeDirection(Direction.DOWN); break; case KeyEvent.VK_LEFT: grid.changeDirection(Direction.LEFT); break; case KeyEvent.VK_RIGHT: grid.changeDirection(Direction.RIGHT); break; } isOver(grid.nextRound()); GameView.draw(); } private void isOver(boolean flag) { if(!flag) {//如果下一步更新棋盤時,出現遊戲結束返回值(如果flag為假)則 this.running = false; GameView.showGameOverMessage(); System.exit(0); } } @Override /*run()函式中的核心邏輯是典型的控制器(Controller)邏輯: 修改模型(Model):呼叫Grid的方法使遊戲進入下一步 更新檢視(View):呼叫GameView的方法重新整理頁面*/ public void run() { while(running) { try { Thread.sleep(Settings.DEFAULT_MOVE_INTERVAL); isOver(grid.nextRound()); GameView.draw(); } catch (InterruptedException e) { break; } // 進入遊戲下一步 // 如果結束,則退出遊戲 // 如果繼續,則繪製新的遊戲頁面 } running = false; } @Override public void keyTyped(KeyEvent e) { } @Override public void keyReleased(KeyEvent e) { } }
Model
Grid
/* * 隨機生成食物,維持貪吃蛇的狀態,根據SnakeApp中的使用者互動來控制遊戲狀態。 */ public class Grid { private Snake snake; int width; int height; Node food; private Direction snakeDirection =Direction.LEFT; public Grid(int length,int high) { super(); this.width = length; this.height = high; initSnake(); food = creatFood(); } //在棋盤上初始化一個蛇 private void initSnake() { snake = new Snake(); int x = width/2; int y = height/2; for(int i = 0;i<5;i++) { snake.addTail(new Node(x,y)); x = x+Settings.DEFAULT_NODE_SIZE; } } //棋盤上隨機制造食物的功能。 //一直迴圈獲取隨機值,直到三個條件都不滿足。 private Node creatFood() { int x,y; do { x =(int)(Math.random()*100)+10; y =(int)(Math.random()*100)+10; System.out.println(x); System.out.println(y); System.out.println(this.width); System.out.println(this.height); }while(x>=this.width-10 || y>=this.height-10 || snake.hasNode(new Node(x,y))); food = new Node(x,y); return food; } //提供下一步更新棋盤的功能,移動後更新遊戲和蛇的狀態。 public boolean nextRound() { Node trail = snake.move(snakeDirection); Node snakeHead = snake.getBody().removeFirst();//將頭部暫時去掉,拿出來判斷是否身體和頭部有重合的點 if(snakeHead.getX()<=width-10 && snakeHead.getX()>=10 && snakeHead.getY()<=height-10 && snakeHead.getY()>=10 && !snake.hasNode(snakeHead)) {//判斷吃到自己和撞到邊界 if(snakeHead.equals(food)) { //原本頭部是食物的話,將move操作刪除的尾部添加回來 snake.addTail(trail); food = creatFood(); } snake.getBody().addFirst(snakeHead); return true;//更新棋盤狀態並返回遊戲是否結束的標誌 } return false; } public Node getFood() { return food; } public Snake getSnake() { return snake; } public int getWidth() { return width; } public int getHeight() { return height; } //提供一個更改貪吃蛇前進方向的方法 public void changeDirection(Direction newDirection){ snakeDirection = newDirection; } }
Snake
/* * 蛇類,實現了自身資料結構,以及移動的功能 */ public class Snake implements Cloneable{ public LinkedList<Node> body = new LinkedList<>(); public Node move(Direction direction) { //根據方向更新貪吃蛇的body //返回移動之前的尾部Node(為了吃到時候後增加尾部長度做準備) Node n;//臨時儲存新頭部移動方向的結點 switch (direction) { case UP: n = new Node(this.getHead().getX(),this.getHead().getY()-Settings.DEFAULT_NODE_SIZE); break; case DOWN: n = new Node(this.getHead().getX(),this.getHead().getY()+Settings.DEFAULT_NODE_SIZE); break; case RIGHT: n = new Node(this.getHead().getX()+Settings.DEFAULT_NODE_SIZE,this.getHead().getY()); break; default: n = new Node(this.getHead().getX()-Settings.DEFAULT_NODE_SIZE,this.getHead().getY()); } Node temp = this.body.getLast(); this.body.addFirst(n); this.body.removeLast(); return temp; } public Node getHead() { return body.getFirst(); } public Node getTail() { return body.getLast(); } public Node addTail(Node area) { this.body.addLast(area); return area; } public LinkedList<Node> getBody(){ return body; } //判斷引數結點是否在蛇身上 public boolean hasNode(Node node) { Iterator<Node> it = body.iterator(); Node n = new Node(0,0); while(it.hasNext()) { n = it.next(); if(n.getX() == node.getX() && n.getY() == node.getY()) { return true; } } return false; } }
Direction
/* * 用來控制蛇的移動方向 */ public enum Direction { UP(0),DOWN(1),LEFT(2),RIGHT(3); //呼叫構造方法對方向列舉例項進行程式碼初始化 //成員變數 private final int directionCode; //成員方法 public int directionCode() { return directionCode; } Direction(int directionCode){ this.directionCode = directionCode; } }
Node
public class Node { private int x; private int y; public Node(int x,int y) { this.x = ((int)(x/10))*10; this.y = ((int)(y/10))*10; }//使用這種方法可以使得節點座標不會出現個位數 public int getX() { return x; } public int getY() { return y; } @Override //判斷兩個Node是否相同 public boolean equals(Object n) { Node temp; if(n instanceof Node) { temp = (Node)n; if(temp.getX()==this.getX() && temp.getY()==this.getY()) return true; } return false; } }
Settings
public class Settings { public static int DEFAULT_NODE_SIZE = 10;//每一個節點方塊的單位 public static int DEFAULT_MOVE_INTERVAL = 200;//蛇移動時間間隔 }
更多有趣的經典小遊戲實現專題,分享給大家:
C++經典小遊戲彙總
python經典小遊戲彙總
python俄羅斯方塊遊戲集合
JavaScript經典遊戲 玩不停
javascript經典小遊戲彙總
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支援我們。