前言
人类建造迷宫已有5000年的历史。在世界的不同文化发展时期,这些奇特的建筑物始终吸引人们沿着弯弯曲曲、困难重重的小路吃力地行走,寻找真相。迷宫类小游戏应运而生。在游戏中,迷宫被表现为冒险舞台里,藏有各式各样奇妙与谜题或宝藏的危险区域。型态有洞窟、人工建筑物、怪物巢穴、密林或山路等。迷宫内有恶徒或凶猛的生物(真实存在或想像物体都有)徘徊,其中可能会有陷阱、不明设施、遗迹等。
《复杂迷宫》游戏是用java语言实现,采用了swing技术进行了界面化处理,设计思路用了面向对象思想。
主要需求
方向键控制移动,角色走出迷宫,游戏胜利。增加游戏难度和增加随机地图。
主要设计
1、构建游戏地图面板
2、设定迷宫地图,包含可走的通道,不可走的墙体,还有出口位置
3、键盘的上下左右按键,来控制角色的移动
4、角色移动的算法,通道可走,遇到墙体不可走
5、走到终点,有成功通关的提示。
6、增加游戏的难度选择,难度1,难度2和难度3
7、每次生成的地图是随机的
8、地图大小可选择,迷宫的长在10-45之间,宽在10-90之间
9、增加撞墙的音乐效果
功能截图
游戏开始页面
生成难度1,10*10的迷宫地图
随机地图:生成难度1,10*10的迷宫地图
生成难度2,30*30的迷宫地图
生成难度3,90*45的迷宫地图
成功过关-效果
代码实现
窗口布局
public class StartView extends JFrame {
public StartView() {
this.setTitle("复杂迷宫");
this.setSize(240, 265);
this.setLocationRelativeTo(null);
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
this.setResizable(false);
initialize();
this.setVisible(true);
}
private void initialize() {
JPanel contentPane = new JPanel();
this.setContentPane(contentPane);
contentPane.setLayout(null);
JLabel widthLabel = new JLabel("迷宫长度:");
JLabel heightLabel = new JLabel("迷宫高度:");
JLabel levelLabel = new JLabel("难度:");
JTextField widthText = new JTextField();
JTextField heightText = new JTextField();
JRadioButton level1 = new JRadioButton("1");
JRadioButton level2 = new JRadioButton("2");
JRadioButton level3 = new JRadioButton("3");
ButtonGroup levelGroup = new ButtonGroup();
levelGroup.add(level1);
levelGroup.add(level2);
levelGroup.add(level3);
JButton run = new JButton("生成迷宫");
widthLabel.setBounds(20, 20, 100, 30);
heightLabel.setBounds(20, 70, 110, 30);
widthText.setBounds(120, 20, 70, 30);
heightText.setBounds(120, 70, 70, 30);
levelLabel.setBounds(20, 120, 60, 30);
level1.setBounds(80, 120, 50, 30);
level2.setBounds(130, 120, 50, 30);
level3.setBounds(180, 120, 50, 30);
run.setBounds(55, 170, 120, 30);
widthText.setDocument(new NumberTextField());
heightText.setDocument(new NumberTextField());
Font font = new Font("楷体", Font.PLAIN, 17);
widthLabel.setFont(font);
heightLabel.setFont(font);
widthText.setFont(font);
heightText.setFont(font);
levelLabel.setFont(font);
level1.setFont(font);
level2.setFont(font);
level3.setFont(font);
run.setFont(font);
level1.setFocusPainted(false);
level2.setFocusPainted(false);
level3.setFocusPainted(false);
level3.setSelected(true);
contentPane.add(widthLabel);
contentPane.add(heightLabel);
contentPane.add(widthText);
contentPane.add(heightText);
contentPane.add(levelLabel);
contentPane.add(level1);
contentPane.add(level2);
contentPane.add(level3);
contentPane.add(run);
run.addActionListener(e -> {
if (widthText.getText().equals("")) {
JOptionPane.showMessageDialog(null, "长度不能为空!", "提示", JOptionPane.INFORMATION_MESSAGE);
} else if (heightText.getText().equals("")) {
JOptionPane.showMessageDialog(null, "高度不能为空!", "提示", JOptionPane.INFORMATION_MESSAGE);
} else {
int width = Integer.parseInt(widthText.getText());
int height = Integer.parseInt(heightText.getText());
if (width >= 10 && width <= 90 && height >= 10 && height <= 45) {
int level = level1.isSelected() ? 1 : level2.isSelected() ? 2 : 3;
MazeModel maze = new MazeModel(width, height, level);
this.dispose();
maze.draw();
} else {
JOptionPane.showMessageDialog(null, "迷宫的长必须在10-45之间,宽必须在10-90之间,请检查输入是否有误!", "错误输入", JOptionPane.ERROR_MESSAGE);
}
}
});
KeyAdapter enterAdapter = new KeyAdapter() {
@Override
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_ENTER) {
run.doClick();
}
}
};
widthText.addKeyListener(enterAdapter);
heightText.addKeyListener(enterAdapter);
}
public static void main(String[] args) {
new StartView();
}
}
迷宫的数学模型
public class MazeModel {
private int width;
private int height;
private ArrayList<MazePoint> mazePoints;
public MazeModel(int width, int height, int level) {
super();
this.width = width;
this.height = height;
switch (level) {
case 1 : this.mazePoints = recursiveDivision();
case 2 : this.mazePoints = recursiveBacktracker();
case 3 : this.mazePoints = prim();
}
}
private ArrayList<MazePoint> recursiveBacktracker() {
ArrayList<MazePoint> maze = new ArrayList<>();
for (int h = 0; h < height; h++) {
for (int w = 0; w < width; w++) {
MazePoint point = new MazePoint(w, h, true);
maze.add(point);
}
}
Stack<MazePoint> stack = new Stack<>();
stack.push(maze.get(0));
maze.get(0).visited = true;
Random random = new Random();
int x;
int y;
int direction;
while (!stack.empty()) {
MazePoint operatingPoint = stack.peek();
x = operatingPoint.getX();
y = operatingPoint.getY();
direction = random.nextInt(4);
MazePoint adjacency;
switch (direction) {
case 0:
if ((x - 1) >= 0) {
adjacency = maze.get(x - 1 + y * width);
if (!adjacency.visited) {
operatingPoint.setLeft(0);
adjacency.setRight(0);
stack.push(adjacency);
adjacency.visited = true;
x--;
}
}
break;
case 1:
if ((x + 1) < width) {
adjacency = maze.get(x + 1 + y * width);
if (!adjacency.visited) {
operatingPoint.setRight(0);
adjacency.setLeft(0);
stack.push(adjacency);
adjacency.visited = true;
x++;
}
}
break;
case 2:
if ((y - 1) >= 0) {
adjacency = maze.get(x + (y - 1) * width);
if (!adjacency.visited) {
operatingPoint.setUp(0);
adjacency.setDown(0);
stack.push(adjacency);
adjacency.visited = true;
y--;
}
}
break;
case 3:
if ((y + 1) < height) {
adjacency = maze.get(x + (y + 1) * width);
if (!adjacency.visited) {
operatingPoint.setDown(0);
adjacency.setUp(0);
stack.push(adjacency);
adjacency.visited = true;
y++;
}
}
break;
}
if ((x - 1 < 0 || maze.get(x - 1 + y * width).visited)
&& (x + 1 >= width || maze.get(x + 1 + y * width).visited)
&& (y - 1 < 0 || maze.get(x + (y - 1) * width).visited)
&& (y + 1 >= height || maze.get(x + (y + 1) * width).visited)) {
stack.pop();
}
}
maze.get(0).setLeft(0);
maze.get(width * height - 1).setRight(0);
return maze;
}
private void divide(ArrayList<MazePoint> maze, int left, int right, int top, int down) {
if (right - left > 0 && top - down > 0) {
for (int x = left, y = (top - down) / 2 + down; x <= right; x++) {
maze.get(x + y * this.width).setDown(1);
maze.get(x + (y + 1) * this.width).setUp(1);
}
for (int x = (right - left) / 2 + left, y = down; y <= top; y++) {
maze.get(x + y * this.width).setRight(1);
maze.get(x + 1 + y * this.width).setLeft(1);
}
Random random = new Random();
int direction = random.nextInt(4);
int x = (right - left) / 2 + left;
int y = (top - down) / 2 + down;
int tempX;
int tempY;
if (direction != 0) {
if (x - left > left) {
tempX = random.nextInt(x - left + 1) + left;
} else {
tempX = left;
}
tempY = y;
maze.get(tempX + tempY * this.width).setDown(0);
maze.get(tempX + (tempY + 1) * this.width).setUp(0);
}
if (direction != 1) {
if (right - (x + 1) > x + 1) {
tempX = random.nextInt(right - (x + 1) + 1) + x + 1;
} else {
tempX = x + 1;
}
tempY = y;
maze.get(tempX + tempY * this.width).setDown(0);
maze.get(tempX + (tempY + 1) * this.width).setUp(0);
}
if (direction != 2) {
tempX = x;
if (y - down > down) {
tempY = random.nextInt(y - down + 1) + down;
} else {
tempY = down;
}
maze.get(tempX + tempY * this.width).setRight(0);
maze.get(tempX + 1 + tempY * this.width).setLeft(0);
}
if (direction != 3) {
tempX = x;
if (top - (y + 1) > y + 1) {
tempY = random.nextInt(top - (y + 1) + 1) + y + 1;
} else {
tempY = y + 1;
}
maze.get(tempX + tempY * this.width).setRight(0);
maze.get(tempX + 1 + tempY * this.width).setLeft(0);
}
maze.stream().limit(this.width).forEach(m -> m.setUp(1));
maze.stream().skip((this.height - 1) * this.width).forEach(m -> m.setDown(1));
maze.stream().filter(m -> m.getX() == 0).forEach(m -> m.setLeft(1));
maze.stream().filter(m -> m.getX() == width - 1).forEach(m -> m.setRight(1));
divide(maze, left, (right - left) / 2 + left, (top - down) / 2 + down, down);
divide(maze, left, (right - left) / 2 + left, top, (top - down) / 2 + down + 1);
divide(maze, (right - left) / 2 + left + 1, right, (top - down) / 2 + down, down);
divide(maze, (right - left) / 2 + left + 1, right, top, (top - down) / 2 + down + 1);
}
}
private ArrayList<MazePoint> recursiveDivision() {
ArrayList<MazePoint> maze = new ArrayList<>();
for (int h = 0; h < height; h++) {
for (int w = 0; w < width; w++) {
MazePoint point = new MazePoint(w, h);
maze.add(point);
}
}
divide(maze, 0, width - 1, height - 1, 0);
maze.get(0).setLeft(0);
maze.get(width * height - 1).setRight(0);
return maze;
}
private ArrayList<MazePoint> prim() {
ArrayList<MazePoint> mazePoints = new ArrayList<>();
PrimMaze primMaze = new PrimMaze(width * 2 + 1, height * 2 + 1);
int[][] tempMaze = primMaze.getMaze();
for (int i = 0; i < tempMaze.length; i++) {
for (int j = 0; j < tempMaze[i].length; j++) {
if (i % 2 != 0 && j % 2 != 0) {
MazePoint mazePoint = new MazePoint(i / 2, j / 2);
if (tempMaze[i - 1][j] == 10) {
mazePoint.setLeft(1);
}
if (tempMaze[i + 1][j] == 10) {
mazePoint.setRight(1);
}
if (tempMaze[i][j - 1] == 11) {
mazePoint.setUp(1);
}
if (tempMaze[i][j + 1] == 11) {
mazePoint.setDown(1);
}
mazePoints.add(mazePoint);
}
}
}
mazePoints.get(0).setLeft(0);
mazePoints.get(width * height - 1).setRight(0);
return mazePoints;
}
public void draw() {
new PlayView(mazePoints);
}
}
普里姆算法
class PrimMaze {
private int[][] maze;
public int[][] getMaze() {
return maze;
}
PrimMaze(int row, int column) {
int row1 = row / 2;
int column1 = column / 2;
maze = new int[row1 * 2 + 1][column1 * 2 + 1];
for (int x = 0; x < row1 * 2 + 1; x++)
{
for (int y = 0; y < column1 * 2 + 1; y++) {
if (x == 0 || x == row1 * 2) {
maze[x][y] = -1;
}
if (y == 0 || y == column1 * 2) {
maze[x][y] = -1;
}
}
}
for (int x = 1; x < row1 * 2; x++) {
for (int y = 1; y < column1 * 2; y++) {
if (x % 2 == 1 || y % 2 == 1) {
maze[x][y] = 0;
}
if (x % 2 == 0 || y % 2 == 0) {
maze[x][y] = 1;
}
}
}
ArrayList<int[]> list = new ArrayList<>();
int[] coordinate = new int[2];
int x = 1, y = 1;
coordinate[0] = coordinate[1] = 1;
list.add(coordinate);
while (list.size() < row1 * column1)
{
boolean flag1;
int[] record = {-1, -1, -1, -1};
if (x - 2 > 0)
{
int[] a = new int[2];
a[0] = x - 2;
a[1] = y;
flag1 = judge(a, list);
if (flag1) {
record[0] = 0;
}
}
if (x + 2 < row1 * 2)
{
int[] a = new int[2];
a[0] = x + 2;
a[1] = y;
flag1 = judge(a, list);
if (flag1) {
record[1] = 1;
}
}
if (y - 2 > 0)
{
int[] a = new int[2];
a[0] = x;
a[1] = y - 2;
flag1 = judge(a, list);
if (flag1) {
record[2] = 2;
}
}
if (y + 2 < column1 * 2)
{
int[] a = new int[2];
a[0] = x;
a[1] = y + 2;
flag1 = judge(a, list);
if (flag1) {
record[3] = 3;
}
}
boolean flag2 = false;
for (int i = 0; i < 4; i++)
{
if (record[i] == i) {
flag2 = true;
break;
}
}
int r = new Random().nextInt(4);
while (record[r] == r) {
r = new Random().nextInt(4);
}
while (record[r] != r && flag2)
{
r = new Random().nextInt(4);
if (record[r] == r)
{
if (r == 0) {
maze[x - 1][y] = 0;
}
if (r == 1) {
maze[x + 1][y] = 0;
}
if (r == 2) {
maze[x][y - 1] = 0;
}
if (r == 3) {
maze[x][y + 1] = 0;
}
}
}
if (r == 0 && flag2)
{
int[] b = new int[2];
b[0] = x - 2;
b[1] = y;
if (judge(b, list)) {
list.add(b);
}
}
if (r == 1 && flag2)
{
int[] b = new int[2];
b[0] = x + 2;
b[1] = y;
if (judge(b, list)) {
list.add(b);
}
}
if (r == 2 && flag2)
{
int[] b = new int[2];
b[0] = x;
b[1] = y - 2;
if (judge(b, list)) {
list.add(b);
}
}
if (r == 3 && flag2)
{
int[] b = new int[2];
b[0] = x;
b[1] = y + 2;
if (judge(b, list)) {
list.add(b);
}
}
int i = new Random().nextInt(list.size());
x = list.get(i)[0];
y = list.get(i)[1];
}
for (int r = 0; r < maze.length; r++)
{
for (int c = 0; c < maze[r].length; c++) {
if (r % 2 == 0 && c % 2 == 1) {
if (maze[r][c] != 0) {
maze[r][c] = 10;
}
}
if (r % 2 == 1 && c % 2 == 0) {
if (maze[r][c] != 0) {
maze[r][c] = 11;
}
}
}
}
}
boolean judge(int[] coordinate, ArrayList<int[]> list)
{
boolean flag = true;
for (int[] ints : list) {
if (coordinate[0] == ints[0] && coordinate[1] == ints[1])
{
flag = false;
break;
}
}
return flag;
}
}
总结
通过此次的《复杂迷宫》游戏实现,让我对swing的相关知识有了进一步的了解,对java这门语言也有了比以前更深刻的认识。
java的一些基本语法,比如数据类型、运算符、程序流程控制和数组等,理解更加透彻。java最核心的核心就是面向对象思想,对于这一个概念,终于悟到了一些。
源码获取
源码下载地址:传送门------->
可关注博主后,私聊博主免费获取
今天是持续写作的第 6 / 100 天。 可以关注我,点赞我、评论我、收藏我啦。
|