JS/HTML5游戏常用算法之路径搜索算法 随机迷宫算法详解【普里姆算法】


Posted in Javascript onDecember 13, 2018

本文实例讲述了JS/HTML5游戏常用算法之路径搜索算法 随机迷宫算法。分享给大家供大家参考,具体如下:

路径搜索算法在游戏中非常常见,特别是在 RPG、SLG 中经常用到。在这些游戏中,通过鼠标指定行走目的地,人物或者NPC就会自动行走到目标地点,这就是通过路径搜索或者称为寻路算法来实现的。通俗地说,就是在一张地图中,如何让主角自动行走到指定的地点,如图6-21所示,假设主角在A处,然后玩家在地图中点击B处,要求主角能够从A点自动找寻一条到 B 点的路径,然后自动移动到 B处,要求就这么简单。

JS/HTML5游戏常用算法之路径搜索算法 随机迷宫算法详解【普里姆算法】

在前面的碰撞检测算法中,我们提到,现在的游戏中的地图一般采用格子的方式,虽然表面地图上无法看到实际的格子,但在地图的结构中专门有一个逻辑层,这个层和地图大小等大,划分出很多小的格子,然后在可以通过的地方使用0表示,有障碍的且不能通过的地方使用 1 或其他数字表示。如下图所示,左边的游戏中的地图,程序中会以右边的一个二维数组保存一个逻辑层,专门用来设定障碍。有了这个逻辑层之后,实际上自动寻路就转化成了,如何在一个二维的数组中找到一条从逻辑值为 0 的地点移动到目标地点的路径。

JS/HTML5游戏常用算法之路径搜索算法 随机迷宫算法详解【普里姆算法】

在介绍如何使用自动寻路算法前,我们先来看另外一个游戏常用的算法,即随机产生地图(迷宫)算法,用于结合寻路算法。

【随机迷宫算法】

根据前面的地图的理论,本质上,地图的障碍逻辑层是由一个二维数组保存,障碍标记在二维数组中的数据值以0或1表示,我们需要做的就是随机产生这个二维的数组。当然,最简单的办法就是循环这个二维数组然后在每一个位置随机地产生 0 或者 1,但这种算法产生的图形比较难看,并且不一定保证图中的任意两点可以相连通。

(1)下图所示为一个6×6的迷宫,先假设迷宫中所有的通路都是完全封闭的,白色的格子表示可以通过,黑色的表示墙壁,表示无法通过。

JS/HTML5游戏常用算法之路径搜索算法 随机迷宫算法详解【普里姆算法】

(2)随机选择一个白色的格子作为当前正在访问的格子,同时,把该格子放进一个表示已经访问的列表。

(3)循环以下操作直到所有的格子都被访问。

  • 得到当前访问格子四周(上、下、左、右)的格子,在这些格子中随机选择一个没有在访问列表中的格子,如果找到,则把该格子和当前访问格子中间的墙"打通"置0,把该格子作为当前访问的格子,并放入访问列表。
  • 如果周围所有的格子都已访问过,则从已访问列表中随机选取一个作为当前访问的格子。

通过以上的迷宫生成算法,可以生成一个自然随机的迷宫。

下面的代码根据以上的算法将产生一个R行N列大小的迷宫,需要注意的是R行表示的是刚开始空白格子的行数,由于要算上墙壁的数据,最终产生二维数组实际上的的行数为2R+1,列数为2N+1:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=0">
  <meta charset="UTF-8">
  <title>01_随机迷宫算法</title>
  <style>
    #stage {
      border: 1px solid lightgray;
    }
    .rebuild{
      width:160px;
      height:40px;
      line-height: 40px;
      text-align: center;
      background-color:#000000;
      color:#fff;
      font-size: 24px;
      margin-bottom: 20px;
      cursor: pointer;
    }
  </style>
</head>
<body>
<div class="rebuild">点击更新</div>
<canvas id="stage"></canvas>
</body>
<script>
  window.onload = function () {
    var stage = document.querySelector('#stage'),
      ctx = stage.getContext('2d');
    stage.width = 600;
    stage.height = 600;
    //取区域随机数x>=min && x<max
    function randInt(min,max)
    {
      max=max||0;
      min=min||0;
      var step=Math.abs(max-min);
      var st = (arguments.length<2)?0:min;//参数只有一个的时候,st = 0;
      var result ;
      result = st+(Math.ceil(Math.random()*step))-1;
      return result;
    }
    //普里姆算法生成连通图的二维数组
    // row 行 column 列
    function primMaze(r, c) {
      //初始化数组
      function init(r, c) {
        var a = new Array(2 * r + 1);
        //全部置1
        for (let i = 0, len = a.length; i < len; i++) {
          var cols = 2 * c + 1;
          a[i] = new Array(cols);
          for(let j=0,len1=a[i].length;j<len1;j++)
          {
            a[i][j]=1;
          }
        }
        //中间格子为0
        for (let i = 0; i < r; i++)
          for (let j = 0; j < c; j++) {
            a[2 * i + 1][2 * j + 1] = 0;
          }
        return a;
      }
      //处理数组,产生最终的数组
      function process(arr) {
        //acc存放已访问队列,noacc存放没有访问队列
        var acc = [], noacc = [];
        var r = arr.length >> 1, c = arr[0].length >> 1;
        var count = r * c;
        for (var i = 0; i < count; i++) {
          noacc[i] = 0;
        }
        //定义空单元上下左右偏移
        var offs = [-c, c, -1, 1], offR = [-1, 1, 0, 0], offC = [0, 0, -1, 1];
        //随机从noacc取出一个位置
        var pos = randInt(count);
        noacc[pos] = 1;
        acc.push(pos);
        while (acc.length < count) {
          var ls = -1, offPos = -1;
          offPos = -1;
          //找出pos位置在二维数组中的坐标
          var pr = pos / c | 0, pc = pos % c, co = 0, o = 0;
          //随机取上下左右四个单元
          while (++co < 5) {
            o = randInt(0, 5);
            ls = offs[o] + pos;
            var tpr = pr + offR[o];
            var tpc = pc + offC[o];
            if (tpr >= 0 && tpc >= 0 && tpr <= r - 1 && tpc <= c - 1 && noacc[ls] == 0) {
              offPos = o;
              break;
            }
          }
          if (offPos < 0) {
            pos = acc[randInt(acc.length)];
          }
          else {
            pr = 2 * pr + 1;
            pc = 2 * pc + 1;
            //相邻空单元中间的位置置0
            arr[pr + offR[offPos]][pc + offC[offPos]] = 0;
            pos = ls;
            noacc[pos] = 1;
            acc.push(pos);
          }
        }
      }
      var a = init(r, c);
      process(a);
      return a;
      //返回一个二维数组,行的数据为2r+1个,列的数据为2c+1个
    }
    //栅格线条
    function drawGrid(context, color, stepx, stepy) {
      context.strokeStyle = color;
      context.lineWidth = 0.5;
      for (var i = stepx + 0.5; i < context.canvas.width; i += stepx) {
        context.beginPath();
        context.moveTo(i, 0);
        context.lineTo(i, context.canvas.height);
        context.stroke();
      }
      for (var i = stepy + 0.5; i < context.canvas.height; i += stepy) {
        context.beginPath();
        context.moveTo(0, i);
        context.lineTo(context.canvas.width, i);
        context.stroke();
      }
    }
    function createRect(x, y, r, c) {
      ctx.beginPath();
      ctx.fillStyle = c;
      ctx.rect(x, y, r, r);
      ctx.fill();
    }
    function update() {
      ctx.clearRect(0, 0, 600, 600);
      drawGrid(ctx, 'lightgray', 40, 40);
      var mapArr = primMaze(7,7);
      console.log(mapArr);
      //根据地图二维数组创建色块
      for (var i = 0, len = mapArr.length; i < len; i++) {
        for (var j = 0, len1 = mapArr[i].length; j < len1; j++) {
          if (mapArr[i][j]) {
            createRect(i * 40, j * 40, 40, "black");
          }
        }
      }
    }
    update();
    document.querySelector('.rebuild').addEventListener('click', update);
  };
</script>
</html>

这里使用在线HTML/CSS/JavaScript代码运行工具:http://tools.3water.com/code/HtmlJsRun 测试上述代码运行效果如下:

JS/HTML5游戏常用算法之路径搜索算法 随机迷宫算法详解【普里姆算法】

github地址:https://github.com/krapnikkk/JS-gameMathematics

希望本文所述对大家JavaScript程序设计有所帮助。

Javascript 相关文章推荐
javascript xml为数据源的下拉框控件
Jul 07 Javascript
Jquery Ajax学习实例 向页面发出请求,返回XML格式数据
Mar 14 Javascript
用Javascript实现Sleep暂停功能代码
Sep 03 Javascript
基于jQuery的input输入框下拉提示层(自动邮箱后缀名)
Jun 14 Javascript
jquery DIV撑大让滚动条滚到最底部代码
Jun 06 Javascript
多个jQuery版本共存的处理方案
Mar 17 Javascript
jQuery的层级查找方式分析
Jun 16 Javascript
在vue里面设置全局变量或数据的方法
Mar 09 Javascript
jQuery添加新内容的四个常用方法分析【append,prepend,after,before】
Mar 19 jQuery
原生js实现随机点名功能
Nov 05 Javascript
javascript解析json格式的数据方法详解
Aug 07 Javascript
vue组件实现移动端九宫格转盘抽奖
Oct 16 Javascript
JS/HTML5游戏常用算法之碰撞检测 包围盒检测算法详解【凹多边形的分离轴检测算法】
Dec 13 #Javascript
JS/HTML5游戏常用算法之碰撞检测 包围盒检测算法详解【矩形情况】
Dec 13 #Javascript
详解Express笔记之动态渲染HTML(新手入坑)
Dec 13 #Javascript
js实现黑白div块画空心的图形
Dec 13 #Javascript
JS/HTML5游戏常用算法之碰撞检测 包围盒检测算法详解【圆形情况】
Dec 13 #Javascript
示例vue 的keep-alive缓存功能的实现
Dec 13 #Javascript
Element UI框架中巧用树选择器的实现
Dec 12 #Javascript
You might like
5.PHP的其他功能
2006/10/09 PHP
浅析ThinkPHP中的pathinfo模式和URL重写
2014/01/06 PHP
不使用php api函数实现数组的交换排序示例
2014/04/13 PHP
php验证码的制作思路和实现方法
2015/11/12 PHP
ThinkPHP中where()使用方法详解
2016/04/19 PHP
PHP编程快速实现数组去重的方法详解
2017/07/22 PHP
PHP实现搜索时记住状态的方法示例
2018/05/11 PHP
Struts2的s:radio标签使用及用jquery添加change事件
2013/04/08 Javascript
基于socket.io和node.js搭建即时通信系统
2014/07/30 Javascript
jQuery学习笔记之创建DOM元素
2015/01/19 Javascript
使用jQuery操作HTML的table表格的实例解析
2016/03/13 Javascript
js流动式效果显示当前系统时间
2016/05/16 Javascript
深入理解JavaScript中的call、apply、bind方法的区别
2016/05/30 Javascript
d3.js中冷门却实用的内置函数总结
2017/02/04 Javascript
[01:02:25]2014 DOTA2华西杯精英邀请赛5 24 NewBee VS VG
2014/05/25 DOTA
[01:03]DOTA2新的征程 你的脚印值得踏上
2014/08/13 DOTA
[01:48]完美圣典齐天大圣至宝宣传片
2016/12/17 DOTA
python计算牛顿迭代多项式实例分析
2015/05/07 Python
详解使用python的logging模块在stdout输出的两种方法
2017/05/17 Python
python3写爬取B站视频弹幕功能
2017/12/22 Python
python解析含有重复key的json方法
2019/01/22 Python
使用Python调取任意数字资产钱包余额功能
2019/08/15 Python
python3 Scrapy爬虫框架ip代理配置的方法
2020/01/17 Python
Python约瑟夫生者死者小游戏实例讲解
2021/01/04 Python
python快速安装OpenCV的步骤记录
2021/02/22 Python
CSS3 Flex 弹性布局实例代码详解
2018/11/01 HTML / CSS
浅析HTML5中的 History 模式
2017/06/22 HTML / CSS
台湾前三大B2C购物网站:MOMO购物网
2017/04/27 全球购物
Allsole美国/加拿大:英国一家专门出售品牌鞋子的网站
2018/10/21 全球购物
俄罗斯家居用品购物网站:Евродом
2020/11/21 全球购物
电钳工人个人求职信
2014/05/10 职场文书
警察正风肃纪剖析材料
2014/10/16 职场文书
2014年终个人工作总结
2014/11/07 职场文书
解除劳动关系协议书2篇
2014/11/28 职场文书
婚宴新娘致辞
2015/07/28 职场文书
Android超详细讲解组件ScrollView的使用
2022/03/31 Java/Android