基于 A*搜索算法迷宫游戏开发的代码实现
一、gird类
操纵玩家角色进行上下左右的走动
class grid {
static final int InTree = 1;
static final int NotIntree = 0;
private int x = -1;//横坐标
private int y = -1;//纵坐标
private int flag = NotIntree;//是否有的标志位;
private grid father = null;
/**
* 构造函数
* @param x 初始化横坐标
* @param y 初始化纵坐标
*/
public grid(int x, int y) {
this.x=x;
this.y=y;
}
/**
* @return 方格的横坐标
*/
public int getX() {
return x;
}
/**
* @return 方格的纵坐标
*/
public int getY() {
return y;
}
/**
* @return 标志位
*/
public int getFlag() {
return flag;
}
/**
* @return 方格对象的父节点
*/
public grid getFather() {
return father;
}
/**
* 修改方格对象的父节点为f
* @param f
*/
public void setFather(grid f) {
father = f;
}
/**
* 修改标志位为f
* @param f
*/
public void setFlag(int f) {
flag = f;
}
/**
* 以字符串形式导出坐标;
* @return 坐标
*/
public String toString() {
return new String("(" + x + "," + y + ")\n");
}
}
二、Super_Marry类
游戏的主要逻辑部分,实现了游戏的控制,地图的绘制,自动寻路等等算法,是游戏最主要的部分
import java.awt.Color;//颜色相关操作的类;
import java.awt.Graphics;//用以基本计何图形的绘制;
import java.awt.event.KeyAdapter;//提供按键事件相关操作的接口
import java.awt.event.KeyEvent;//按键事件
import java.util.Random;//随机类
import java.util.Stack;//栈
import javax.swing.JOptionPane;//java的对话框类;
import javax.swing.JPanel;//Panel默认的布局管理器;
public class Super_Marry extends JPanel {
private static final long serialVersionUID = -8300339045454852626L;
private int NUM, width, padding;// NUM为界面总边长,width为每个格子的边长;padding为内边距;
private grid[][] maze;//迷宫对象
/**
* 构造方法
*
* @param num 游戏窗口边长
* @param wid 每个格子的边长
* @param pad 内边距
*/
Super_Marry(int num, int wid, int pad) {
NUM = num;
width = wid;
padding = pad;
maze = new grid[NUM][NUM];//调用Lattice的构造函数,对尾端的小方格构造;
// 对除最后一格外的每一个坐标认定为一个小方格对象并构造;
for (int i = 0; i <= NUM - 1; i++)
for (int j = 0; j <= NUM - 1; j++)
maze[i][j] = new grid(i, j); //每个在窗口内具有整数坐标的点被视为一个方格对象;
createMaze();//绘制地图
setKeyListener();//加载侦听
this.setFocusable(true);
}
private int myX, myY;//玩家位置坐标信息;
private boolean drawPath = false;//判断是否需要提示
/**
* 私有的构造方法
* 初始化各种参数,类似于restart
*/
private void init() {
for (int i = 0; i <= NUM - 1; i++)
for (int j = 0; j <= NUM - 1; j++) {
maze[i][j].setFather(null);//设置每个小方格的父节点为null;
maze[i][j].setFlag(grid.NotIntree);//设置每个方格的标志位;
}
myX = 0;
myY = 0;
drawPath = false;//路径绘制标识;
createMaze();//地图绘制
this.setFocusable(true);//使能控件获得焦点能力
repaint();//重新绘制;
}
//内边距属性;第x+1位的小方格中心点横坐标位置;
public int getCenterX(int x) {
return padding + x * width + width / 2;
}
//内边距属性;第y+1位的小方格中心点纵坐标位置;
public int getCenterY(int y) {
return padding + y * width + width / 2;
}
//得到方格对象P的第x+1位的小方格中心点横坐标位置;
public int getCenterX(grid p) {
return padding + p.getY() * width + width / 2;
}
//得到方格对象P的第x+1位的小方格中心点纵坐标位置;
public int getCenterY(grid p) {
return padding + p.getX() * width + width / 2;
}
int sum = 0;//步数;
int rand = 0;//关数;
/**
* 查看游戏是否胜利
*/
private void checkIsWin() {
//如果当前玩家位置坐标对于末尾方格的坐标;
if (myX == NUM - 1 && myY == NUM - 1) {
JOptionPane.showMessageDialog(null, "你成功的走出了迷宫 !" + "你一共走了" + sum + "步");
rand++;
sum = 0;
//返回值为0或1
int m = JOptionPane.showConfirmDialog(
null, "你已经通过" + rand + "关,是否要继续闯关?", "是否继续", JOptionPane.YES_NO_OPTION);
if (m == 0) {
init();
}
if (m == 1) {
JOptionPane.showMessageDialog(null, "本次你一共通过了 !" + rand + "关,下次继续挑战");
}
}
}
/**
* 移动
*
* @param key 当前按键信息
*/
synchronized private void move(int key) {
int tx = myX, ty = myY;
switch (key) {
//上下左右
case KeyEvent.VK_LEFT:
ty--;
sum++;
break;
case KeyEvent.VK_RIGHT:
ty++;
sum++;
break;
case KeyEvent.VK_UP:
tx--;
sum++;
break;
case KeyEvent.VK_DOWN:
tx++;
sum++;
break;
//如果是回车键
case KeyEvent.VK_ENTER:
//如果路径绘制标识符为1,就置0;
if (drawPath == true) {
drawPath = false;
} //否则就把路径标识符置1;
else {
drawPath = true;
}
break;
default:
}
//如果是要越界,则不变化
if (!isOutOfBorder(tx, ty) && (maze[tx][ty].getFather() == maze[myX][myY]
|| maze[myX][myY].getFather() == maze[tx][ty])) {
myX = tx;
myY = ty;
}
}
/**
* 按键监视器
* 兼游戏部分逻辑实现
* 移动、是否胜利
*/
private void setKeyListener() {
this.addKeyListener(new KeyAdapter() {
public void keyPressed(KeyEvent keyEvent) {//按键事件;
int key = keyEvent.getKeyCode();//获取当前按键信息;
move(key);//根据按键信息继续坐标操作;
repaint();//将操作后的信息修正展示;
checkIsWin();//检测是否赢了;
}
});
}
/**
* 判断玩家是否越界
*
* @param p 玩家
* @return true 越界、false 未越界
*/
private boolean isOutOfBorder(grid p) {//子方格对象调用的越界判断;
return isOutOfBorder(p.getX(), p.getY());
}
//坐标调用的越界判断
private boolean isOutOfBorder(int x, int y) {
return (x > NUM - 1 || y > NUM - 1 || x < 0 || y < 0) ? true : false;
}
/**
* 判断玩家的可移动方向
*
* @param p 玩家
* @return 可移动的方向
*/
private grid[] getNeis(grid p) {
final int[] adds = {-1, 0, 1, 0, -1};// 顺序为左上右下 一个数组;
//未越界不操作;
if (isOutOfBorder(p)) {
return null;
}
grid[] ps = new grid[4];// 顺序为上右下左
int xt;
int yt;
for (int i = 0; i <= 3; i++) {
xt = p.getX() + adds[i];
yt = p.getY() + adds[i + 1];
if (isOutOfBorder(xt, yt))//遍历点的左上右下四个方向,如果越界则不操作;
continue;
ps[i] = maze[xt][yt];//将可能的方向存入对象数组中;
}
return ps;//将得到的全部方向返回;
}
/**
* 随机迷宫的建立
*/
private void createMaze() {
Random random = new Random();//创建随机对象;
//生成【0,num】间的随机数做横纵坐标
int rx = Math.abs(random.nextInt()) % NUM;
int ry = Math.abs(random.nextInt()) % NUM;
//网格栈;
Stack<grid> s = new Stack<grid>();
grid p = maze[rx][ry];//由随机坐标生成随机方格对象
grid neis[] = null;//方格对象数组neis
s.push(p);//随机方格对象压栈
//如果栈非空
while (!s.isEmpty()) {
p = s.pop();
p.setFlag(grid.InTree);//将之前随机生成的点都修改标志位
neis = getNeis(p);//获取当前方格的全部可行方向,存入neis中
int ran = Math.abs(random.nextInt()) % 4;//产生【0,3】的随机整数
for (int a = 0; a <= 3; a++) {
ran++;
ran %= 4;//每次修改ran的值并保证其介于【0,4】
// 如果不存在或者已经标记,就不操作
if (neis[ran] == null || neis[ran].getFlag() == grid.InTree)
continue;
s.push(neis[ran]);//对于可用的点都入栈操作;
neis[ran].setFather(p);//如果存在,又未入树则标记其父节点
}
}
}
/**
* 清除掉树的枝干上的边框
* @param i
* @param j
* @param fx
* @param fy
* @param g 当前网格
*/
private void clearFence(int i, int j, int fx, int fy, Graphics g) {
int sx = padding + ((j > fy ? j : fy) * width); //取较大的设置
int sy = padding + ((i > fx ? i : fx) * width);
int dx = (i == fx ? sx : sx + width);
int dy = (i == fx ? sy + width : sy);
if (sx != dx) {
sx++;
dx--;
} else {
sy++;
dy--;
}
g.drawLine(sx, sy, dx, dy); //绘制栅栏边界;
}
/**
* 根据生成的地图将其绘制在屏幕上
* @param g 要绘制的玩家对象
*/
protected void paintComponent(Graphics g) {
super.paintComponent(g);//super 调用父类的方法;
//需要两个坐标(x1,y1)和(x2,y2)绘制全局格框(注意空出出入口);
for (int i = 0; i <= NUM; i++) {
g.drawLine(padding + i * width, padding, padding + i * width, padding + NUM * width);
}
for (int j = 0; j <= NUM; j++) {
g.drawLine(padding, padding + j * width, padding + NUM * width, padding + j * width);
}
g.setColor(this.getBackground());
for (int i = NUM - 1; i >= 0; i--) {
for (int j = NUM - 1; j >= 0; j--) {
grid f = maze[i][j].getFather();//取出个方格对象的父节点;
if (f != null) {
int fx = f.getX(), fy = f.getY(); //如果父节点存在(父节点其实就是一个对象)获取父节点的坐标;
clearFence(i, j, fx, fy, g);//清除掉树枝干上的边框;
}
}
}
/**
* 绘制玩家
*/
g.drawLine(padding, padding + 1, padding, padding + width - 1);
int last = padding + NUM * width;
g.drawLine(last, last - 1, last, last - width + 1); //补充绘制外边框;
g.setColor(Color.red);
g.fillOval(getCenterX(myY) - width / 3, getCenterY(myX) - width / 3, width / 2, width / 2);
//填充我们需要操作的小圆球的对象;Graphics中用当前颜色填充以指定矩形为边界的椭圆的方法;
if (drawPath == true)
drawPath(g);
}
/**
* 绘制迷宫的通道(即提示)
* @param path
*/
private void drawPath(Graphics path) {
//完整路径寻路;逆二叉树搜索;
if (drawPath == true) {
path.setColor(Color.blue);
}
//不需要答案提示情况下显示背景颜色(白色)
else {
path.setColor(this.getBackground());
}
//游戏胜利的最后一个点,即右下角那个点
grid winPoint = maze[NUM - 1][NUM - 1];
//当存在父节点时
while (winPoint.getFather() != null) {
winPoint.setFlag(2);//将节点标志位置2;
winPoint = winPoint.getFather(); //再将当前节点更迭至其父节点处
}
//经过一遍遍历,从末尾格局父节点唯一原则,生成了一条从末尾遍历至开头的通路,路上全标记为2;
//将点设为首位
winPoint = maze[0][0];
//存在父节点时;
while (winPoint.getFather() != null) {
if (winPoint.getFlag() == 2) {//如果对象标志位为2
winPoint.setFlag(3); //对象标志位置为3;
path.setColor(Color.blue);
}
winPoint = winPoint.getFather();//将p点对象更迭为其父类对象位置;
}
path.setColor(Color.blue);//绘制颜色(颜色覆盖)
winPoint = maze[NUM - 1][NUM - 1];
while (winPoint.getFather() != null) { //该点对象存在父节点;
if (winPoint.getFlag() == 3)
break;
path.drawLine(getCenterX(winPoint), getCenterY(winPoint), getCenterX(winPoint.getFather()), getCenterY(winPoint.getFather()));
winPoint = winPoint.getFather();
}
}
}
三、Main类
主方法,游戏窗口的创建本来要重新创建一个类的,但仅仅几行代码,我为了省事,就放在主方法里了
import javax.swing.*;
class Main {
public static void main(String[] args) {
final int num = 10, width = 400, padding = 20;
final int LX = 400, LY = 200; // n为边界的方格数目,width设置窗口边长;
JPanel marry = new Super_Marry(num, (width - padding - padding) / num, padding);
JFrame frame = new JFrame("迷宫游戏(按回车键提示获取提示)");
frame.getContentPane().add(marry);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//设置窗口1点×可关闭;
frame.setSize(width + padding, width + padding + padding);//设置窗口大小;
frame.setLocation(LX, LY);//设置窗口位置;
frame.setVisible(true);//通过 setVisible()并设置参数为true,把内存中的窗口显示在屏幕上;
}
}