【Unity】A*算法搜索过程可视化

来源:互联网 发布:成功的网络促销 编辑:程序博客网 时间:2024/06/16 14:24

前言

A*算法是常用的游戏算法之一,也是初学者比较难掌握的一个算法。

本文实现的是在Unity引擎中,以可视化的方式形象表现A*算法搜索的具体过程步骤,

包括地图的搜索路径产生过程、FGH的计算以及开启关闭列表的变化等。


博文首发地址:http://blog.csdn.net/duzixi


一、GUI可视化版本

以下脚本为GUI可视化版本,将其添加给新场景的主摄像机,运行即可。

/// <summary>/// A*算法 Unity GUI实现/// Created by 杜子兮(duzixi.com) 2015.2.19/// Edited by 马帅 2015.5.5/// www.lanou3g.com All Rights Reserved/// </summary>using UnityEngine;using System.Collections;using System; // 用到排序接口// 枚举:定义格子类型public enum GridType{Normal,    // 常规Obstacle,  // 障碍Start,     // 起点End        // 终点}// 定义格子类(继承可比较接口 IComparable)public class Grid : IComparable{public int x;// x 坐标public int y;       // y 坐标public int F;       // 总评分public int G;       // 从起点到当前点的消耗值public int H;       // 从当前点到终点的估算值(直走10,斜走14)public GridType gridType;  // 格子类型public Grid fatherNode;// 可比较接口的实现(用于排序)public int CompareTo (object obj){   Grid g1 = (Grid)obj; // 强制类型转换if (this.F < g1.F)    // 升序return -1;  if (this.F > g1.F)    // 降序return 1;  return 0;             // 相等}}// A*算法public class AStar : MonoBehaviour{private const int col = 7;          // 列数private const int row = 5;          // 行数private int size = 70;              // 大小private Grid[,] map;                // 地图(格子二维数组)private const int xStart = 1;       private const int yStart = 1;       private const int xEnd = 2;         private const int yEnd = 6;        ArrayList openList;                 // 开启列表(重要!!)ArrayList closeList;                // 关闭列表(重要!!)// 初始化void Start (){map = new Grid[row, col];       // 创建地图for (int i = 0; i < row; i++) {for (int j = 0; j < col; j++) {map [i, j] = new Grid ();  // 实例化格子map [i, j].x = i;// x坐标赋值map [i, j].y = j;// y坐标赋值}}map [xStart, yStart].gridType = GridType.Start;  // 确定开始位置map [xStart, yStart].H = Manhattan (xEnd, yEnd); // 初始化开始位置的H值map [xEnd, yEnd].gridType = GridType.End;     // 确定结束位置for (int i = 1; i <= 3; i++) {             // 确定障碍位置map [i, 3].gridType = GridType.Obstacle;}openList = new ArrayList ();             // 初始化开启列表openList.Add (map [xStart, yStart]);       // 将开始节点放入开放列表中closeList = new ArrayList ();                 // 初始化关闭列表}void OnGUI (){// 绘制地图for (int i = 0; i < row; i++) {for (int j = 0; j < col; j++) {// 根据格子类型设置背景颜色Color bgColor;if (map [i, j].gridType == GridType.Start) {bgColor = Color.green;} else if (map [i, j].gridType == GridType.End) {bgColor = Color.red;} else if (map [i, j].gridType == GridType.Obstacle) {bgColor = Color.blue;} else if (closeList.Contains (map [i, j])) {bgColor = Color.black;} else {bgColor = Color.gray;}GUI.backgroundColor = bgColor;// 用按钮表示格子GUI.Button (new Rect (j * size, i * size, size, size), FGH (map [i, j]));}}if (GUI.Button (new Rect (col * size, 0, size, size), "Go Next")) {NextStep ();}// 绘制开启列表for (int j = 0; j < openList.Count; j++) {GUI.Button (new Rect (j * size, (row + 1) * size, size, size), FGH ((Grid)openList [j]));}// 绘制关闭列表for (int j = 0; j < closeList.Count; j++) {GUI.Button (new Rect (j * size, (row + 2) * size, size, size), FGH ((Grid)closeList [j]));}}// 通过逆向追溯找到路径void showFatherNode (Grid grid){if (grid.fatherNode != null) {print (grid.fatherNode.x + "," + grid.fatherNode.y);showFatherNode (grid.fatherNode);} }// 走下一步void NextStep (){//  0. 只要开启列表有节点, 就进行下一个过程if (openList.Count == 0) {print ("Over !");return;}//1. 从开放列表中选择第一个节点并将其作为当前节点Grid grid = (Grid)openList [0];if (grid.gridType == GridType.End) {showFatherNode (grid);print ("Over !");return;}//2. 获得这个当前节点不是障碍物的邻近节点for (int m = -1; m <= 1; m++) {for (int n = -1; n <= 1; n++) {if (!(m == 0 && n == 0)) {int x = grid.x + m;int y = grid.y + n;//3. 对于每一个邻近节点,查看是否已在关闭列表中.if (x >= 0 && x < row && y >= 0 && y < col &&map [x, y].gridType != GridType.Obstacle && !closeList.Contains (map [x, y])) {// 4.计算G //  2015.5.5 moveint g = grid.G + (int)(Mathf.Sqrt (Mathf.Abs (m) + Mathf.Abs (n)) * 10);// 如果不在closeList if (!openList.Contains (map [x, y])) { //如果不在集合里//  2015.5.5 delete//if (map[x, y].G == 0 || g < map[x, y].G) {//map [x, y].G = g;////}map [x, y].G = g;map [x, y].H = Manhattan (x, y);map [x, y].F = map [x, y].G + map [x, y].H;//5.将代价数据存储在邻近节点中,并且将当前节点保存为该邻近节点的父节点.//    最后我们将使用这个父节点数据来追踪实际路径.map [x, y].fatherNode = grid;//6.将邻近节点存储在开放列表中.openList.Add (map [x, y]);} else { // 如果在集合里 2015.5.5 addif (map [x, y].G > g) { // 2015.7.15map [x, y].fatherNode = grid;map [x, y].G = g;map [x, y].F = map [x, y].G + map [x, y].H;}}//  7.根据F,以升序排列开放列表.openList.Sort ();}}}}//8. 如果没有邻近节点需要处理, 将当前节点放入关闭列表并将其从开放列表中移除.closeList.Add (grid);openList.Remove (grid);}// H值(曼哈顿估算法)int Manhattan (int x, int y){return (int)(Mathf.Abs (xEnd - x) + Mathf.Abs (yEnd - y)) * 10;}// 将格子FGH 以字符串形式显示string FGH (Grid grid){string fgh = "F:" + grid.F + "\n";fgh += "G:" + grid.G + "\n";fgh += "H:" + grid.H + "\n";fgh += "(" + grid.x + "," + grid.y + ")";return fgh;}}



运行后点击画面上的“Go Next”按钮,即可观察每部计算详情。




二、游戏对象及线性渲染可视化版本


核心逻辑与第一种方式相同,代码不再赘述。

工程下载链接请参见4楼评论。

所有的参数可在工程中GameController对象上的脚本属性里设置,

运行后Game场景里点击鼠标即可观察路径搜索过程。



后语

A*算法的具体实现细节有很多,本文脚本只是给出了其中一种。

另外,按照这个算法障碍墙是可以斜穿的,若要避免斜穿还需进一步修改。

0 0
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 膝盖运动时疼痛睡觉不疼怎么办 在农村里床上老是有小蜈蚣该怎么办 瑜伽垫晒太阳后散发的甲醛怎么办 杯子盖上的皮垫子掉了漏水怎么办 20个月宝宝天天晚上不睡觉怎么办 八个月大小孩天天晚上不睡觉怎么办 如果开了光的貔貅不要了要怎么办 刚岀生的婴儿长得太快怎么办 呼市去办牌照时没有牌照怎么办 宾馆发现隐藏的摄像头怎么办报警吗 拍拍贷律师函寄到家里了怎么办 欠了拍拍贷本息一万多了怎么办 把人偷小孩的人贩子打死了怎么办 怀孕3个月没有胎心怎么办 社保局打印关系转移信封之后怎么办 长裙变装外出被发现了怎么办 超变陀螺怎么绳子拉不出来怎么办 梦幻诛仙传说时间得不到东西怎么办 夹在强势母亲和强势老公中间怎么办 工作调动校长总拖着不盖章怎么办 宝贝在妈妈肚子里发育慢怎么办 领导安排你负责一次讲座你怎么办 雷蛇北海巨妖耳机有回音怎么办 手机直播声卡有杂音有回音怎么办 大班见到陌生人入园怎么办安全教案 两首歌合并到一起中间有停顿怎么办 想做主持人但不是播音专业怎么办 动脉造影术2天后穿刺点出血怎么办 战舰世界买了重复金币船怎么办 戒指戴在手上取不下来怎么办 工作中难以和同事上司相处怎么办 老是被上司和同事欺负我该怎么办 桌面太低座位太高写字不舒服怎么办 一个人如果欠下网贷无法偿还怎么办 c语言的编译和运行不见了怎么办 8k纸的国庆节手抄报怎么办 合同员工签字了老板一直未签怎么办 下体长毛了很多肉芽很痒怎么办 对方拟把假离婚协议弄假成真怎么办 领主之塔改版后旧材料怎么办 汽车遥控钥匙打不开车门怎么办