平哥走天下 發表於 2025-4-23 11:57:00

最好懂的自动寻路-前端

<div class="lake-content">
<h1 id="wMz6N"><span class="ne-text">先看效果</span></h1>
<p id="u21efb8fc" class="ne-p"><span class="ne-text">黑块是障碍物,可以理解成游戏里的墙</span></p>
<p id="udf20f614" class="ne-p"><span class="ne-text">白块是可走的路</span></p>
<p id="ueb2ca3ad" class="ne-p"><span class="ne-text">点哪里走哪里,类似于游戏里的移动</span></p>
<p id="u2739d423" class="ne-p"><strong><span class="ne-text">在线演示</span></strong></p>
<p id="u262c13c8" class="ne-p"><img src="https://img2024.cnblogs.com/blog/1049707/202504/1049707-20250423115435106-1980563019.gif"></p>
<p id="ufce590ef" class="ne-p"></p>
<h1 id="ZLi58"><span class="ne-text">前言</span></h1>
<p id="u6688d0e2" class="ne-p"><span class="ne-text">本节采用BFS算法,没有涉及到一些更深层次的优化。</span></p>
<p id="ucb2a9146" class="ne-p"><span class="ne-text">代码用的vue3+ts,单组件,复制粘贴即可运行,核心的代码就一个函数,可以用GPT转成其他语言并运行</span></p>
<p id="u6e02a31c" class="ne-p"><span class="ne-text">鄙人没有从事这方面工作,纯属兴趣爱好,只能带大家看看</span><strong><span class="ne-text">寻路</span></strong><span class="ne-text">到底是怎么个回事,以下均为个人理解。</span></p>
<p id="ufa3766d1" class="ne-p"><span class="ne-text">先说业务逻辑,再贴实现代码</span></p>
<p id="u0c496843" class="ne-p"></p>
<p id="u8d51a235" class="ne-p"><span class="ne-text">寻路三步走:</span><strong><span class="ne-text">画图-&gt;寻路-&gt;走路</span></strong><span class="ne-text">。</span></p>
<ul class="ne-ul">
<li id="u516e09ac" data-lake-index-type="0"><span class="ne-text">地图的根本作用是让位置更直观</span></li>
<li id="ua1b3585b" data-lake-index-type="0"><span class="ne-text">核心难点是寻路</span></li>
<li id="u9b5bbc26" data-lake-index-type="0"><span class="ne-text">走路是最终目的</span></li>
</ul>
<p id="u24dc202b" class="ne-p"><span class="ne-text">我们就以这样的顺序开始吧</span></p>
<h1 id="XZtNs"><span class="ne-text">画图</span></h1>
<p id="u5558d6f1" class="ne-p"><span class="ne-text">怎么去实现地图,图到底长什么样呢?如果从没接触过,那看起来有点抽象,先看实现地图的代码</span></p>
<p id="ud7b90a22" class="ne-p"><span class="ne-text">现象:观察右边的图片,注意0,1和黑白方块</span></p>
<p id="uf59d7ce4" class="ne-p"><img src="https://img2024.cnblogs.com/blog/1049707/202504/1049707-20250423115455901-1652932991.png"><img src="https://img2024.cnblogs.com/blog/1049707/202504/1049707-20250423115447155-986644716.png"></p>
<p id="uffa2e776" class="ne-p"><span class="ne-text">总结:【0=白块】,【1=黑块】</span></p>
<pre id="bVcqy" class="ne-codeblock language-plain highlighter-hljs" data-language="plain" data-dark-theme="true"><code>IF(X=4&amp;Y=0){
    渲染黑块
}ELSE{
    渲染白块
}</code></pre>
<h2 id="rvwTg"><span class="ne-text">实现</span></h2>
<p id="ua8143206" class="ne-p"><span class="ne-text">我这里的布局很简陋</span></p>
<pre id="NF6da" class="ne-codeblock language-vue highlighter-hljs" data-language="vue" data-dark-theme="true"><code>&lt;div class="grid"&gt;
&lt;div v-for="(item, k) in stage" :key="k" class="flex"&gt;
    &lt;div
      v-for="(m, n) in item"
      :key="n"
      class="square"
      :class=""
      &gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/div&gt;

const stage = [
,
,
,
,
,
,
,
,
];

.square {
border: 1px solid #000;
@apply w- h-;
}</code></pre>
<h2 id="vByz8"><span class="ne-text">怎么让路和墙丰富起来</span></h2>
<p id="u4d7e8a51" class="ne-p"><span class="ne-text">有些同学可能有疑问了,我要做墙做怪物,墙有很多种,怪物也有很多种,仅有0和1怎么表示呢?</span></p>
<p id="u0047858d" class="ne-p"><span class="ne-text">把换成</span><strong><span class="ne-text">对象</span></strong><span class="ne-text">就好了,以前端为例,可以直接用JSON,把上面的代码修改一下</span></p>
<p id="ue229f1a6" class="ne-p"><img src="https://img2024.cnblogs.com/blog/1049707/202504/1049707-20250423115504586-817008495.png"></p>
<p id="u01398eb7" class="ne-p"><span class="ne-text">接下来只要根据image渲染不同的图片,type和style可以用来做逻辑,比如经过style=地狱门的时候就扣血,经过砂石路移动速度变慢,判断是否经过,就是看英雄的坐标和障碍物是否重叠。</span></p>
<h2 id="con6B"><span class="ne-text">地图编辑器</span></h2>
<p id="u2f7658ab" class="ne-p"><span class="ne-text">种类是丰富了,但是我想做一个英雄联盟这样的地图,那不得写到宇宙二次爆炸?那该怎么做呢?</span></p>
<p id="u1860ce80" class="ne-p"><span class="ne-text">玩过魔兽争霸的朋友可能知道,有一个东西叫地图编辑器,从编辑器里诞生Dota,Dota衍生LOL,Lol变异成王者农药,农药里的三国的关羽能骑马砍战国末期的嬴政....</span></p>
<p id="uff10aba6" class="ne-p"><span class="ne-text">很久以前做了一个简单的地图编辑器,墙能够粉刷,其实还做了怪物来着,后面觉得实现方式有问题,给删了。</span></p>
<p id="ubb3b3b02" class="ne-p"><span class="ne-text">前半段演示画墙,后面演示走路</span></p>
<p id="ud31eb638" class="ne-p"><span class="ne-text">核心就是用</span><span class="ne-text">可视化</span><span class="ne-text">创建地图数据</span></p>
<p id="u18459ac3" class="ne-p"><img src="https://img2024.cnblogs.com/blog/1049707/202504/1049707-20250423115515675-986115169.gif"></p>
<p id="udaeb1390" class="ne-p"></p>
<h1 id="e2tKb"><span class="ne-text">寻路</span></h1>
<p id="u4256f8d4" class="ne-p"><span class="ne-text">来到核心内容了,代码一大堆,网上搜的到,AI能生成,但都没那么容易理解。</span></p>
<p id="u16b34d24" class="ne-p"><span class="ne-text">要想写出代码,先要了解业务逻辑</span></p>
<h2 id="cTfMw"><span class="ne-text">寻路前的数据</span></h2>
<p id="u21a88b1e" class="ne-p"><span class="ne-text">寻路,至少要知道自己在哪儿,要去哪儿。</span></p>
<p id="u37d98230" class="ne-p"><span class="ne-text">App导航需要先获取你的位置,再输入目的地,最后确认路线,这里也是如此,寻路前需要最基本的数据</span></p>
<ul class="ne-ul">
<li id="uc2e56acb" data-lake-index-type="0"><span class="ne-text">起点和终点【蓝色,粉色】</span></li>
<li id="u4ca663db" data-lake-index-type="0"><span class="ne-text">将起点加入到已走过的路</span></li>
<li id="u4f97a293" data-lake-index-type="0"><span class="ne-text">起点和终点不能相同【没写】</span></li>
<li id="uf9a4af5b" data-lake-index-type="0"><span class="ne-text">还有不能出界,导航从广州到上海,想必你也不想从西藏方向出发,再从美国那边飞回来吧</span></li>
</ul>
<p id="u4187e674" class="ne-p"></p>
<p id="ud994ca33" class="ne-p">  <img src="https://img2024.cnblogs.com/blog/1049707/202504/1049707-20250423115534936-1366235408.png"></p>
<p id="u520b5420" class="ne-p"><span class="ne-text">所对应的代码如下,有了最基本的数据后才能寻路</span></p>
<pre id="Pyfkp" class="ne-codeblock language-javascript highlighter-hljs" data-language="javascript" data-dark-theme="true"><code>/**
* @description 寻找路径
* @param map 二维数组
* @param start 起始点
* @param end 终点
* @returns 路径数组
*/
export const findPath = (stage: number[][], start: number[], end: number[]) =&gt; {
//终点坐标
    const = end;
//        将起点加入队列,这是整个寻路的核心
//        也是结束的条件之一,如果队列空了,表示没有找不到路了
    const queue = [];
//如果终点为障碍物,则不走了
    if (grid === 1) {
      return [];
    }
//已经走过的路,用SET去重
const visited = new Set&lt;string&gt;();
//将起点设为已经走过的路
visited.add(`${start},${start}`);
//开始寻路
}</code></pre>
<h2 id="bkvPM"><span class="ne-text">开始寻路</span></h2>
<ul class="ne-ul">
<li id="u135f9e55" data-lake-index-type="0"><span class="ne-text">寻路</span></li>
</ul>
<ul class="ne-list-wrap">
<ul class="ne-ul">
<li id="u218a5813" data-lake-index-type="0"><span class="ne-text">探索的过程只能一格一格的找,一次找很多步,容易错过终点</span></li>
<li id="u4523cca2" data-lake-index-type="0"><span class="ne-text">如果没有找到终点,并且还有没走过的路,就要一直找,【核心驱动】</span></li>
</ul>
</ul>
<ul class="ne-ul">
<li id="u00ece555" data-lake-index-type="0"><span class="ne-text">搜索:</span></li>
</ul>
<ul class="ne-list-wrap">
<ul class="ne-ul">
<li id="ue299d538" data-lake-index-type="0"><span class="ne-text">每经过一个点,都要找一下</span><strong><span class="ne-text">上下左右</span></strong><span class="ne-text">有没有终点的坐标,如此循环</span></li>
</ul>
</ul>
<p id="udc22703a" class="ne-p"></p>
<p id="u17661bfb" class="ne-p"><span class="ne-text">图示寻路过程:</span></p>
<ul class="ne-ul">
<li id="u97d22955" data-lake-index-type="0"><span class="ne-text">蓝块是我们的本体,粉色是终点</span></li>
<li id="ubb0bd2e5" data-lake-index-type="0"><span class="ne-text">旁边的色块是搜索的结果,</span></li>
</ul>
<ul class="ne-list-wrap">
<ul class="ne-ul">
<li id="uf39c004b" data-lake-index-type="0"><span class="ne-text">红色代表出界或已走过,总之不能走的地方,且不会加入到队列</span></li>
<li id="u1be13eca" data-lake-index-type="0"><span class="ne-text">深绿色代表优先走,会加入到队列,且下一次循环就要走到这个点【很重要】</span></li>
<li id="u6ef20f61" data-lake-index-type="0"><span class="ne-text">浅绿色代表能走,且经过检测不是终点,但不会加入队列</span></li>
<li id="u2fd60866" data-lake-index-type="0"><span class="ne-text">优先级取决于方向的顺序,可以根据情况调整,比如知道目标点在最下面,可以把去下面的方向调整到最前面来</span></li>
</ul>
</ul>
<p id="u525263b6" class="ne-p"></p>
<p id="u8d60df2b" class="ne-p"><img src="https://img2024.cnblogs.com/blog/1049707/202504/1049707-20250423115557765-1066930976.png"><img src="https://img2024.cnblogs.com/blog/1049707/202504/1049707-20250423115602245-1046497262.png"><img src="https://img2024.cnblogs.com/blog/1049707/202504/1049707-20250423115608054-530616827.png"></p>
<h3 id="f5wRr"><span class="ne-text">代码</span></h3>
<pre id="OPIgb" class="ne-codeblock language-javascript highlighter-hljs" data-language="javascript" data-dark-theme="true"><code>// 方向,下面有解释
const DIRECTIONS = [
,
,
[-1, 0],
,
];
/**
* @description 寻找路径
* @param grid 二维数组
* @param start 起始点
* @param end 终点
* @returns 路径数组
*/
export const findPath = (grid: number[][], start: number[], end: number[]) =&gt; {
const = end;
/* 三维数组 */
const queue = [];

if (grid === 1) {
    return [];
}
const visited = new Set&lt;string&gt;();
visited.add(`${start},${start}`);

while (queue.length &gt; 0) {
    /* 取出最后一个加入的坐标 */
    const lastRoute = queue.shift();
    if (lastRoute) {
      /* 取出新的坐标 */
      const route = lastRoute;
      /* 新的坐标的x,y,用于判断是否为终点 */
      const = route;
      if (x === endX &amp;&amp; y === endY) {
      /* 如果是终点则返回 */
      return lastRoute;
      }
      /* 继续根据方向寻找,下面有解释 */
      for (const of DIRECTIONS) {
      /* 周围的坐标,上下左右 */
      const newX = dx + x;
      const newY = dy + y;

      const newRoute = [...lastRoute, ];
      /* 如果找到坐标,就别找了 */
      if (newX === endX &amp;&amp; newY === endY) {
          return newRoute;
      }
      /* 检测边界,下面有解释 */
      if (collisionDetection(grid, x, y, !visited.has(`${newX},${newY}`))) {
          visited.add(`${x},${y}`);
          //将新的坐标和旧的队列一并加入到路线中
          queue.push(newRoute);
      }
      }
    }
}
return [];
};</code></pre>
<p id="u8273cb41" class="ne-p"></p>
<h2 id="VtVYz"><span class="ne-text">难点解析</span></h2>
<h3 id="pxcaJ"><span class="ne-text">边界检测</span></h3>
<p id="ue0c7bd62" class="ne-p"><span class="ne-text">普通地图,一般都是有边界的,且限定不能出界,所以需要准备相关条件进行判断,障碍物也是边界</span></p>
<p id="ub25e390d" class="ne-p"><span class="ne-text">所以地图有几个地方不能去,</span></p>
<ul class="ne-ul">
<li id="uf7da91cd" data-lake-index-type="0"><strong><span class="ne-text">上下左右</span></strong><span class="ne-text">的边界不能走</span></li>
<li id="u3273dad8" data-lake-index-type="0"><span class="ne-text">已经走过的路,不能走,否则就一直绕圈,完蛋啦。</span></li>
<li id="u25f776c8" data-lake-index-type="0"><span class="ne-text">障碍物也不能走,穿墙了就到都处是路</span></li>
</ul>
<pre id="QRAPa" class="ne-codeblock language-javascript highlighter-hljs" data-language="javascript" data-dark-theme="true"><code>
/**
*@description 边界检测
* @param stage 二维数组
* @param x 当前的横坐标
* @param y 当前的纵坐标
* @param alreadyPassed 是否已经过
*/
export const collisionDetection = (
stage: number[][],
x: number,
y: number,
alreadyPassed: boolean
) =&gt; {
const MAX_X = stage.length - 1; /* 右边 */
const MIN_X = 0 /* 左边 */,
    MIN_Y = 0; /* 上边 */
const MAX_Y = stage.length - 1; /* 下边 */

return (
    x &lt;= MAX_X &amp;&amp;
    x &gt;= MIN_X &amp;&amp;
    y &gt;= MIN_Y &amp;&amp;
    y &lt;= MAX_Y &amp;&amp;
    stage !== 1 &amp;&amp;//判断不能是障碍物
    alreadyPassed
);
};</code></pre>
<h3 id="GLC1M"><span class="ne-text"> 方向</span></h3>
<p id="ua89f77d8" class="ne-p"><span class="ne-text">以我们正常去移动用的方式去看,只要修改横轴纵轴坐标即可,如下</span></p>
<ul class="ne-ul">
<li id="uc6f9fab0" data-lake-index-type="0"><span class="ne-text">往右移动x+=1</span></li>
<li id="ud4b965b3" data-lake-index-type="0"><span class="ne-text">往左移动x-=1</span></li>
<li id="uc1e97f47" data-lake-index-type="0"><span class="ne-text">往上移动y-=1</span></li>
<li id="uc4379db2" data-lake-index-type="0"><span class="ne-text">往下移动y+=1</span></li>
</ul>
<p id="u23c49438" class="ne-p"><span class="ne-text">但这里不相同,需要没走一步,就要四周观察,查看四次,所以被设计成了这样,当然坐标数组可以用对象代替,方便调整顺序</span></p>
<pre id="tHHP9" class="ne-codeblock language-javascript highlighter-hljs" data-language="javascript" data-dark-theme="true"><code>const DIRECTIONS = [
,        //上
,                //下
[-1, 0],        //左
,                //右
];
//方向结合当前的坐标,就能检测周围了
let =current;//假设当前坐标为
for (const of DIRECTIONS) {
      /* 周围的坐标,上下左右 */
      const newX = dx + x;
      const newY = dy + y;
   //循环次数                                                方向                                                                        新的坐标                                备注
   //   1                                DIRECTIONS=                                                                        出界了                       
   //   2                                DIRECTIONS=                                                                        往右走一格               
}</code></pre>
<h3 id="hQu5P"><span class="ne-text">队列</span></h3>
<p id="ue7580aea" class="ne-p"><span class="ne-text">这可能是最难理解的一部分了,然而我也很难用一两张图去表达,这个过程是动态的,除非做成动画,可目前还没有加这个技能点,那只能大致说说了</span></p>
<p id="u51adbf0f" class="ne-p"><code class="ne-code"><span class="ne-text">const queue = [];</span></code></p>
<p id="udc965767" class="ne-p"><span class="ne-text">队列详解</span></p>
<p id="u5ec862d6" class="ne-p"><span class="ne-text">队列初始化数据是queue=[[]],是一个三维数组,</span></p>
<ol class="ne-ol">
<li id="u577e385f" data-lake-index-type="0"><span class="ne-text">最里面一层是</span><strong><span class="ne-text">坐标</span></strong></li>
<li id="uc7a6ed43" data-lake-index-type="0"><span class="ne-text">中间一层是真正的</span><strong><span class="ne-text">路线</span></strong></li>
<li id="u589f6d5d" data-lake-index-type="0"><strong><span class="ne-text">最外层</span></strong><span class="ne-text">是终止条件,始终只有一个数据,当四处都是障碍物,没有路线的时候,终止循环</span></li>
<li id="uf55ba667" data-lake-index-type="0"><span class="ne-text">发现有可用的坐标的话,把坐标加入到路线中,再把路线加入到queue中</span></li>
</ol>
<p id="ue131b2a7" class="ne-p"><img src="https://img2024.cnblogs.com/blog/1049707/202504/1049707-20250423115630402-696025569.png"></p>
<p id="u9e3e6cb6" class="ne-p"><span class="ne-text">简单流程</span></p>
<p id="ud1fbe974" class="ne-p"><span class="ne-text">(flag)发现可用的</span><strong><span class="ne-text">坐标</span></strong><span class="ne-text">,更新到路线和队列中,取出最新的坐标,继续寻找,回到前面的(flag)继续发现,直到找到目标</span></p>
<h1 id="tkEVP"><span class="ne-text">走路</span></h1>
<p id="ub5969839" class="ne-p"><span class="ne-text">执行完上面的</span><strong><span class="ne-text">findPath</span></strong><span class="ne-text">,我们就能找到路了,路长这个样子</span></p>
<pre id="Q9FQg" class="ne-codeblock language-javascript highlighter-hljs" data-language="javascript" data-dark-theme="true"><code>[,,]</code></pre>
<p id="ud3aa14f2" class="ne-p"><span class="ne-text">接下来该走路了。</span></p>
<p id="u4303ad3a" class="ne-p"><span class="ne-text">有的同学可能肃然起敬,这个我熟,for循环,把路线中的每个点都赋值给英雄就好了。</span></p>
<p id="u3dcbeef4" class="ne-p"><span class="ne-text">好吧,你又中招了,for循环让整个寻路失去了意义</span></p>
<pre id="F2hnU" class="ne-codeblock language-javascript highlighter-hljs" data-language="javascript" data-dark-theme="true"><code>//错误演示
const routes = findPath(stage, start.value, end.value);
// routes=[,,] 示例结构,非真实代码
routes.forEach((v) =&gt; {
    const = v;
    hero.value.x = x;
    hero.value.y = y;
});</code></pre>
<h2 id="Ow8BP"><span class="ne-text">看效果</span></h2>
<p id="uc73ec97c" class="ne-p"><span class="ne-text">可以看到,都是点哪里去哪里,全程闪现</span></p>
<p id="u70a92e5a" class="ne-p"><img src="https://img2024.cnblogs.com/blog/1049707/202504/1049707-20250423115640763-2123411952.gif" height="343" width="315"></p>
<p id="u5e6d9aaa" class="ne-p"><span class="ne-text">核心原因是for计算没有延迟,中间的确经过了我们之前得到的路径,但由于太快,我们看不到</span></p>
<p id="u72944cc8" class="ne-p"><span class="ne-text">没有延迟,制造延迟,用定时器模拟for循环</span></p>
<p id="udc3c6116" class="ne-p"><span class="ne-text">创造一个变量i=0,相当于for的累加</span></p>
<p id="uf44796a1" class="ne-p"></p>
<h2 id="onNRw"><span class="ne-text">代码</span></h2>
<pre id="HtW6a" class="ne-codeblock language-javascript highlighter-hljs" data-language="javascript" data-dark-theme="true"><code>// 我们需要移动的目标英雄
const hero = ref({
x: 0,
y: 0,
});
const move = () =&gt; {
let i = 0;
//我们通过上面的函数最终找到的路线
const routes = findPath(stage, start.value, end.value);
//如果没找到,就不执行了
if (!routes || routes.length === 0) {
    return;
}
//多次点击,先清空上一次执行的定时器
clearInterval(flag);

flag = setInterval(() =&gt; {
    if (routes.length &gt; 0 &amp;&amp; i++ &lt; routes.length - 1) {
      const = routes;
      hero.value.x = x;
      hero.value.y = y;
    } else {
      start.value = [...end.value];
      i = 0;
      clearInterval(flag);
    }
}, 50);
};</code></pre>
<h1 id="Tq4q3"><span class="ne-text">完整代码</span></h1>
<p id="ud517ae16" class="ne-p"><span class="ne-text">用vue实现,复制粘贴即可运行</span></p>
<pre id="ieC3z" class="ne-codeblock language-javascript highlighter-hljs" data-language="javascript" data-dark-theme="true"><code>&lt;template&gt;
&lt;div class="parent"&gt;
    &lt;div class="grid"&gt;
      &lt;div v-for="(item, k) in stage" :key="k" class="flex"&gt;
      &lt;div
          v-for="(m, n) in item"
          :key="n"
          class="square"
          @click="setEnd(n, k)"
          :class=""
      &gt;&lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;

    &lt;div
      class="hero"
      :style="{
      left: `${hero.x * 100}px`,
      top: `${hero.y * 100}px`,
      }"
    &gt;&lt;/div&gt;
&lt;/div&gt;
&lt;/template&gt;

&lt;script setup lang="ts"&gt;
import { ref } from 'vue';

let flag: NodeJS.Timeout | undefined = undefined;

const stage = [
,
,
,
,
,
,
,
,
];

const hero = ref({
x: 0,
y: 0,
});
const end = ref([-1, -1]);
const start = ref();
const setEnd = (x: number, y: number) =&gt; {
end.value = ;
move();
};
const DIRECTIONS = [
,
,
[-1, 0],
,
];
/**
* @description 寻找路径
* @param grid 二维数组
* @param start 起始点
* @param end 终点
* @returns 路径数组
*/
const findPath = (grid: number[][], start: number[], end: number[]) =&gt; {
const = end;
/* 三维数组 */
const queue = [];

if (grid === 1) {
    return [];
}
const visited = new Set&lt;string&gt;();
visited.add(`${start},${start}`);

while (queue.length &gt; 0) {
    /* 取出最后一个加入的坐标 */
    const lastRoute = queue.shift();
    if (lastRoute) {
      /* 取出新的坐标 */
      const route = lastRoute;
      /* 新的坐标的x,y,用于判断是否为终点 */
      const = route;
      if (x === endX &amp;&amp; y === endY) {
      /* 如果是终点则返回 */
      return lastRoute;
      }
      /* 继续根据方向寻找 */
      for (const of DIRECTIONS) {
      /* 新的 */
      const newX = dx + x;
      const newY = dy + y;

      const newRoute = [...lastRoute, ];
      /* 如果找到坐标,就别找了 */
      if (newX === endX &amp;&amp; newY === endY) {
          return newRoute;
      }
      /* 检测边界 */
      if (collisionDetection(grid, x, y, !visited.has(`${newX},${newY}`))) {
          visited.add(`${x},${y}`);
          //将新的坐标和旧的队列一并加入到路线中
          queue.push(newRoute);
      }
      }
    }
}
return [];
};

/**
*@description 边界检测
* @param stage 二维数组
* @param x 英雄横坐标
* @param y 英雄纵坐标
* @param alreadyPassed 是否已经过
*/
const collisionDetection = (
stage: number[][],
x: number,
y: number,
alreadyPassed: boolean
) =&gt; {
const MAX_X = stage.length - 1; /* 右边 */
const MIN_X = 0 /* 左边 */,
    MIN_Y = 0; /* 上边 */
const MAX_Y = stage.length - 1; /* 下边 */

return (
    x &lt;= MAX_X &amp;&amp;
    x &gt;= MIN_X &amp;&amp;
    y &gt;= MIN_Y &amp;&amp;
    y &lt;= MAX_Y &amp;&amp;
    stage !== 1 &amp;&amp;
    alreadyPassed
);
};

const move = () =&gt; {
let i = 0;
const routes = findPath(stage, start.value, end.value);

if (!routes || routes.length === 0) {
    return;
}
clearInterval(flag);
flag = setInterval(() =&gt; {
    if (routes.length &gt; 0 &amp;&amp; i++ &lt; routes.length - 1) {
      const = routes;
      hero.value.x = x;
      hero.value.y = y;
    } else {
      start.value = [...end.value];
      i = 0;
      clearInterval(flag);
    }
}, 50);
};
&lt;/script&gt;

&lt;style scoped lang="scss"&gt;
.parent {
--wh: 100px;
position: relative;
.square {
    border: 1px solid #000;
    width: var(--wh);
    height: var(--wh);
}
.hero {
    position: absolute;
    text-align: center;
    width: var(--wh);
    height: var(--wh);
    background-color: red;
}
.black {
    background-color: #000;
}
}
.grid {
display: grid;
}
.flex {
display: flex;
}
&lt;/style&gt;</code></pre>
<p id="uc9c8720d" class="ne-p"></p>
</div><br><br>
来源:https://www.cnblogs.com/blogin/p/18842499
頁: [1]
查看完整版本: 最好懂的自动寻路-前端