Java實(shí)現(xiàn)可視化走迷宮小游戲的示例代碼
效果圖
數(shù)據(jù)層
本實(shí)例需要從 .txt 文件中讀取迷宮并繪制,所以先來實(shí)現(xiàn)文件讀取IO類 MazeData.java,該程序在構(gòu)造函數(shù)運(yùn)行時(shí)將外部文件讀入,并完成迷宮各種參數(shù)的初始化,注意規(guī)定了外部 .txt 文件的第一行兩個(gè)數(shù)字分別代表迷宮的行數(shù)和列數(shù)。此外還提供了各類接口來讀取或操作私有數(shù)據(jù)。
import java.io.BufferedInputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.util.Scanner; public class MazeData { public static final char ROAD = ' '; public static final char WALL = '#'; private int N, M; // 高,寬(行,列) private char[][] maze; private int entranceX, entranceY; // 入口 private int exitX, exitY; // 出口 public boolean[][] visited; // 記錄尋路過程某位置是否被訪問過 public boolean[][] path; // 存儲(chǔ)迷宮的解 public boolean showPath; // 是否打印系統(tǒng)提示的開關(guān) public Position player; // 玩家所處位置 public MazeData(String filename){ if (filename == null) throw new IllegalArgumentException("Filename can not be null!"); Scanner scanner = null; try { File file = new File(filename); if (!file.exists()) throw new IllegalArgumentException("File " + filename + " doesn't exist"); FileInputStream fis = new FileInputStream(file); scanner = new Scanner(new BufferedInputStream(fis), "UTF-8"); // 讀取第一行 String nmline = scanner.nextLine(); String[] nm = nmline.trim().split("\\s+"); // 正則 匹配任意空白字符 N = Integer.parseInt(nm[0]); M = Integer.parseInt(nm[1]); maze = new char[N][M]; visited = new boolean[N][M]; path = new boolean[N][M]; showPath = false; // 讀取后續(xù)的N行 for (int i = 0; i < N; i ++){ String line = scanner.nextLine(); // 每行保證有M個(gè)字符 if(line.length() != M) throw new IllegalArgumentException("Maze file " + filename + " is invalid"); for (int j = 0; j < M; j ++) { maze[i][j] = line.charAt(j); visited[i][j] = false; path[i][j] = false; } } } catch (IOException e){ e.printStackTrace(); } finally { if (scanner != null) scanner.close(); } // 入口,第二行第一列 entranceX = 1; entranceY = 0; // 出口,倒數(shù)第二行最后一列 exitX = N - 2; exitY = M - 1; } public int N(){ return N; } public int M(){ return M; } public int getEntranceX(){return entranceX;} public int getEntranceY(){return entranceY;} public int getExitX(){return exitX;} public int getExitY(){return exitY;} public char getMaze(int i, int j){ if (!inArea(i, j)) throw new IllegalArgumentException("i or j is out of index in getMaze!"); return maze[i][j]; } // 判斷點(diǎn)(x,y)是否在迷宮中 public boolean inArea(int x, int y){ return x >= 0 && x < N && y >= 0 && y < M; } // 控制臺(tái)打印迷宮 public void print(){ System.out.println(N + " " + M); for(int i = 0 ; i < N ; i ++){ for(int j = 0 ; j < M ; j ++) System.out.print(maze[i][j]); System.out.println(); } return; } }
將迷宮的各個(gè)位置封裝成一個(gè)類 Position.java,便于操作
public class Position { private int x, y; public Position(int x, int y, Position prev){ this.x = x; this.y = y; } public Position(int x, int y){ this(x, y, null); } public int getX(){ return x; } public int getY(){ return y; } public void setX(int x){ this.x = x; } public void setY(int y){ this.y = y; } }
視圖層
AlgoFrame.java 是繪制界面的核心代碼,使用java的JFrame控件,在上面添加JPanel畫板,在JFrame中定義渲染方法render來調(diào)用畫板的 paintComponent 方法實(shí)現(xiàn)繪制,其中需要用到自己定義的繪制輔助類 AlgoVisHelper.java,在里面封裝了繪制矩形,設(shè)置畫筆顏色,停頓等方法,也定義了一些顏色,也可以不用定義該輔助類而直接在 AlgoFrame.java 中使用awt包中的各種方法直接實(shí)現(xiàn),如有需要可自行下載代碼。
import java.awt.*; import javax.swing.*; public class AlgoFrame extends JFrame{ private int canvasWidth; private int canvasHeight; public AlgoFrame(String title, int canvasWidth, int canvasHeight){ super(title); this.canvasWidth = canvasWidth; this.canvasHeight = canvasHeight; AlgoCanvas canvas = new AlgoCanvas(); setContentPane(canvas); pack(); setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); setResizable(false); setVisible(true); } public AlgoFrame(String title){ this(title, 1024, 768); } public int getCanvasWidth(){return canvasWidth;} public int getCanvasHeight(){return canvasHeight;} private MazeData data; public void render(MazeData data){ this.data = data; repaint(); } private class AlgoCanvas extends JPanel{ public AlgoCanvas(){ // 雙緩存 super(true); } @Override public void paintComponent(Graphics g) { super.paintComponent(g); Graphics2D g2d = (Graphics2D)g; // 抗鋸齒 RenderingHints hints = new RenderingHints( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); hints.put(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); g2d.addRenderingHints(hints); // 具體繪制 int w = canvasWidth / data.M(); // 寬 int h = canvasHeight / data.N();// 高 for (int i = 0; i < data.N(); i ++){ for (int j = 0; j < data.M(); j ++){ if (data.getMaze(i,j) == MazeData.WALL) AlgoVisHelper.setColor(g2d, AlgoVisHelper.LightBlue); else AlgoVisHelper.setColor(g2d, AlgoVisHelper.White); if (data.path[i][j] && data.showPath == true) AlgoVisHelper.setColor(g2d, AlgoVisHelper.Yellow); if (data.player.getX() == i && data.player.getY() == j) AlgoVisHelper.setColor(g2d, AlgoVisHelper.Red); AlgoVisHelper.fillRectangle(g2d, j*w, i*h, w, h); } } } @Override public Dimension getPreferredSize(){ return new Dimension(canvasWidth, canvasHeight); } } }
控制層
主函數(shù) AlgoVisualizer.java ,其中在程序運(yùn)行最開始時(shí)采用了基于遞歸的DFS算法將迷宮的解事先求出,用戶按下空格則可以實(shí)現(xiàn)提示功能,紅色表示玩家,鍵盤上下左右控制四個(gè)方向的移動(dòng)。run()方法實(shí)現(xiàn)了所有的動(dòng)畫邏輯
import java.awt.*; import java.awt.event.KeyAdapter; import java.awt.event.KeyEvent; public class AlgoVisualizer { private static int DELAY = 10; private static int blockSide = 8; private MazeData data; private AlgoFrame frame; private static final int d[][] = {{-1,0},{0,1},{1,0},{0,-1}}; // 四個(gè)方向移動(dòng) public AlgoVisualizer(String mazeFile){ // 初始化數(shù)據(jù) data = new MazeData(mazeFile); int sceneHeight = data.N() * blockSide; int sceneWidth = data.M() * blockSide; // 初始化視圖 EventQueue.invokeLater(() -> { frame = new AlgoFrame("Maze Solver Visualization", sceneWidth, sceneHeight); frame.addKeyListener(new AlgoKeyListener()); new Thread(() -> { run(); }).start(); }); } public void run(){ setData(-1, -1, false); data.player = new Position(data.getEntranceX(), data.getEntranceY()); // 遞歸實(shí)現(xiàn) if(!autoGo(data.getEntranceX(), data.getEntranceY())) System.out.println("The maze has NO solution!"); System.out.println("初始化已完成"); while (true){ frame.render(data); AlgoVisHelper.pause(DELAY); setData(-1, -1, false); if (data.player.getX() == data.getExitX() && data.player.getY() == data.getExitY()){ System.out.println("游戲結(jié)束"); frame.render(data); AlgoVisHelper.pause(DELAY); break; } } setData(-1, -1, false); } // 返回值:求解是否成功 private boolean autoGo(int x, int y){ if(!data.inArea(x,y)) throw new IllegalArgumentException("x,y are out of index in go function!"); data.visited[x][y] = true; setData(x, y, true); if (x == data.getExitX() && y == data.getExitY()) return true; for (int i = 0; i < 4; i ++){ int newX = x + d[i][0]; int newY = y + d[i][1]; if (data.inArea(newX, newY) && data.getMaze(newX, newY) == MazeData.ROAD && !data.visited[newX][newY]){ if (autoGo(newX, newY)) return true; } } setData(x, y, false); return false; } private void setData(int x, int y, boolean isPath){ if (data.inArea(x, y)) data.path[x][y] = isPath; } private class AlgoKeyListener extends KeyAdapter{ @Override public void keyPressed(KeyEvent event){ if (event.getKeyCode() == KeyEvent.VK_LEFT){ System.out.println("go left"); oneStep(data.player.getX(), data.player.getY(), 3); } else if (event.getKeyCode() == KeyEvent.VK_DOWN){ System.out.println("go down"); oneStep(data.player.getX(), data.player.getY(), 2); } else if (event.getKeyCode() == KeyEvent.VK_RIGHT){ System.out.println("go right"); oneStep(data.player.getX(), data.player.getY(), 1); } else if (event.getKeyCode() == KeyEvent.VK_UP){ System.out.println("go up"); oneStep(data.player.getX(), data.player.getY(), 0); } else if (event.getKeyChar() == ' '){ System.out.println("顯示提示"); data.showPath = !data.showPath; } } } private void oneStep(int x, int y, int direction){ int newX = x + d[direction][0]; int newY = y + d[direction][1]; if (data.inArea(newX, newY) && data.getMaze(newX, newY) == MazeData.ROAD){ data.player.setX(newX); data.player.setY(newY); } } public static void main(String[] args) { String mazefile = "maze_101_101.txt"; AlgoVisualizer vis = new AlgoVisualizer(mazefile); } }
到此這篇關(guān)于Java實(shí)現(xiàn)可視化走迷宮小游戲的示例代碼的文章就介紹到這了,更多相關(guān)Java走迷宮游戲內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Spring Boot環(huán)境屬性占位符解析及類型轉(zhuǎn)換詳解
這篇文章主要給大家介紹了關(guān)于Spring Boot環(huán)境屬性占位符解析及類型轉(zhuǎn)換的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2018-08-08Spring?Security實(shí)現(xiàn)接口放通的方法詳解
在用Spring?Security項(xiàng)目開發(fā)中,有時(shí)候需要放通某一個(gè)接口時(shí),我們需要在配置中把接口地址配置上,這樣做有時(shí)候顯得麻煩。本文將通過一個(gè)注解的方式快速實(shí)現(xiàn)接口放通,感興趣的可以了解一下2022-05-05如何使用SpringBoot進(jìn)行優(yōu)雅的數(shù)據(jù)驗(yàn)證
這篇文章主要介紹了如何使用SpringBoot進(jìn)行優(yōu)雅的數(shù)據(jù)驗(yàn)證,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-11-11淺談Java設(shè)計(jì)模式之七大設(shè)計(jì)原則
在此之前,我已經(jīng)寫過很多篇關(guān)于設(shè)計(jì)模式的文章.但都比較草草的理解和簡(jiǎn)單的實(shí)現(xiàn),并未深入理解.為了更加深入感受Java設(shè)計(jì)的魅力,編程的藝術(shù),今天進(jìn)行了七大設(shè)計(jì)原則的學(xué)習(xí)理解,后續(xù)進(jìn)行23種設(shè)計(jì)模式的深入學(xué)習(xí)探究,需要的朋友可以參考下2021-05-05