js A*寻路算法之3d简单化

  1 /* SeekPath A*寻路
  2 
  3 parameter: 
  4     option: Object{
  5         angle, timeout, maxHeight, size, lenX, lenY
  6     }
  7 
  8     heights: Array[Number]
  9 
 10 attribute:
 11     size: Number;     //每个索引的大小
 12     lenX: Number;     //最大长度x (设置此属性时, 你需要重新.initMap(heights); .range 会被重置)
 13     lenY: Number;     //最大长度y (设置此属性时, 你需要重新.initMap(heights); .range 会被重置)
 14 
 15     range: Box;            //本次的搜索范围, 默认: 0,0,lenX,lenY
 16     angle: Number;         //8四方向 或 16八方向 默认 16
 17     timeout: Number;     //超时毫秒 默认 500
 18     maxHeight: Number;     //相邻可走的最大高 默认 6
 19 
 20     //只读属性
 21     success: Bool;            //只读; 本次搜索是否成功找到终点; (如果为false说明.run()返回的是 距终点最近的路径; 超时也会被判定为false)
 22     path: Array[x, y, z];    //存放.run()返回的路径
 23     map: Map;                 //地图的缓存数据
 24 
 25 method:
 26     initMap(heights: Array[Number]): undefiend; //初始化类时自动调用一次; heights:如果你的场景存在高请定义此参数
 27     run(x, y, x1, y1: Number): Array[x, y, z]; //参数索引坐标
 28 
 29 demo:
 30     const sp = new SeekPath({
 31         angle: 16,
 32         timeout: 500,
 33         maxHeight: 6,
 34         size: 10,
 35         lenX: 1000,
 36         lenY: 1000,
 37     }),
 38 
 39     path = sp.run(0, 0, 1000, 1000);
 40 
 41     console.log(sp);
 42 
 43 */
 44 class SeekPath{
 45 
 46     static _open = []
 47     static _dots = [] 
 48     static dots = []
 49     static _sort = function (a, b){return a["f"] - b["f"];}
 50 
 51     #map = null;
 52     #path = [];
 53     #success = true;
 54     #halfX = 50;
 55     #halfY = 50;
 56 
 57     #size = 10;
 58     #lenX = 10;
 59     #lenY = 10;
 60 
 61     constructor(option = {}, heights = null){
 62         this.angle = (option.angle === 8 || option.angle === 16) ? option.angle : 16; //8四方向 或 16八方向
 63         this.timeout = option.timeout || 500; //超时毫秒
 64         this.maxHeight = option.maxHeight || 6;
 65         this.range = new Box();
 66         this.size = option.size || 10;
 67         this.lenX = option.lenX || 10;
 68         this.lenY = option.lenY || 10;
 69         
 70         this.initMap(heights);
 71 
 72     }
 73 
 74     get map(){
 75         return this.#map;
 76     }
 77 
 78     get path(){
 79         return this.#path;
 80     }
 81 
 82     get success(){
 83         return this.#success;
 84     }
 85 
 86     get size(){
 87         return this.#size;
 88     }
 89 
 90     set size(v){
 91         this.#size = v;
 92         v = v / 2;
 93         this.#halfX = v * this.#lenX;
 94         this.#halfY = v * this.#lenY;
 95     }
 96 
 97     get lenX(){
 98         return this.#lenX;
 99     }
100 
101     set lenX(v){
102         this.#lenX = v;
103         v = this.#size / 2;
104         this.#halfX = v * this.#lenX;
105         this.#halfY = v * this.#lenY;
106         this.range.x = 0;
107         this.range.w = this.#lenX-1;
108     }
109 
110     get lenY(){
111         return this.#lenY;
112     }
113 
114     set lenY(v){
115         this.#lenY = v;
116         v = this.#size / 2;
117         this.#halfX = v * this.#lenX;
118         this.#halfY = v * this.#lenY;
119         this.range.y = 0;
120         this.range.h = this.#lenY-1;
121     }
122 
123     toScene(n, v){ //n = "x|y"
124         //n = n === "y" ? "lenY" : "lenX";
125         if(n === "y") return v * this.#size - this.#halfY;
126         return v * this.#size - this.#halfX;
127     
128     }
129     
130     toIndex(n, v){
131         //n = n === "y" ? "lenY" : "lenX";
132         if(n === "y") return Math.round((this.#halfY + v) / this.#size);
133         return Math.round((this.#halfX + v) / this.#size);
134 
135     }
136 
137     initMap(heights){
138         heights = Array.isArray(heights) === true ? heights : null;
139         
140         const lenX = this.lenX, lenY = this.lenY;
141         var getHeight = (ix, iy) => {
142             if(heights === null) return 0;
143             ix = heights[ix * lenY + iy];
144             if(ix === undefined) return -99999999;
145             return ix;
146         },
147 
148         map = []//new Map();
149 
150         for(let x = 0, y, m; x < lenX; x++){
151             m = []//new Map();
152             for(y = 0; y < lenY; y++) m[y] = {x:x, y:y, height:getHeight(x, y),   g:0, h:0, f:0, p:null, id:""}//m.set(y, {x:x, y:y, height:getHeight(x, y),   g:0, h:0, f:0, p:null, id:""});
153             map[x] = m;//map.set(x, m);
154         }
155         
156         this.#map = map;
157         this._id = -1;
158         this._updateID();
159 
160         map = heights = getHeight = undefined;
161 
162     }
163 
164     setDots(x, y, a, r){ //获取周围的点 x,y, a:8|16, r:存放结果数组
165         r.length = 0;
166         const x_1 = x-1, x1 = x+1, y_1 = y-1, y1 = y+1;
167         if(a === 16) r.push(x_1, y_1, x, y_1, x1, y_1, x_1, y, x1, y, x_1, y1, x, y1, x1, y1);
168         else r.push(x, y_1, x, y1, x_1, y, x1, y);
169     
170     }
171 
172     _updateID(){
173         this._id++;
174         this._openID = "o_"+this._id;
175         this._closeID = "c_"+this._id;
176         
177     }
178 
179     run(x, y, x1, y1){
180         this.#path.length = 0;
181         if(this.#map === null || this.range.containsPoint(x, y) === false) return this.#path;
182         this._updateID();
183 
184         const _map = this.#map,
185         _sort = SeekPath._sort,
186         _open = SeekPath._open,
187         _dots = SeekPath._dots, 
188         dots = SeekPath.dots,
189         time = Date.now();
190 
191         var _n = _map[x][y],//_map.get(x).get(y), 
192         isDot = true, 
193         suc = _n, 
194         k, _k, _x, _y, mhd, g, h, f, _d;
195 
196         _n.g = 0;
197         _n.h = _n.h = Math.abs(x1 - x) * 10 + Math.abs(y1 - y) * 10; 
198         _n.f = _n.h;
199         _n.p = null;
200         _n.id = this._openID;
201         _open.push(_n);
202         
203         while(_open.length !== 0){
204             if(Date.now() - time > this.timeout) break;
205 
206             _open.sort(_sort);
207             _n = _open.shift();
208             if(_n.x === x1 && _n.y === y1){
209                 suc = _n;
210                 break;
211             }
212             
213             if(suc.h > _n.h) suc = _n;
214             _n.id = this._closeID;
215             this.setDots(_n.x, _n.y, this.angle, _dots);
216             
217             for(k = 0; k < this.angle; k += 2){
218 
219                 _x = _dots[k]; _y = _dots[k+1];
220                 if(this.range.containsPoint(_x, _y) === false) continue;
221 
222                 _d = _map[_x][_y];//_map.get(_x).get(_y);
223                 if(_d.id === this._closeID) continue;
224 
225                 mhd = Math["abs"](_n["x"] - _x) + Math["abs"](_n["y"] - _y);
226                 g = _n["g"] + (mhd === 1 ? 10 : 14);
227                 h = Math["abs"](x1 - _x) * 10 + Math["abs"](y1 - _y) * 10;
228                 f = g + h;
229             
230                 if(_d.id !== this._openID){
231                     
232                     if(Math["abs"](_n.height - _d.height) < this.maxHeight){
233                         
234                         if(mhd !== 1 && this.angle === 16){
235                             
236                             this.setDots(_d.x, _d.y, 8, dots); //与 d 正对的4个点
237 
238                             for(_k = 0; _k < 8; _k += 2){
239                                 _x = dots[_k]; _y = dots[_k+1];
240                                 if(this.range.containsPoint(_x, _y) === false) continue;
241 
242                                 if(Math["abs"](_n.x - _x) + Math["abs"](_n.y - _y) === 1){
243 
244                                     if(Math["abs"](_n.height - _map[_x][_y].height) >= this.maxHeight){
245                                         isDot = false;
246                                         break;
247                                     }
248                                     
249                                 }
250 
251                             }
252                             
253                         }
254 
255                         if(isDot === true){
256                             _d.g = g;
257                             _d.h = h;
258                             _d.f = f;
259                             _d.p = _n;
260                             _d.id = this._openID;
261                             _open.push(_d);
262                             
263                         }
264 
265                         else isDot = true;
266                         
267                     }
268 
269                 }
270 
271                 else if(g < _d.g){
272                     _d.g = g;
273                     _d.f = g + _d.h;
274                     _d.p = _n;
275                     
276                 }
277     
278             }
279         
280         }
281 
282         this.#success = suc === _n;
283 
284         while(suc !== null){
285             this.#path.unshift(this.toScene("x", suc["x"]), suc["height"], this.toScene("y", suc["y"]));
286             suc = suc["p"];
287         }
288 
289         _open.length = _dots.length = dots.length = 0;
290         
291         return this.#path;
292     }
293 
294 }
完整代码

 

API说明:

 

parameter:
    option: Object{
        angle, timeout, maxHeight, size, lenX, lenY
    }

    heights: Array[Number]

attribute:
    size: Number;   //每个索引的大小
    lenX: Number;   //最大长度x (设置此属性时, 你需要重新.initMap(heights); .range 会被重置)
    lenY: Number;   //最大长度y (设置此属性时, 你需要重新.initMap(heights); .range 会被重置)

    range: Box;         //本次的搜索范围, 默认: 0,0,lenX,lenY
    angle: Number;      //8四方向 或 16八方向 默认 16
    timeout: Number;    //超时毫秒 默认 500
    maxHeight: Number;  //相邻可走的最大高 默认 6

    //只读属性
    success: Bool;          //只读; 本次搜索是否成功找到终点; (如果为false说明.run()返回的是 距终点最近的路径; 超时也会被判定为false)
    path: Array[x, y, z];   //存放.run()返回的路径
    map: Map;               //地图的缓存数据

method:
    initMap(heights: Array[Number]): undefiend; //初始化类时自动调用一次; heights:如果你的场景存在高请定义此参数
    run(x, y, x1, y1: Number): Array[x, y, z]; //参数索引坐标

demo:

 

 

posted @ 2022-05-28 14:36  鸡儿er  阅读(127)  评论(0编辑  收藏  举报