项目三:基于 A*搜索算法迷宫游戏开发

基于 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,把内存中的窗口显示在屏幕上;
    }
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值