YIXUNFE / blog

文章区
151 stars 25 forks source link

用 JS 制作随机地图(二) #47

Open ajccom opened 8 years ago

ajccom commented 8 years ago

用 JS 制作随机地图(二)

上一节我们讲述了用 Voronoi 图 对空间进行划分得到一个网格图。这一节我们讲讲如何对这个网格图进行填充,区分出地图中的陆地与海洋。


海拔与 Perlin Noise

在我们的世界中,利用一个区域不同的海拔可以确定地形高低,从而划分高山、平原与海洋,所以我们需要为地图中的各个区域设置一个海拔值,这样我们就可以标记区域的地形。


Perlin Noise

由于是随机地图,地图中的地形也应该是随机的。但是这个随机是有一定的要求的。比如地图的相邻区域的海拔不应该跳跃的很大,因为这一般不符合我们现实中观察到的规律。所以这里我们使用 Perlin Noise(柏林噪声)算法生成海拔图。

Perlin Noise 是指由 Ken Perlin 发明的一种自然噪声生成算法,可以用来模拟自然界中的噪声现象。由于它的连续性,如果将二维噪声中的一个轴作为时间轴,得到的就是一个连续变化的一维函数。同样的也可以得到连续变化的二维图像。该噪声可以用来模拟人体的随机运动,蚂蚁行进的线路等。另外,还可以通过计算分形和模拟云朵,火焰等非常复杂的纹理。

perlin

Perlin Noise 图像


地图元素对象

利用 Perlin Noise 图像中的变化连续性,我们就可以给地图设置海拔了。为了后续步骤的顺利,在这一步之前我们可以先定义好角、边、重心与多边形对象。

//添加角对象
function add (point) {
  //防止在集合器中添加重复的角对象
  var str = point[0] + '-' + point[1]
  if (!corners[str]) {
    corners[str] = {
      point: point,
      elevation: _getElevation(point) //通过角对象的坐标获取海拔
    }
  }
  return corners[str]
}

一个角对象或者重心对象获取其海拔值,就是该点坐标在 Perlin Noise 图像中对应点的像素值获得。由于我们生成的是黑白(即 RGB 三色值相同)的 Perlin Noise 图像,所以仅获取 RGBA 中的第一个值即可。这种方法同时还能为我们提供海拔的范围(0 - 255 之间),方便我们后续定义海拔值与地形之间的关系。

//通过像素获取海拔值
function _getElevation (point) {
  return perlinNoiseData[Math.floor(point[0] * 4 + point[1] * width * 4)]
}


绘制地形

目前我们以地图中的多边形为单元绘制地形。首先我们需要通过多边形对象的重心和多个角的海拔值确定多边形的海拔值。

var arr = []
arr.push(polygon.centers[0].elevation)
polygon.corners.map(function (corner) {
  arr.push(corner.elevation / 2)
})
polygon.elevation = _getLandDesc(arr)

我们先看下以海拔值作为填充色的地图:

p1

左图是以海拔值作填充色的地图,右图是海拔值数据源


利用海拔确定陆地与海洋

在确定各个区域的海拔值之后,我们需要设置一个阈值,区分陆地与海洋。比如我们设置 64 为阈值,海拔值小于 64 的区域为海洋,反之为陆地。

lao

颜色区分后的陆地和海洋


总结

这节我们介绍了 Perlin Noise 以及利用 Perlin Noise 生成的图像给地图各区域确定海拔值。在确定海拔值之后可以设置一个阈值区分地图中的陆地与海洋。

第二节 DEMO 在这里

下一节我们讲讲怎么画出地图上的湖泊、河流以及确定各个区域的湿度。


参考文章

http://www-cs-students.stanford.edu/~amitp/game-programming/polygon-map-generation/ https://en.wikipedia.org/wiki/Perlin_noise