在Unity中新建类A_Star,其内复制下述全文,再根据下文中讲述的应用案例就可以明白如何使用了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 | using System.Collections.Generic; using UnityEngine; public class A_Star {<!-- --> /* 自己书写的Unity可使用的A星算法(C#版) 该脚本仅针对上下左右移动,没有做斜向移动 如果要看算法过程,可以转到最下面,不保证说明正确或清晰 */ /* 该脚本应用举例: //设置地图stringMap,可走点1,不可走点0,比如: string stringMap =//作为地图数据 "0000001111" + "0000111000" + "0000011010" + "0011111010" + "0110101010" + "1111111000"; int maxX = 9;//行从0计到9,则声明maxX=9 int maxY = 5;//列从0计到5,则声明maxY=5 //上方数据中,最左下角为坐标(0,0),然后: Vector2Int[] way; A_Star.GetWay(new Vector2Int(1, 0), new Vector2Int(6, 4), stringMap,maxX,maxY, out way); //就可以得到stringMap中,从坐标(0,0)到坐标(6,4)的路线way,way[0]表示起点坐标,其后元素顺序与路径上经过的各点一致 ------------------------------------------------------------------- //或者使用int[]作为地图数据也行,比如: int[] intMap = { 0,1,1,1,1 1,1,0,1,1 1,0,0,0,1 } int maxX=4;//行从0计到4,则声明maxX=4 int maxY=2;//列从0计到2,则声明maxY=2 //上方数据中,最左下角为坐标(0,0),然后: Vector2Int[] way; A_Star.GetWay(new Vector2Int(0,1),new Vector2Int(4,0),intMap,maxX,maxY,out way); //就可以得到intMap中,从坐标(0,1)到坐标(4,0)的路线way,way[0]表示起点坐标,其后元素顺序与路径上经过的各点一致 ------------------------------------------------------------------- //如果对同一个地图使用多次A_Star寻路,那么使用下述方式最好: A_Star.Point[][] points=A_Star.GetPointsMatrixByStringMap(stringMap,maxX,maxY);//或根据intMap得到points A_Star.GetWay(new VectorInt(起点1x,起点1y), new VectorInt(终点1x,终点1y), points, out way); A_Star.GetWay(new VectorInt(起点2x,起点2y), new VectorInt(终点2x,终点2y), points, out way); A_Star.GetWay(new VectorInt(起点3x,起点3y), new VectorInt(终点3x,终点3y), points, out way); //... //上述将Point[][]类型数据暂存起来,再多次使用GetWay */ //根据string型地图以及起点,终点,来试图获取路径(经过点的坐标数组),返回是否存在一条可行的路径 public static bool GetWay(Vector2Int startPos, Vector2Int endPos, string stringMap, int maxX, int maxY, out Vector2Int[] way) {<!-- --> return GetWay(startPos, endPos, GetPointsMatrixByStringMap(stringMap, maxX, maxY), out way); } //根据int[]型地图以及起点,终点,来试图获取路径(经过点的坐标数组),返回是否存在一条可行的路径 public static bool GetWay(Vector2Int startPos, Vector2Int endPos, int[] intMap, int maxX, int maxY, out Vector2Int[] way) {<!-- --> return GetWay(startPos, endPos, GetPointsMatrixByIntMap(intMap, maxX, maxY), out way); } //根据string型地图信息获得Point[][]类型数据 public static Point[][] GetPointsMatrixByStringMap(string stringMap, int maxX, int maxY) {<!-- --> int[] map = GetIntMapByString(stringMap, maxX, maxY); bool[][] boolMatrix = GetBoolMatrixByIntMap(map, maxX, maxY); return GetPointsMatrixByBoolMatrix(boolMatrix, maxX, maxY); } //根据int[]型地图信息获得Point[][]类型数据 public static Point[][] GetPointsMatrixByIntMap(int[] intMap, int maxX, int maxY) {<!-- --> bool[][] boolMatrix = GetBoolMatrixByIntMap(intMap, maxX, maxY); return GetPointsMatrixByBoolMatrix(boolMatrix, maxX, maxY); } //根据Point[][],以及起点和终点的信息,获得最终路径(经过点的坐标数组),返回是否存在一条可行的路径 public static bool GetWay(Vector2Int startPos, Vector2Int endPos, Point[][] points, out Vector2Int[] way) {<!-- --> foreach (var array in points) {<!-- --> foreach (var o in array) {<!-- --> o.fromStart = int.MaxValue; o.toEnd = 0; o.inClosed = false; o.inOpen = false; } } open.RemoveRange(0, open.Count); closed.RemoveRange(0, closed.Count); way = new Vector2Int[0]; Point start = points[startPos.x][startPos.y]; start.Refresh(0, start, endPos.x, endPos.y); while (true) {<!-- --> if (open.Count <= 0) return false; if (FindMinPointF().CalculateAround(points, endPos)) {<!-- --> way = GetWayFromPointMatrix(points, startPos, endPos); return true; } } } static Point FindMinPointF() {<!-- --> int where = -1; int min = int.MaxValue; for (int i = 0; i < open.Count; ++i) if (open[i].F <= min) {<!-- --> where = i; min = open[i].F; } return open[where]; } static Vector2Int[] GetWayFromPointMatrix(Point[][] points, Vector2Int startPos, Vector2Int endPos) {<!-- --> List<Vector2Int> wayList = new List<Vector2Int>(); Point prePoint = points[endPos.x][endPos.y]; wayList.Add(new Vector2Int(prePoint.x, prePoint.y)); while (true) {<!-- --> if (prePoint.x == startPos.x && prePoint.y == startPos.y) break; prePoint = prePoint.lastPoint; wayList.Add(new Vector2Int(prePoint.x, prePoint.y)); } Vector2Int[] way = new Vector2Int[wayList.Count]; for (int i = 0; i < wayList.Count; ++i) way[i] = wayList[wayList.Count - 1 - i]; return way; } static int[] GetIntMapByString(string stringMap, int maxX, int maxY) {<!-- --> int amout = (maxX + 1) * (maxY + 1); int[] intMap = new int[amout]; char[] cs = stringMap.ToCharArray(); for (int i = 0; i < amout; ++i) intMap[i] = cs[i] - 48; return intMap; } static bool[][] GetBoolMatrixByIntMap(int[] map, int maxX, int maxY) {<!-- --> bool[][] canTo = new bool[maxX + 1][]; for (int i = 0; i <= maxX; ++i) canTo[i] = new bool[maxY + 1]; int tempX = maxX + 1; int mapPos; for (int i = 0; i <= maxX; i += 1) for (int j = 0; j <= maxY; j += 1) {<!-- --> /* map一行的个数是maxX+1个,一列的个数是maxY+1个 这里举一个例子: 这里有个int[] map如下,其中maxX=5,maxY=3 1,0,1,1,0,1, 1,0,1,1,0,1, 1,1,0,1,0,1, 0,1,1,1,1,1 最左下角(0,0)点对应map的位置是(maxX+1)*(maxY-0)+0=18 (1,0)点对应map的位置是(maxX+1)*(maxY-0)+1=19 (1,1)点对应map的位置是(maxX+1)*(maxY-1)+1=13 (4,3)点对应map的位置是(maxX+1)*(maxY-3)+4=4 这样就得到了规律(x,y),x作为加数加到前式的后面,y作为maxY后面的减数 */ mapPos = tempX * (maxY - j) + i; ; if (map[mapPos] == 1) canTo[i][j] = true; else canTo[i][j] = false; } return canTo; } static Point[][] GetPointsMatrixByBoolMatrix(bool[][] canTo, int maxX, int maxY) {<!-- --> int tempX = maxX + 1, tempY = maxY + 1; Point[][] points = new Point[tempX][]; for (int i = 0; i < tempX; i += 1) points[i] = new Point[tempY]; for (int i = 0; i < tempX; i += 1) for (int j = 0; j < tempY; j += 1) points[i][j] = new Point(i, j, canTo[i][j]); return points; } static List<Point> open = new List<Point>(); static List<Point> closed = new List<Point>(); public class Point {<!-- --> public int x; public int y; public int fromStart;//G public int toEnd;//H public int F; public bool canTo; public bool inOpen; public bool inClosed; public Point lastPoint; public Point(int x, int y, bool canTo) {<!-- --> this.x = x; this.y = y; fromStart = int.MaxValue; toEnd = 0; F = 0; this.canTo = canTo; inOpen = false; inClosed = false; } public void Refresh(int _fromStart, Point _lastPoint, int endX, int endY) {<!-- --> if (!inOpen) open.Add(this); inOpen = true; int temp = endX - x; if (temp < 0) temp = -temp; toEnd += temp; temp = endY - y; if (temp < 0) temp = -temp; toEnd += temp; if (_fromStart <= fromStart) {<!-- --> fromStart = _fromStart; F = fromStart + toEnd; lastPoint = _lastPoint; } } public void Passed() {<!-- --> closed.Add(this); for (int i = 0; i < open.Count; ++i) if (open[i].x == x && open[i].y == y) {<!-- --> open.RemoveAt(i); break; } inOpen = false; inClosed = true; } public bool CalculateAround(Point[][] points, Vector2Int endPos) {<!-- --> if (x == endPos.x && y == endPos.y) return true; int maxX = points.Length - 1; int maxY = points[0].Length - 1; if (x - 1 >= 0) {<!-- --> if (!points[x - 1][y].inClosed && points[x - 1][y].canTo) points[x - 1][y].Refresh(fromStart + 1, this, endPos.x, endPos.y); } if (x + 1 <= maxX) {<!-- --> if (!points[x + 1][y].inClosed && points[x + 1][y].canTo) points[x + 1][y].Refresh(fromStart + 1, this, endPos.x, endPos.y); } if (y - 1 >= 0) {<!-- --> if (!points[x][y - 1].inClosed && points[x][y - 1].canTo) points[x][y - 1].Refresh(fromStart + 1, this, endPos.x, endPos.y); } if (y + 1 <= maxY) {<!-- --> if (!points[x][y + 1].inClosed && points[x][y + 1].canTo) points[x][y + 1].Refresh(fromStart + 1, this, endPos.x, endPos.y); } Passed(); return false; } } } /* 包含的数据结构: Point以及Point[][],后者记录整个地图上的点 每个Point点除了含有坐标值x,y,还含有 fromStart/toEnd/F/[上一个点]/[是否在open列表中]/[是否在closed列表中] fromStart表示从原点到该点的总路程(fromStart会动态变化) toEnd表示从该点到目的坐标的直接路程(固定值,就是 横坐标差(绝对值)+纵坐标差(绝对值)) F值赋值为fromStart+toEnd [上一个点]赋值为上一个经过的点(下面有具体的赋值方式说明) [open列表]和[closed列表] 分别记录 [可以走的点]和[不能再走的点] ------------------------ 现在给出了地图,然后要计算从起点到终点的一条最短路径: 过程描述: 计算起点的fromStart(=0),toEnd,F,起点的[上一个点]可以赋值为空或者自身,然后加入该起点到open列表 然后重复下列步骤,直到找到最终点,如果循环过程中,若open列表元素数量变为0,表示没有找到一条可行的路径 { 从open列表中选择F最小的点记为P点(如果多个点有相同的最小值F,一般取其中最后添加进open列表的点) 然后判断P点是否为终点,若是,则得到最终路径(根据每一个点所记录的[上一个点]可以得到完整路径)并返回,若不是,则对该点周围(上下左右)的每个点C进行下述: .若C没有在open列表中,则该点的fromStart赋值为P点的fromStart+1,并计算toEnd,F,以及设置C点的[上一个点]为P点,然后将C点添加到open列表,C点的[是否在open列表中]赋值为true .若C已在open列表中,则判断P点的fromStart+1是否比当前点的fromStart更小,若更小则更新该点的fromStart,F,以及替换[上一个点]为P点 .若C在closed列表中则不计算 加入P点到closed列表(closed列表中的点不会再被计算或刷新),P点的[是否在closed列表中]赋值为true,[是否在open列表中]赋值为false } */ /* 如果将上述脚本用到C#项目,需要新建类Vector2Int,如下: public class Vector2Int { public int x; public int y; public Vector2Int(){} public Vector2Int(int x,int y) { this.x=x; this.y=y; } } */ |