效果图
数据层
本实例需要从 .txt 文件中读取迷宫并绘制,所以先来实现文件读取IO类 MazeData.java,该程序在构造函数运行时将外部文件读入,并完成迷宫各种参数的初始化,注意规定了外部 .txt 文件的第一行两个数字分别代表迷宫的行数和列数。此外还提供了各类接口来读取或操作私有数据。
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; // 存储迷宫的解 public boolean showPath; // 是否打印系统提示的开关 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; // 读取后续的N行 for (int i = 0; i < N; i ++){ String line = scanner.nextLine(); // 每行保证有M个字符 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; // 出口,倒数第二行最后一列 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]; } // 判断点(x,y)是否在迷宫中 public boolean inArea(int x, int y){ return x >= 0 && x < N && y >= 0 && y < M; } // 控制台打印迷宫 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; } }
将迷宫的各个位置封装成一个类 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来调用画板的 paintComponent 方法实现绘制,其中需要用到自己定义的绘制辅助类 AlgoVisHelper.java,在里面封装了绘制矩形,设置画笔颜色,停顿等方法,也定义了一些颜色,也可以不用定义该辅助类而直接在 AlgoFrame.java 中使用awt包中的各种方法直接实现,如有需要可自行下载代码。
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); } } }
控制层
主函数 AlgoVisualizer.java ,其中在程序运行最开始时采用了基于递归的DFS算法将迷宫的解事先求出,用户按下空格则可以实现提示功能,红色表示玩家,键盘上下左右控制四个方向的移动。run()方法实现了所有的动画逻辑
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}}; // 四个方向移动 public AlgoVisualizer(String mazeFile){ // 初始化数据 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()); // 递归实现 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("游戏结束"); 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); } }
到此这篇关于Java实现可视化走迷宫小游戏的示例代码的文章就介绍到这了,更多相关Java走迷宫游戏内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!