You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

如何实现Voronoi图边缘包裹以生成程序化幻想地图?

实现Voronoi图东西向边缘包裹的方案

嘿,这个问题我做程序化幻想地图时踩过类似的坑!要实现Voronoi图的东西向边缘包裹(像地球那样左右衔接),核心是把地图当成环形空间处理,不用硬加边界点——给你一套实用的方案,适配你的Poisson采样和AS3环境:

1. 生成采样点的左右镜像副本

你现在用makePoints()生成Poisson采样点,不需要加预定义边界点,而是给靠近左右边缘的点生成镜像点,让Voronoi算法认为地图左右是连通的。

假设你的地图宽为mapWidth,高为mapHeight,Poisson采样半径为poissonRadius,适配AS3的伪代码:

// 先获取原始Poisson采样点(你的第236行函数)
var originalPoints:Array = makePoints();
var wrappedPoints:Array = originalPoints.concat(); // 复制原始点到新数组

// 边缘阈值:用采样半径的2倍,避免镜像点和原始点冲突
var edgeThreshold:Number = poissonRadius * 2;

for each (var p:Point in originalPoints) {
    // 左侧边缘的点,生成右侧镜像(x += mapWidth)
    if (p.x < edgeThreshold) {
        wrappedPoints.push(new Point(p.x + mapWidth, p.y));
    }
    // 右侧边缘的点,生成左侧镜像(x -= mapWidth)
    if (p.x > mapWidth - edgeThreshold) {
        wrappedPoints.push(new Point(p.x - mapWidth, p.y));
    }
}

2. 用环形距离替代欧几里得距离

普通Voronoi算法用欧几里得距离计算点的远近,这会导致左右边缘的点互不关联。我们需要修改距离计算逻辑,让x方向的距离取地图左右两侧的最短路径

环形距离公式(伪代码):

function getCircularDistance(p1:Point, p2:Point, mapWidth:Number):Number {
    var dx:Number = Math.abs(p2.x - p1.x);
    // x方向取最短路径:要么直接差值,要么绕地图边缘的差值
    var wrappedDx:Number = Math.min(dx, mapWidth - dx);
    var dy:Number = p2.y - p1.y;
    return Math.sqrt(wrappedDx * wrappedDx + dy * dy);
}

如果你用的是第三方Voronoi库,需要找到库中计算距离的函数,替换成这个环形距离;如果是自己实现的Bowyer-Watson算法,就在判断点属于哪个单元格时用这个距离。

3. 折叠跨边缘的单元格顶点

生成Voronoi单元格后,有些单元格会因为镜像点的存在,顶点超出原始地图范围(x < 0 或 x > mapWidth)。这时需要把这些顶点“折叠”回地图内:

for each (var cell:VoronoiCell in voronoiCells) {
    for each (var vertex:Point in cell.vertices) {
        // 右侧超出的顶点,减去地图宽度回到左侧
        if (vertex.x > mapWidth) {
            vertex.x -= mapWidth;
        }
        // 左侧超出的顶点,加上地图宽度回到右侧
        if (vertex.x < 0) {
            vertex.x += mapWidth;
        }
    }
}

这样处理后,跨边缘的单元格会在左右两侧同时显示,看起来就像是从右侧延伸到左侧,完美实现东西向包裹。

额外优化建议

  • 只给靠近边缘的点生成镜像:不用给所有点都做镜像,只处理x在0~2*poissonRadiusmapWidth-2*poissonRadius~mapWidth范围内的点,减少计算量
  • 测试时缩小地图宽度:方便快速观察左右边缘的衔接效果,确认逻辑正确后再放大
  • 板块比例不受影响:30%陆地/70%海洋的设定,在生成单元格后用随机或海拔逻辑分配即可,包裹逻辑不会干扰这个比例

内容的提问来源于stack exchange,提问作者Olin Kirkland

火山引擎 最新活动