• 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:
  • 相关阅读:
    JS使用 popstate 事件监听物理返回键
    JQ判断div是否隐藏
    SQL Server DATEDIFF() 函数
    取消a或input标签聚焦后出现虚线框
    C#定时任务
    C# 保留N位小数
    C#打印单据
    SQL语句创建函数
    SVN检出新项目
    解决jQuery的toggle()的自动触发问题
  • 原文地址:https://www.cnblogs.com/weihexinCode/p/16320640.html
Copyright © 2020-2023  润新知