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 不只是脚本
May 30 Javascript
不安全的常用的js写法
Sep 15 Javascript
基本jquery的控制tabs打开的数量的代码
Oct 17 Javascript
LABjs、RequireJS、SeaJS的区别
Mar 04 Javascript
JavaScript在浏览器标题栏上显示当前日期和时间的方法
Mar 19 Javascript
Bootstrap组件(一)之菜单
May 11 Javascript
JavaScript中的this引用(推荐)
Aug 05 Javascript
微信小程序 删除项目工程实现步骤
Nov 10 Javascript
Vue.js学习笔记之修饰符详解
Jul 25 Javascript
常用的9个JavaScript图表库详解
Dec 19 Javascript
vue.js实现的幻灯片功能示例
Jan 18 Javascript
关于Node.js中频繁修改代码重启服务器的问题
Oct 15 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
PHP+AJAX实现无刷新注册(带用户名实时检测)
2007/01/02 PHP
php循环检测目录是否存在并创建(循环创建目录)
2011/01/06 PHP
php记录代码执行时间(实现代码)
2013/07/05 PHP
摘自织梦CMS的HTTP文件下载类
2015/08/08 PHP
ThinkPHP框架安全实现分析
2016/03/14 PHP
PHP的简单跳转提示的实现详解
2019/03/14 PHP
js 字符串操作函数
2009/07/25 Javascript
javascript模拟select,jselect的方法实现
2012/11/08 Javascript
实现局部遮罩与关闭原理及代码
2013/02/04 Javascript
jquery左边浮动到一定位置时显示返回顶部按钮
2014/06/05 Javascript
Javascript基础知识(二)事件
2014/09/29 Javascript
JavaScript基础教程之alert弹出提示框实例
2014/10/16 Javascript
JS+CSS实现简单滑动门(滑动菜单)效果
2015/09/19 Javascript
js带闹铃功能的倒计时代码
2016/09/29 Javascript
概述jQuery的元素筛选
2016/11/23 Javascript
bootstrap选项卡使用方法解析
2017/01/11 Javascript
JavaScript条件判断_动力节点Java学院整理
2017/06/26 Javascript
微信小程序wx.previewImage预览图片实例详解
2017/12/07 Javascript
基于substring()和substr()的使用以及区别(实例讲解)
2017/12/28 Javascript
JavaScript设计模式之命令模式实例分析
2019/01/16 Javascript
React实现全选功能
2020/08/25 Javascript
JQuery基于FormData异步提交数据文件
2020/09/01 jQuery
python选择排序算法的实现代码
2013/11/21 Python
python解析中国天气网的天气数据
2014/03/21 Python
Python中%是什么意思?python中百分号如何使用?
2018/03/20 Python
Random 在 Python 中的使用方法
2018/08/09 Python
Python多进程池 multiprocessing Pool用法示例
2018/09/07 Python
python自带tkinter库实现棋盘覆盖图形界面
2019/07/17 Python
DataFrame 数据合并实现(merge,join,concat)
2020/06/14 Python
Python reques接口测试框架实现代码
2020/07/28 Python
Python学习之time模块的基本使用
2021/01/17 Python
突袭HTML5之Javascript API扩展4—拖拽(Drag/Drop)概述
2013/01/31 HTML / CSS
Waterford美国官网:爱尔兰水晶制品品牌
2017/04/26 全球购物
大学生党课思想汇报
2013/12/29 职场文书
golang 如何通过反射创建新对象
2021/04/28 Golang
MySQL 如何分析查询性能
2021/05/12 MySQL