简介hexgen 是一套基于 Python 3 的六角形世界地图生成器面向游戏开发者、独立制作人与程序化生成爱好者。它通过参数化六边形网格生成随机行星表面并能按需将地图分割为任意大小的“领土”既可用于浏览器游戏的原型验证也能为 DnD 跑团快速生成战役底图。压缩包共 34 个文件、548KB代码以 Python 脚本为主20 个 .py并配有说明文档、依赖清单、单元测试与文档生成脚本目录结构清晰便于快速安装与二次开发。内容上涵盖河流、地形高度、气候、边界、领土等生成模块示例脚本可一键演示默认效果调整参数即可得到风格各异的世界地图。从源码中还能看到六边形网格建模、随机地形与气候叠加、领土划分等常见程序化生成思路配合测试用例和文档可以加深理解已有 1547 人学习下载适合希望系统掌握六边形地图生成算法或快速搭建随机地图工具的开发者参考。1. hexgen把六角格世界的随机生成变成一条命令的 Python 工具某开发者在做策略游戏原型时需要一张六边形格子组成的行政地图上百个格子手动摆位置、调坐标一个下午耗进去最后导出的图还全是锯齿缝隙。后来他改用了一个 Python 小工具 hexgen指定尺寸和随机种子就能在几秒内输出一张带地形起伏、海岸线、植被分布的世界地图 PNG。这个项目解决的核心问题不是“画得漂亮”而是把六边形网格系统、噪声地形生成、生物群落分配这三块地图生成最繁琐的环节封装成一条可复现的流程。对做回合制策略、跑团战役地图、地理模拟实验的开发者来说hexgen 是一个可以直接拿来改的起点。下文从坐标算法讲到渲染输出拆清楚每一步怎么算、怎么调以及实际踩过的坑。2. 技术选型与整体架构从噪声采样到六角格渲染的完整链路2.1 技术栈拆解坐标系统、噪声、渲染三件套hexgen 的技术栈非常收敛没有引入重型游戏引擎或地理信息系统库核心依赖只有 numpy、PillowPIL和 opensimplex 三个。numpy 负责把高度图数据组织成二维数组方便做向量化运算和阈值筛选opensimplex 提供 Simplex 噪声生成比传统 Perlin 噪声在二维网格上产生的伪随机更自然方向性伪影更少Pillow 负责最终把六边形格子绘制成 PNG 图片。选择 Simplex 噪声而不是单纯用随机数是因为地图需要“连续起伏”的地形——相邻格子之间高度变化要有相关性否则会得到一块雪花噪点一样的电视雪花图。opensimplex 这个库体积小、接口简单seed 可控非常适合这种脚本型项目。渲染层我一般建议用 Pillow 而不是 matplotlib前者能精确控制每个像素和多边形填充输出尺寸不受窗口限制后者更适合做数据分析可视化画地图容易出现边框、坐标轴残留还得额外关闭。从架构角度看hexgen 刻意把“生成”和“渲染”分离噪声生成只输出数值矩阵不关心地图长什么样渲染模块只负责把数值变成颜色和多边形不关心数值怎么来的。这样替换任意一个环节都不会影响其他模块。2.2 目录结构与模块职责hexgen 的项目结构很直白解开压缩包之后你会看到一个典型的 Python 包布局。每个模块职责单一方便二次开发时替换逻辑。hexgen/ ├── main.py # 命令行入口解析参数并串联全流程 ├── generator/ │ ├── __init__.py │ ├── hexgrid.py # 六边形网格轴向坐标定义、邻接与距离计算 │ ├── noise.py # Simplex 噪声高度图生成与归一化 │ ├── climate.py # 温度、湿度模型与生物群落分配 │ └── render.py # 坐标转换、六边形绘制、颜色映射 ├── requirements.txt └── output/ # 生成结果默认输出目录main.py 是整个流程的编排者它读取用户传入的地图宽度、高度、随机种子、噪声缩放比例等参数依次调用 generator 包里的函数最后把渲染结果写入 output 目录。generator 包内部不感知命令行参数只接收明确的数据对象这样如果你想把它嵌进 Flask 接口或者游戏服务里只要调用对应的函数就行。这种模块划分还有一个好处调试时可以单独跑某个模块。比如怀疑噪声生成有问题就直接调用 noise.generate_heightmap 打印矩阵内容不用从头跑一遍完整流程。2.3 数据流从四个参数到一张 PNG 的完整链路整个生成过程是一条单向数据流没有回环依赖。当你执行 python main.py --width 80 --height 50 --seed 2024发生的事情可以拆成五步。第一步main.py 使用 argparse 解析命令行参数得到地图列数 width、行数 height、随机种子 seed、噪声缩放 scale。第二步noise.py 根据 seed 和 scale 生成一张 width x height 的高度浮点矩阵值域在 -1.0 到 1.0 之间。第三步climate.py 结合纬度信息与湿度噪声生成温度矩阵和湿度矩阵再按高度、温度、湿度三个维度给每个格子分配生物群落类型比如海洋、沙漠、草原、森林、雪原。第四步hexgrid.py 基于 width 和 height 构建所有六边形格子的轴向坐标q, r并计算相邻关系。之所以把坐标构建放在高度图之后是因为网格的尺寸直接由 width 和 height 决定但计算本身并不依赖高度值这一步其实可以提前放到后面是为了让 main.py 的调用顺序更接近“先算内容再建格子”的直觉。第五步render.py 把每个格子的轴向坐标转换为屏幕像素坐标确定中心点再按六边形半径计算六个顶点用对应生物群落的颜色填充最终保存为 PNG。整个链路里最容易出问题的是坐标转换时把 q 和 r 弄反或者忘记处理六边形网格特有的“偏移”对齐方式。这个问题后面避坑章节会专门讲。3. 核心模块拆解从坐标计算到植被覆盖的五个关键实现3.1 坐标系统选型为什么用轴向坐标而不是 Offset 坐标六边形网格有三种常见坐标表示Offset 坐标offset coordinates、轴向坐标axial coordinates、立方体坐标cube coordinates。Offset 坐标最接近人眼观察表格的行列但邻接计算需要区分奇偶行容易写错分支立方体坐标的数学公式最漂亮却多用一个变量浪费内存。hexgen 选用轴向坐标用 (q, r) 两个整数表示一个六边形。q 表示列方向r 表示斜向行。轴向坐标本质上是立方体坐标去掉第三个轴 s因为它满足 q r s 0所以 s 可以由 q 和 r 推导出来。这样既保留了立方体坐标的简洁公式又只需要存两个数。# generator/hexgrid.py AXIAL_DIRECTIONS [ (1, 0), (1, -1), (0, -1), (-1, 0), (-1, 1), (0, 1) ] def axial_neighbor(coord, direction_index): 返回当前格子在指定方向上的邻居坐标 q, r coord dq, dr AXIAL_DIRECTIONS[direction_index] return (q dq, r dr) def hex_distance(a, b): 计算两个轴向坐标之间的距离 dq a[0] - b[0] dr a[1] - b[1] ds -dq - dr return max(abs(dq), abs(dr), abs(ds))这里的关键是 AXIAL_DIRECTIONS 数组的顺序我按逆时针方向排列六个方向顺序固定后就不会出现方向混淆。hex_distance 函数用立方体坐标的切比雪夫距离公式对轴向坐标同样适用因为 s 轴已经隐含在 q r 的关系里。参数说明里有一个容易忽略的点邻居坐标的 direction_index 如果越界函数不会报错会导致静默错误。我实际开发时会加一个 assert 校验这里为了代码精简没写二次开发时建议补上。对比 Offset 坐标轴向坐标的最大优势是算法对称性高。距离计算、寻路、视野计算都能直接用简洁公式而 Offset 坐标需要做奇偶判断。缺点是渲染时不能直接用行列号定位必须经过一次数学换算这个换算在渲染模块处理。3.2 网格构建生成全部六边形格子并建立邻接表有了坐标系统就需要根据地图宽高生成所有有效的六边形坐标。这里用轴向坐标时宽高定义的是菱形的边界而不是矩形边界。# generator/hexgrid.py def build_grid(width, height): 生成 width x height 大小的六边形网格坐标集合 cells [] for q in range(width): q_offset q // 2 for r in range(-q_offset, height - q_offset): cells.append((q, r)) return cells def build_adjacency(cells): 为每个格子建立邻居映射返回 dict cell_set set(cells) adjacency {} for cell in cells: neighbors [] for direction in AXIAL_DIRECTIONS: neighbor (cell[0] direction[0], cell[1] direction[1]) if neighbor in cell_set: neighbors.append(neighbor) adjacency[cell] neighbors return adjacencybuild_grid 的思路是让每一列 q 在 r 方向产生错位q_offset 等于 q 整除 2这样形成的菱形地图在视觉上左右两侧是对称的倒三角边界边缘格子缺失的形状刚好被地图边框裁掉。这种菱形布局在地图生成中最常见因为所有格子都在同一个矩形参数空间里数据矩阵和网格坐标一一对应没有空洞。build_adjacency 返回的是一个 dict键是格子坐标值是邻居坐标列表。用 set 判断邻居是否存在于地图中时间复杂度 O(1)。这里有个性能细节如果地图尺寸很大比如 200x200格子数四万每个格子查六次集合总共也就二十四万次查询耗时非常低不需要额外优化。开发时要注意build_grid 生成的是“坐标集合”而不是“有序数组”。如果后面需要按某种顺序遍历格子比如从地图左上角开始渲染必须额外排序因为循环中的生成顺序并不等于视觉上的行优先顺序。我的做法是在渲染模块里用像素坐标做排序。3.3 高度图生成用 Simplex 噪声模拟连续地形地形生成的核心是让相邻格子的高度相关联。直接给每个格子赋随机数会造成马赛克状的地形完全没有山脉和盆地的感觉。Simplex 噪声是一种连续噪声函数输入二维坐标输出平滑变化的数值。# generator/noise.py import opensimplex import numpy as np def generate_heightmap(width, height, scale10.0, seed42): 生成归一化到[-1, 1]区间的高度图矩阵 opensimplex.seed(seed) heightmap np.zeros((height, width)) for y in range(height): for x in range(width): # 将像素坐标除以 scale让采样点在噪声场中平滑过渡 nx x / scale ny y / scale heightmap[y][x] opensimplex.noise2(nx, ny) return heightmap这段代码有三个关键参数。scale 是噪声缩放比例值越大采样点之间的间隔越小地形起伏越平缓值越小地形越破碎。我一般用 8 到 12 之间的值这样能保证地图上既有大块大陆轮廓又有局部山地细节。seed 是随机种子opensimplex.seed 后全局噪声场固定同一个 seed 必然生成同一个高度图。这里有一个新手容易踩的坑opensimplex.seed 是全局的如果你在循环里多次调用 generate_heightmap 生成不同区域每次都要重置 seed否则第二次生成的噪声会接着上一次的流导致两张图看似不同实则相关。默认返回的矩阵值域是 -1 到 1 之间后续生物群落判断需要高度阈值。比如海平面以下设为 -0.05那低于这个值的格子就标记为海洋。实际生成时你会发现地图边缘和中心的分布并不均匀因为这本质上是随机噪声不会天然形成“四周海洋中间大陆”的格局。一个巧妙做法是叠加径向渐变让中心区域高度略增边缘略降这样就能生成类似真实星球的地貌。这个进阶技巧在二次开发里会说。3.4 生物群落分配温度、湿度与高度的三重判断有了高度图还不能直接决定格子颜色因为同样是陆地赤道附近可能是雨林极地附近可能是雪原。hexgen 简化了真实气候模型用纬度估算温度用第二张噪声图模拟湿度再结合高度查表分配群落。# generator/climate.py import opensimplex import numpy as np def generate_climate(heightmap, width, height, scale10.0, seed42): 生成温度矩阵和湿度矩阵 opensimplex.seed(seed 100) temp np.zeros((height, width)) hum np.zeros((height, width)) for y in range(height): # 纬度系数中间行温度高上下两端温度低模拟赤道到极地 lat_factor 1.0 - abs(y - height / 2) / (height / 2) for x in range(width): temp[y][x] lat_factor 0.3 * opensimplex.noise2(x / scale, y / scale) hum[y][x] opensimplex.noise2(x / scale 5, y / scale 5) 0.5 return temp, hum def assign_biome(height, temperature, humidity, sea_level-0.05): 根据高度、温度、湿度返回生物群落名称 if height sea_level: return ocean if height 0.55: return mountain if humidity 0.3: return desert if temperature 0.2: return tundra if humidity 0.7: return forest return grasslandtemperature 矩阵由纬度和噪声叠加而成lat_factor 让地图上下两端冷、中间热0.3 乘噪声是让温度有一些不规则波动。humidity 矩阵则是把噪声值加 0.5把值域从 -1 到 1 平移成 0 到 1.5方便用阈值切分。assign_biome 的阈值顺序很有讲究先判断海洋和山脉再判断其它陆地类型。因为山脉高度值很高即使温度湿度条件可能像沙漠也应该优先显示为山脉。如果你把沙漠判断放在山脉前面海拔极高的地区会被画成沙漠视觉上就会出现“空中沙漠”的反直觉效果。这个优先级问题是我第一次跑这个项目时翻车最严重的地方。3.5 渲染输出把轴向坐标转换成屏幕像素并绘制多边形渲染模块是整个项目里最容易出现视觉 bug 的地方。必须把轴向坐标 (q, r) 转换成屏幕上的 x, y 像素坐标然后以中心点绘制出正六边形。# generator/render.py import math from PIL import Image, ImageDraw def axial_to_pixel(coord, size, origin): 轴向坐标转像素坐标size 是六边形半径 q, r coord x size * math.sqrt(3) * (q r / 2) origin[0] y size * 1.5 * r origin[1] return (x, y) def draw_hex_grid(cells, biome_map, size, image_size, color_map): 为每个格子绘制填充多边形 img Image.new(RGB, image_size, (20, 30, 40)) draw ImageDraw.Draw(img) for cell in cells: center axial_to_pixel(cell, size, (size, size)) points [] for i in range(6): angle math.pi / 6 i * math.pi / 3 px center[0] size * math.cos(angle) py center[1] size * math.sin(angle) points.append((px, py)) draw.polygon(points, fillcolor_map[biome_map[cell]]) return imgaxial_to_pixel 的公式里x 方向要乘以 sqrt(3)因为六边形水平方向的实际宽度是半径的两倍乘以根号三y 方向每行递增 1.5 倍半径这对应六边形纵向的“行高”。origin 参数是左上角偏移量避免边缘格子绘制到画布外。draw_hex_grid 中angle 从 math.pi / 6 开始是为了让六边形有一个“平顶”的边正对着上方而不是尖顶。这不是必须的但平顶六边形在视觉上更适合表现地形因为相邻格子垂直方向对齐更自然。如果你想要尖顶六边形把起始角度改成 0 即可同时邻接方向表也要相应旋转。注意这段代码是逐格子创建多边形再填充格子里没有缝隙是因为相邻六边形的顶点共用同一套公式计算不会出现浮点误差累积。但如果你把 size 设置得过大格子会超出画布边缘最好预先根据地图宽高计算出 image_size 和合适的 size。常见做法是让 size 等于 ceil(image_width / (width * sqrt(3)))确保整张地图恰好铺满画布。4. 运行与调参从环境准备到性能优化的完整流程4.1 环境准备与依赖安装在跑 hexgen 之前先准备好 Python 3.8 以上的环境。项目依赖很简单用 pip 就能装齐。pip install numpy pillow opensimplex如果你使用的是虚拟环境建议先创建并激活环境再安装避免把依赖装到系统全局 Python 里。我一般会顺手创建一个 requirements.txt 以上的内容已经在压缩包里带了实际执行时直接用 pip install -r requirements.txt 更省事。装好依赖后在项目根目录执行一次简单生成验证环境没问题。python main.py --width 40 --height 30 --seed 128 --output demo.png这行命令会生成一张 40 列 30 行的六边形地图如果一切正常output 目录下会出现 demo.png 文件。打开图片能看到蓝色海洋、绿色草原、灰白色山脉的色块。如果这一步就出问题优先检查依赖是否安装完整尤其是 opensimplex 这个库在部分 Python 版本上可能没被正确编译卸载重装基本能解决。4.2 命令行参数详解hexgen 的命令行参数不多但每个参数对生成效果的影响都是决定性的。参数类型默认值说明--widthint80地图列数格子数--heightint50地图行数格子数--seedint42随机种子决定噪声场形状--scalefloat10.0噪声缩放比例越大地形越平缓--sea-levelfloat-0.05海平面高度阈值调大变海洋变少--outputstroutput/map.png输出 PNG 文件路径width 和 height 控制的是“格子数量”而不是“像素尺寸”输出图片的像素尺寸会自动根据格子数量计算。seed 参数非常关键同一个 seed 必然生成完全一样的地图这是实现地图场景复现的基础。scale 参数直接影响地形特征调小比如到 5地图会出现密集的岛屿和破碎陆地调大到 20则会生成大面积连成一片的大陆。sea-level 是我后来加的参数默认 -0.05 是因为 Simplex 噪声的均值在 0 附近稍微降低一点阈值可以让海洋面积占比略高于陆地看起来更接近真实星球。如果你想把地图变成全陆地直接设成 -0.9 即可。4.3 常见运行报错排查我实际跑这个项目的过程中遇到过几个非常典型的报错这里单独说一下排查思路因为这些问题在二次开发时几乎必现。第一种是 ModuleNotFoundError: No module named opensimplex。这通常是因为当前 Python 环境和安装依赖的环境不是同一个。解决方案是检查 pip list 里有没有 opensimplex没有就重装装了还找不到就检查虚拟环境是否激活。第二种是运行正常但输出的 PNG 是一片纯色。这种情况九成是 sea-level 设置太高导致所有格子全部被判定为海洋。可以用 --sea-level -1.0 跑一次如果出现陆地就能确认是阈值问题。第三种是报错 TypeError: NoneType object is not iterable这通常发生在 render.py 里遍历 biome_map 时。biome_map 是一个 dict如果代码在循环里访问了不存在的键比如某个格子没有通过 assign_biome 赋值就会取到 None。排查方式是打印 heightmap 里是否有 NaN 值opensimplex 在某些极端参数下可能返回 NaN污染了后续判断。4.4 性能优化大尺寸地图的生成策略当 width 和 height 超过 150 时纯 Python 的双循环开始变得吃力。generate_heightmap 里两层 for 循环逐个采样噪声是最大的瓶颈因为 opensimplex.noise2 是单点调用无法直接向量化。一个常见优化是使用 numpy 的 meshgrid 预生成坐标矩阵然后用向量化函数去填充循环。opensimplex 官方提供了 OpenSimplex 类的 vectorized 方法可以接收 numpy 数组批量生成噪声值。改造后的核心逻辑如下。# generator/noise.py 向量化版本 def generate_heightmap_fast(width, height, scale10.0, seed42): opensimplex.seed(seed) x np.arange(width) / scale y np.arange(height) / scale X, Y np.meshgrid(x, y) heightmap opensimplex.noise2array(X, Y) return heightmapnoise2array 是 opensimplex 提供的批量接口直接输入二维坐标数组输出同形状的噪声矩阵。测试下来在 200x200 的地图上这个版本比双循环快接近一个数量级渲染反而成了新的瓶颈。渲染优化的思路是减少 Pillow 绘制调用的数量。ImageDraw.Draw.polygon 对每个格子调用一次在画面格子数超过两万时会有明显卡顿。常见做法是把相同生物群落的格子合并成一个大区域再绘制这需要做连通区域合并实现复杂度会增加不少。如果只是追求快速预览可以降低图片尺寸让每个格子半径变小渲染时间会线性下降。我认为这个项目在 100x100 以下的地图不需要过度优化超过这个规模再考虑向量化和区域合并方案。5. 避坑指南六角格地图生成器开发中的五个真实翻车现场5.1 坐标轴颠倒导致整张地图水平镜像现象生成的地图看起来左右对称但仔细对比会发现自己期望的“西海岸”出现在东边整个大陆布局被镜像。原因在 axial_to_pixel 函数里x 坐标计算用了 (r q / 2) 而不是 (q r / 2)。轴向坐标的齐次偏移方向被我搞反导致所有格子沿垂直轴对称翻转。解决严格按照公式 x size * sqrt(3) * (q r / 2) 书写然后拿一个已知格子比如 (0, 0) 和 (1, 0) 的手算结果对比确保 (1, 0) 在 (0, 0) 的右上方而不是右下方。从那以后我每次改完坐标转换都先跑一个 3x3 小地图用不同颜色标记角落格子肉眼确认几何关系正确再继续。5.2 opensimplex.seed 被二次调用导致两张图高度相关现象在同一个进程里连续生成两张地图第一张是大陆第二张也几乎一模一样只是细节略有抖动。原因opensimplex 的 seed 设置是全局单例状态。第二次调用 generate_heightmap 时没有重置 seed而噪声生成是从上次的状态继续推进的导致第二次的高度场只是第一次的高度场平移了一块区域。解决每次生成高度图或湿度图之前都显式调用 opensimplex.seed 并且使用不同的种子偏移值比如主 seed 是 42温度图用 142湿度图用 242。这个方法也被我用在循环生成多张预览图时确保每张图互不相同。5.3 生物群落出现“空中沙漠”和“冰封雨林”现象高海拔地区画成黄色沙漠赤道附近的高湿度区域画成白色雪原乍一看非常违和。原因assign_biome 的阈值判断顺序不合理。当时我把 desert 判断放在 mountain 之前高海拔低温但湿度正常的地方先命中 desert又把 tundra 判断放在 forest 之前导致高湿度低温的赤道高山被判定为 tundra。解决把 assign_biome 改成先判断高度和海平面再判断山脉然后按温度湿度组合判断平原类型。调整顺序后效果符合直觉山顶是灰色靠近两极是雪原赤道低海拔高湿度是森林。这之后我养成了习惯凡是多重条件的分支判断先把互斥的强约束条件放在前面。5.4 六边形之间出现透明缝隙现象输出 PNG 里每个六边形周围有一圈很细的背景色线条看起来像是拼贴画没对齐。原因Pillow 的 polygon 绘制使用浮点坐标会做抗锯齿处理相邻多边形的共享边如果两边独立计算顶点浮点舍入误差可能导致 0.5 像素的缝隙。另外 image_size 计算偏小边缘格子被裁切出边界。解决把共享边的顶点计算统一到同一套 axial_to_pixel 和顶点生成函数里确保相邻格子交点坐标完全一致同时 image_size 在计算时额外加两个 size 的边距避免边缘裁切。对于缝隙最简单的视觉修正是在画布背景色上先铺一层和海洋相同的颜色这样即使有一点缝隙也不显眼。5.5 固定 seed 却无法复现同一张地图现象用同一个 seed 在同事的电脑上跑生成的地图和我这边不一样生物群落分布有明显差异。原因numpy 版本不同会影响某些浮点运算的微小结果但更主要的是不同平台下 opensimplex 的浮点实现可能有差异。另一个隐蔽原因是字典遍历顺序Python 3.7 之后 dict 保序这不是问题但在渲染时如果 biome_map 是普通 dict 而 cells 是 set遍历顺序不稳定导致绘制顺序不一致。解决将 cells 从 set 改为排序列表按 (q, r) 先排序再传给渲染函数。生成的数值矩阵用 np.save 保存一份作为复现基准后续迭代时对比偏差。这个坑让我意识到所谓“可复现”不仅取决于 seed还取决于代码的确定性遍历顺序、浮点库版本都要锁定。6. 二次开发把 hexgen 改造成更懂你的地图工具6.1 增加大陆与湖泊的随机扰动现版本的生物群落完全依赖噪声和阈值自然生成的大陆往往形状不规则。但如果你想要一个“中心大陆加环形海洋”的世界可以在高度图上叠加一个径向渐变。# 在 generate_heightmap 返回前叠加岛屿形状 dx x - width / 2 dy y - height / 2 dist math.sqrt(dx * dx dy * dy) / (width / 2) heightmap[y][x] heightmap[y][x] * (1 - dist) 0.2 * (1 - dist)这个扰动让地图边缘高度下降中心高度抬升噪声加入的细节仍然保留但整体轮廓变成类似大陆漂移后的形状。湖泊生成同理可以在 assign_biome 前把局部高度调整为低于海平面的值。6.2 输出 SVG 适配矢量编辑如果你的目标是让美术同学在矢量软件里二次编辑地图可以在 render.py 里追加一个 svg 导出函数。把生成多边形顶点坐标的循环搬进文本拼接逻辑每个格子输出一个 polygon 标签颜色值用生物群落对应的十六进制码。SVG 文件不需要像素渲染也不会有圆角缝隙问题而且地图每个格子都是独立对象方便后期改色和删除。我实际做的时候会同时输出 PNG 和 SVG 两份结果PNG 用于快速预览SVG 用于交付编辑。需要注意 SVG 文件体积会比 PNG 大不少尤其是格子数量超过五千时启动编辑器会有些卡顿。6.3 参数化批量生成多地图方案对比做玩法测试时往往需要一次生成几十张候选地图然后人工挑选。我在 main.py 外面包了一层批量脚本传入一个种子列表循环调用 hexgen 的生成函数把每张地图存成以 seed 命名的文件。同时生成一个 index.html 把所有图片平铺展示方便对比不同种子下的地形质量。这个小技巧让我从手动跑几十条命令中解脱出来也统一了输出命名规范。过程中最深的体会是地图生成项目的价值不在于单张图有多好看而在于能不能稳定复现、快速迭代。从那以后我每次改动核心算法都强制跑一遍固定 seed 的地图对比新旧版本差异确认没有破坏既有结果再做下一步。希望帮到你如果按这篇笔记跑通了试着把 sea-level 参数降到 -0.8看看你生成的第一片大陆长什么样。本文还有配套的精品资源点击获取