java實現(xiàn)貪吃蛇小游戲
本文實例為大家分享了java實現(xiàn)貪吃蛇小游戲的具體代碼,供大家參考,具體內(nèi)容如下

這是MVC模式的完整Java項目,編譯運行SnakeApp.java即可開始游戲。
可擴展功能:
1、積分功能:可以創(chuàng)建得分規(guī)則的類(模型類的一部分), 在GameController的run()方法中計算得分
2、變速功能:比如加速功能,減速功能,可以在GameController的keyPressed()方法中針對特定的按鍵設(shè)置每一次移動之間的時間間隔,將Thread.sleep(Settings.DEFAULT_MOVE_INTERVAL);替換為動態(tài)的時間間隔即可
3、更漂亮的游戲界面:修改GameView中的drawXXX方法,比如可以將食物渲染為一張圖片,Graphics有drawImage方法
View
SnakeApp.java
/*
* 屬于View,用來根據(jù)相應(yīng)的類展示出對應(yīng)的游戲主界面,也是接收控制信息的第一線。
*/
public class SnakeApp {
public void init() {
//創(chuàng)建游戲窗體
JFrame window = new JFrame("一只長不大的蛇");
//初始化500X500的棋盤,用來維持各種游戲元素的狀態(tài),游戲的主要邏輯部分
Grid grid = new Grid(50*Settings.DEFAULT_NODE_SIZE,50*Settings.DEFAULT_NODE_SIZE);
//傳入grid參數(shù),新建界面元素對象
GameView gameView = new GameView(grid);//繪制游戲元素的對象
//初始化面板
gameView.initCanvas();
//根據(jù)棋盤信息建立控制器對象
GameController gameController = new GameController(grid);
//設(shè)置窗口大小
window.setPreferredSize(new Dimension(526,548));
//往窗口中添加元素,面板對象被加入到窗口時,自動調(diào)用其中的paintComponent方法。
window.add(gameView.getCanvas(),BorderLayout.CENTER);
//畫出蛇和棋盤和食物
GameView.draw();
//注冊窗口監(jiān)聽器
window.addKeyListener((KeyListener)gameController);
//啟動線程
new Thread(gameController).start();
//窗口關(guān)閉的行為
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//設(shè)置窗口大小不可變化
window.setResizable(false);
//渲染和顯示窗口
window.pack();
window.setVisible(true);
}
//可以忽略,以后每個類中都有這么一個測試模塊
public static void main(String[] args) {
SnakeApp snakeApp = new SnakeApp();
snakeApp.init();
}
}
GameView.java
/*
* 屬于View,用于繪制地圖、蛇、食物
*/
/* Graphics 相當(dāng)于一個畫筆。對象封裝了 Java 支持的基本呈現(xiàn)操作所需的狀態(tài)信息。此狀態(tài)信息包括以下屬性:
要在其上繪制的 Component 對象。
呈現(xiàn)和剪貼坐標(biāo)的轉(zhuǎn)換原點。
當(dāng)前剪貼區(qū)。
當(dāng)前顏色。
當(dāng)前字體。
當(dāng)前邏輯像素操作函數(shù)(XOR 或 Paint)。
當(dāng)前 XOR 交替顏色
*/
/* java.awt.Component的repaint()方法
作用:更新組件。
如果此組件不是輕量級組件,則為了響應(yīng)對 repaint 的調(diào)用,AWT 調(diào)用 update 方法??梢约俣ㄎ辞宄尘?。
Component 的 update 方法調(diào)用此組件的 paint 方法來重繪此組件。為響應(yīng)對 repaint 的調(diào)用而需要其他工作的子類通常重寫此方法。重寫此方法的 Component 子類應(yīng)該調(diào)用 super.update(g),或者直接從其 update 方法中調(diào)用 paint(g)。
圖形上下文的原點,即它的(0,0)坐標(biāo)點是此組件的左上角。圖形上下文的剪貼區(qū)域是此組件的邊界矩形。
*/
public class GameView {
private final Grid grid;
private static JPanel canvas;//畫板,用于在這上面制作畫面,然后返回。
public GameView(Grid grid) {
this.grid = grid;
}
//重新繪制游戲界面元素,不斷重新調(diào)用paintComponent方法覆蓋原本的面板。
public static void draw() {
canvas.repaint();
}
//獲取畫板對象的接口
public JPanel getCanvas() {
return canvas;
}
//對畫板進行初始化
public void initCanvas() {
canvas = new JPanel() {
//指向一個方法被覆蓋的新面板子類對象
//paintComponent()繪制此容器中的每個組件,Swing會在合適的時機去調(diào)用這個方法,展示出合適的界面,這就是典型的回調(diào)(callback)的概念。
public void paintComponent(Graphics graphics) {
super.paintComponent(graphics); //這里必須調(diào)用一下父類 也就是 container的重繪方法,否則表現(xiàn)為之前的繪圖不會覆蓋
drawGridBackground(graphics);//畫出背景網(wǎng)格線
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, 0, i*Settings.DEFAULT_NODE_SIZE , this.grid.getHeight());
}
graphics.setColor(Color.red);
graphics.fillRect(0, 0, this.grid.width, Settings.DEFAULT_NODE_SIZE);
graphics.fillRect(0, 0, Settings.DEFAULT_NODE_SIZE, this.grid.height);
graphics.fillRect(this.grid.width, 0, Settings.DEFAULT_NODE_SIZE,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)
在此圖形上下文的坐標(biāo)系中,使用當(dāng)前顏色在點 (x1, y1) 和 (x2, y2) 之間畫一條線。
參數(shù):
x1 - 第一個點的 x 坐標(biāo)。
y1 - 第一個點的 y 坐標(biāo)。
x2 - 第二個點的 x 坐標(biāo)。
y2 - 第二個點的 y 坐標(biāo)。
*/
//提供直接出現(xiàn)游戲結(jié)束的選項框的功能。
public static void showGameOverMessage() {
JOptionPane.showMessageDialog(null,"游戲結(jié)束","短暫的蛇生到此結(jié)束", 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, Node squareArea, Color color) {
graphics.setColor(color);
int size = Settings.DEFAULT_NODE_SIZE;
graphics.fillOval(squareArea.getX(), squareArea.getY(), size, size);
}
}
Controller
GameController
/*
* 接收窗體SnakeApp傳遞過來的有意義的事件,然后傳遞給Grid,讓Grid即時的更新狀態(tài)。
* 同時根據(jù)最新狀態(tài)渲染出游戲界面讓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) {//如果下一步更新棋盤時,出現(xiàn)游戲結(jié)束返回值(如果flag為假)則
this.running = false;
GameView.showGameOverMessage();
System.exit(0);
}
}
@Override
/*run()函數(shù)中的核心邏輯是典型的控制器(Controller)邏輯:
修改模型(Model):調(diào)用Grid的方法使游戲進入下一步
更新視圖(View):調(diào)用GameView的方法刷新頁面*/
public void run() {
while(running) {
try {
Thread.sleep(Settings.DEFAULT_MOVE_INTERVAL);
isOver(grid.nextRound());
GameView.draw();
} catch (InterruptedException e) {
break;
}
// 進入游戲下一步
// 如果結(jié)束,則退出游戲
// 如果繼續(xù),則繪制新的游戲頁面
}
running = false;
}
@Override
public void keyTyped(KeyEvent e) {
}
@Override
public void keyReleased(KeyEvent e) {
}
}
Model
Grid
/*
* 隨機生成食物,維持貪吃蛇的狀態(tài),根據(jù)SnakeApp中的用戶交互來控制游戲狀態(tài)。
*/
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;
}
}
//棋盤上隨機制造食物的功能。
//一直循環(huán)獲取隨機值,直到三個條件都不滿足。
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;
}
//提供下一步更新棋盤的功能,移動后更新游戲和蛇的狀態(tài)。
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;//更新棋盤狀態(tài)并返回游戲是否結(jié)束的標(biāo)志
}
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
/*
* 蛇類,實現(xiàn)了自身數(shù)據(jù)結(jié)構(gòu),以及移動的功能
*/
public class Snake implements Cloneable{
public LinkedList<Node> body = new LinkedList<>();
public Node move(Direction direction) {
//根據(jù)方向更新貪吃蛇的body
//返回移動之前的尾部Node(為了吃到時候后增加尾部長度做準(zhǔn)備)
Node n;//臨時存儲新頭部移動方向的結(jié)點
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;
}
//判斷參數(shù)結(jié)點是否在蛇身上
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); //調(diào)用構(gòu)造方法對方向枚舉實例進行代碼初始化
//成員變量
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;
}//使用這種方法可以使得節(jié)點坐標(biāo)不會出現(xiàn)個位數(shù)
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;//每一個節(jié)點方塊的單位
public static int DEFAULT_MOVE_INTERVAL = 200;//蛇移動時間間隔
}
更多有趣的經(jīng)典小游戲?qū)崿F(xiàn)專題,分享給大家:
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Spring Boot集成tablesaw插件快速入門示例代碼
Tablesaw是一款Java的數(shù)據(jù)可視化庫,數(shù)據(jù)解析庫,主要用于加載數(shù)據(jù),對數(shù)據(jù)進行操作(轉(zhuǎn)化,過濾,匯總等),類比Python中的Pandas庫,本文介紹Spring Boot集成tablesaw插件快速入門Demo,感興趣的朋友一起看看吧2024-06-06
Java實現(xiàn)MapStruct對象轉(zhuǎn)換的示例代碼
本文主要介紹了MapStruct在Java中的對象轉(zhuǎn)換使用方法,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2024-12-12
Spring Boot利用Docker快速部署項目的完整步驟
這篇文章主要給大家介紹了關(guān)于Spring Boot利用Docker快速部署項目的完整步驟,文中通過示例代碼介紹的非常詳細,對大家學(xué)習(xí)或者使用Spring Boot具有一定的參考學(xué)習(xí)價值,需要的朋友們下面來一起學(xué)習(xí)學(xué)習(xí)吧2019-07-07
關(guān)于Process的waitFor死鎖問題及解決方案
這篇文章主要介紹了關(guān)于Process的waitFor死鎖問題及解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-12-12

