在游戏开发中程序化生成地图一直是个很有吸引力的方向。无论是战棋游戏、模拟经营还是 Roguelike 玩法程序化生成都能极大地提升内容的复用率和随机性。本文我将围绕一个具体目标展开使用 Summer Engine基于 Godot 引擎的工具框架结合 Codex从零搭建一个能自动生成六边形地块的 Demo。你可能已经对 Godot 有所耳闻也可能正在尝试把 AI 辅助编程引入工作流。本文会把两件事都讲透一方面拆解六边形地块生成的核心数学和 GDScript 实现另一方面演示如何借助 Codex 快速生成代码片段、定位报错并给出常见的踩坑方案。无论是零基础想入门 Godot还是想尝试 AI 辅助开发的进阶读者这份教程都很合适。1. 背景与核心概念在开始写代码之前有必要先把几个关键词理解清楚。这里面的概念并不复杂但搞清楚了再动手后面会顺畅很多。1.1 什么是六边形地块六边形地块简单来说就是用正六边形作为地图的最小单元。相比常见的正方形网格六边形的每个格子有 6 个相邻格子中心到所有邻居中心的距离完全一致不存在正方形网格中对角线距离大于边距的问题。这让移动、寻路、攻击范围计算都变得更直观。六边形网格在游戏里并不少见经典的三国志系列、文明系列、以及大量独立战棋游戏都采用六边形地块。它的优点可以总结为三点邻居距离统一计算简单。视觉上交错排布更自然适合表现地形起伏。每块土地边缘共享地图拼接更紧密。在程序化生成领域六边形网格还可以配合噪声函数生成高低起伏的地形是很多地编工具的底层基础。本文我们会从一个简化的方向入手生成一个平面六边形网格但后续你可以很容易扩展出高度、地块类型等逻辑。1.2 Summer Engine 和 Godot 是什么关系Summer Engine 可以理解为一套围绕 Godot 封装的游戏开发学习框架。它本身并不是另一个引擎而是把 Godot 的项目结构、常用工具链和启动模板整合起来让你可以更快地进入一个相对规范的项目流中。如果你之前没接触过 Summer Engine也没关系。本文的绝大部分代码在标准 Godot 4 中也能直接运行。我们使用 Summer Engine 的初衷是希望项目在起步时就有清晰的目录划分方便后续扩展。你完全可以把它当作一个 Godot 项目模板来看待。如果你使用的是标准版 Godot请确保版本在 4.0 以上因为 GDScript 2.0 的语法和部分节点接口在 4.x 才全面支持。版本差异会在后面专门说明。1.3 程序化生成解决什么问题程序化生成Procedural Generation指通过算法生成游戏内容而不是手工一张张绘制地图或摆放资源。它带来的好处非常明显节省美术资源不需要逐个绘制地图块地表。增加游戏重复可玩性每次开局的关卡都可能完全不同。降低存储体积算法生成的场景不需要存储大量预制体。方便动态调整修改参数即可生成不同风格的地块。当然程序化生成也不是银弹。它需要你投入时间设计算法规则生成结果也需要做合理性校验。对新手来说从简单的六边形地块开始是一个非常合适的练手项目。1.4 Codex 能帮我们做什么Codex 是 OpenAI 推出的编程辅助工具提供命令行交互界面可以在终端里直接向它提问、让它写代码、让它解释现有代码、甚至让它帮你排查报错。在本文的演示里我们会使用 Codex 来做三件事根据描述生成六边形顶点计算函数。生成网格遍历与地块实例化代码。帮助分析运行时的报错。需要特别注意的是AI 生成的代码一定要人工复核。Codex 并不知道你项目里所有上下文也不了解你自定义类的内部细节因此生成结果可能只作为参考原型不保证直接可用。我们用 AI 提高效率的边界是“它帮我写好 80% 的基础结构我来做 20% 的集成和优化”。2. 环境准备与版本说明在动手前先把开发环境准备好。本文示例以常见环境为例重点演示配置思路具体版本请根据你的项目实际情况调整。2.1 操作系统与工具以下环境均可以支持本文演示操作系统Windows 10/11、macOS 或 Linux。Godot 引擎推荐使用 Godot 4.2 及以上稳定版本文代码基于 GDScript 2.0 编写。Summer Engine如果你了解其安装方式可以直接使用不了解也无妨按标准 Godot 项目创建即可。代码编辑器Godot 内置编辑器即可也可以外接 VS Code配合 GDScript 插件。Codex安装并登录 Codex CLI用于辅助生成代码。2.2 Godot 版本差异说明Godot 3.x 和 4.x 的 GDScript 语法有较大差异。例如var类型推断、get_node简化、TileMap节点结构等都发生了变化。如果你使用的是 Godot 3.x部分代码可能需要调整。本文所有代码均以 Godot 4.x 为准。如果你尚未安装 Godot 4可以前往好耶的官网下载对应的稳定版本。由于网络环境变化较快这里不写死具体下载链接你只需要在搜索引擎中搜索“Godot 4 download 官网”进入官方页面找到对应系统安装包即可。2.3 安装 Codex CLICodex CLI 是一个命令行工具。安装前你需要确保已经具备 Node.js 和 npm 环境。你可以在终端中执行以下命令检查node -v npm -v如果尚未安装 Node.js请先安装 Node.js 的 LTS 版本再继续下面的步骤。安装 Codex CLI 的通用做法是使用 npm 全局安装npm install -g openai/codex安装完成后登录账号codex login登录完成后可以在终端中直接向 Codex 提问codex exec 用 GDScript 写一个函数计算平顶六边形的六个顶点坐标这里需要说明的是Codex 的安装方式和登录流程可能会随版本更新而变化。如果你已经安装了 Codex却发现codex命令无法执行可以先检查是否真的安装了 CLI以及系统环境变量是否配置正确。后面常见问题部分我会专门解释一条高频报错信息。3. 核心原理拆解在写完整项目前先把六边形网格的原理和 Godot 中的节点结构搞清楚。这部分理解透了代码就是水到渠成的事。3.1 六边形的方向与坐标六边形有两个常见方向尖顶六边形六边形的一条对角线垂直放置形状像一个“竖起来的盾牌”。平顶六边形六边形的一条边在顶部像一块“横向的砖头”。两种六边形的顶点坐标计算方式略有不同但逻辑是相通的。以平顶六边形为例其中心到顶点的距离外接圆半径为size则六个顶点的角度分别为0°、30°、90°、...、330°。在 GDScript 中可以这样计算顶点func get_hex_vertices(center: Vector2, size: float): var vertices PackedVector2Array() for i in range(6): var angle deg_to_rad(60 * i) var vertex center Vector2(cos(angle), sin(angle)) * size vertices.append(vertex) return vertices有人可能会问为什么平顶六边形的角度是0°开始因为平顶六边形的第一个顶点在右侧然后逆时针旋转 60 度依次得到其他顶点。这里并不唯一但只要你确保六个点均匀分布绘制出来的形状就是正六边形。3.2 轴向坐标系统六边形网格有多种坐标系统最常用的是轴向坐标。它使用两个坐标轴q和r对应的第三个隐式坐标s -q - r用来约束网格范围。使用轴向坐标的好处是邻居计算非常规则。每个六边形有 6 个邻居其(q, r)偏移分别为(1, 0), (1, -1), (0, -1), (-1, 0), (-1, 1), (0, 1)在 Godot 里我们通常会把逻辑坐标(q, r)映射为屏幕上的像素坐标。对于平顶六边形像素坐标的计算方式如下func hex_to_pixel(hex: Vector2i, size: float) - Vector2: var q hex.x var r hex.y var x size * sqrt(3.0) * (q r * 0.5) var y size * 1.5 * r return Vector2(x, y)对于尖顶六边形公式会略有不同但原理一致。我们先用平顶六边形做演示方便直观理解。3.3 Godot 中的地块实例化方式在 Godot 2D 中表现六边形地块有几种方式使用Polygon2D节点直接绘制一个六边形多边形简单直观适合小批量生成。使用TileMapLayer节点适合大批量地图性能更优但需要提前准备 TileSet。使用MeshInstance2D或Sprite2D配合美术资源表现更丰富。本文为了把原理讲清楚采用Polygon2D的方式。每个地块是一个独立的Polygon2D节点我们可以控制它的位置、颜色、边框和点击响应。当地块数量不大几百个以内时完全够用而且代码更容易读懂。3.4 随机种子与可控生成程序化生成的一个关键点是可复现性。游戏里如果每次运行地图都完全不同调试将会很困难。所以我们通常使用随机种子seed来控制生成结果。Godot 中可以直接使用全局的seed()函数也可以为随机数生成器单独创建实例var rng RandomNumberGenerator.new() rng.seed 12345设置种子后每次运行生成的随机序列都是相同的。这样你可以稳定复现某种地形也方便分享案例给其他人。4. 完整实战案例现在进入本文的核心环节。我们会创建一个 Godot 项目用 GDScript 编写一个六边形地块生成脚本并让它显示在游戏窗口中。整个流程我会按步骤拆开每一步都附上代码和解释。4.1 创建项目结构打开 Godot 项目管理器新建一个 2D 项目命名为HexGridDemo。创建完成后建议在res://下建立以下目录结构HexGridDemo/ |-- scenes/ | |-- main.tscn |-- scripts/ | |-- main.gd |-- assets/如果使用 Summer Engine通常它已经帮你搭建好了类似的目录结构你只需要找到场景目录和脚本目录即可。4.2 创建主场景在scenes目录下新建一个场景根节点选择Node2D命名为Main。然后为根节点添加脚本scripts/main.gd。这个脚本将负责定义网格大小、地块半径等参数。在_ready中生成六边形地块。绘制每个地块并设置颜色。4.3 编写六边形网格生成代码下面给出完整的main.gd代码。代码中包含了顶点计算、像素坐标转换、地块生成三个部分。# 文件路径scripts/main.gd extends Node2D # 六边形半径中心到顶点距离 export var hex_size: float 40.0 # 网格半径从中心到最外圈的格子数 export var grid_radius: int 5 # 随机种子 export var seed_value: int 20240101 var rng : RandomNumberGenerator.new() func _ready() - void: rng.seed seed_value generate_hex_grid() func generate_hex_grid() - void: for q in range(-grid_radius, grid_radius 1): for r in range(-grid_radius, grid_radius 1): # 第三个坐标 s 用于限制网格为正六边形 var s -q - r if abs(s) grid_radius: continue var hex_coord : Vector2i(q, r) var center : hex_to_pixel(hex_coord, hex_size) var vertices : get_hex_vertices(center, hex_size) var color : get_hex_color(hex_coord) create_hex_polygon(vertices, color) func hex_to_pixel(hex: Vector2i, size: float) - Vector2: var q float(hex.x) var r float(hex.y) var x size * sqrt(3.0) * (q r * 0.5) var y size * 1.5 * r return Vector2(x, y) func get_hex_vertices(center: Vector2, size: float) - PackedVector2Array: var vertices : PackedVector2Array() for i in range(6): var angle deg_to_rad(60 * i) vertices.append(center Vector2(cos(angle), sin(angle)) * size) return vertices func get_hex_color(hex: Vector2i) - Color: var rnd : rng.randf_range(0.0, 1.0) if rnd 0.2: return Color(0.2, 0.6, 0.3) elif rnd 0.6: return Color(0.4, 0.4, 0.3) else: return Color(0.5, 0.5, 0.5) func create_hex_polygon(vertices: PackedVector2Array, color: Color) - void: var polygon : Polygon2D.new() polygon.polygon vertices polygon.color color add_child(polygon)这段代码的关键逻辑如下grid_radius控制地图半径半径越大地块越多。hex_to_pixel将轴向坐标转换为屏幕像素坐标。get_hex_vertices计算每个六边形的顶点。get_hex_color根据随机值决定地块颜色。create_hex_polygon创建Polygon2D节点并添加到场景。运行后的效果就是在窗口中央生成一个明显的六边形蜂窝状网格。4.4 使用 Codex 生成局部代码实际开发中我不会建议你直接手写以上全部代码。你可以先向 Codex 提出需求让 AI 先生成初稿。比如在终端执行codex exec 在 Godot 4 中用 GDScript 写一个函数计算平顶六边形的顶点坐标参数是中心和半径Codex 会返回类似下面这样的代码func get_hex_vertices(center: Vector2, size: float) - PackedVector2Array: var vertices PackedVector2Array() for i in range(6): var angle_deg 60 * i var angle_rad deg_to_rad(angle_deg) var point center Vector2(cos(angle_rad), sin(angle_rad)) * size vertices.append(point) return vertices拿到这个生成结果后你需要核对几个点六边形方向是否符合需求。顶点顺序是否满足绘制要求。变量命名是否清晰。这种先用 AI 生成骨架再人工整合到项目的流程可以大幅节省入门时间。尤其当你对某个引擎 API 不熟悉时通过 AI 快速了解函数用法很高效。需要注意的是AI 生成代码只是起点尤其对 Godot 中PackedVector2Array、Polygon2D这类引擎类型你必须确认类型是否匹配。如果直接把泛型数组传给polygon.polygon运行时会报错。4.5 添加鼠标交互为了让 Demo 更有趣我们再加一个功能鼠标悬停在某个六边形地块上时高亮该地块。这里需要实现从屏幕坐标反向算出六边形坐标的逻辑。为了简化我们可以使用 Godot 内置的碰撞检测。给每个Polygon2D添加一个Area2D或直接监听鼠标位置然后判断点是否在多边形内。更简单的做法是使用Geometry2D.is_point_in_polygon判断鼠标点是否落在当前地块顶点数组内。修改后的部分代码func _physics_process(delta: float) - void: var mouse_pos : get_global_mouse_position() for child in get_children(): if child is Polygon2D: var polygon: Polygon2D child if Geometry2D.is_point_in_polygon(mouse_pos, polygon.polygon): polygon.color Color(1, 0.8, 0.2) else: # 这里需要记录原始颜色否则会覆盖之前的颜色分配 pass这种写法有一个问题当我们改变了地块颜色后原始的随机颜色就丢失了。更好的做法是在每个 Polygon2D 上挂一个脚本或存储原始颜色属性。为了保持 Demo 简洁你可以先不修改原始颜色只高亮悬停地块移开后恢复默认色。这里不展开复杂的状态管理但你可以看到Godot 中节点与几何工具的配合非常方便这让交互逻辑的编写成本变得很低。4.6 运行与验证按F5运行场景。正常情况下你会看到窗口中央出现一个由六边形地块组成的蜂窝状地图不同地块有不同的颜色。如果一切正常你已经完成了第一个基于 Godot 的程序化六边形地块 Demo。如果运行后窗口是空的请检查是否把main.gd脚本挂到了根节点上。是否运行的是正确的场景。窗口大小是否足以显示网格hex_size是否设置过小。5. 常见问题与排查思路开发过程中最容易遇到的就是各种报错和异常表现。这里整理几个与本文主题高度相关的问题并给出排查思路。问题现象常见原因解决思路六边形地块重叠严重hex_to_pixel公式用错或者中心点计算错误对比公式确保使用正确的轴向坐标转像素公式地块之间有明显缝隙hex_size与顶点计算不匹配或 Polygon2D 抗锯齿设置增大hex_size检查顶点数量是否为 6颜色随机但每次运行都相同没有设置 seed 或 seed 固定不变显式设置RandomNumberGenerator.seedCodex 命令无法执行CLI 未安装或路径未配置检查 npm 全局安装目录配置 PATHCodex 提示 unable to locate the codex cli binary程序找不到 codex 可执行文件路径检查是否安装成功重新确认环境变量地块数量巨大时卡顿Polygon2D 节点数量太多改用 TileMapLayer 或减少生成范围5.1 Codex CLI 报错unable to locate the codex cli binary这是一个非常典型的问题。现象是启动 Codex 相关程序时程序提示找不到codex可执行文件。可能的原因有两种Codex CLI 并没有真正安装成功。安装成功了但程序的执行环境没有把 Codex 所在目录加入 PATH。排查步骤如下在终端执行codex --version看命令是否可用。如果命令不可用重新执行npm install -g openai/codex。检查 npm 全局安装路径确认codex文件存在于 bin 目录。配置系统环境变量将 npm 全局 bin 目录加入 PATH。重启终端再测试。这个过程和 Godot 本身无关但当你把 Codex 集成到编辑器插件时插件往往无法正确读取到你终端中的 PATH 配置导致启动失败。这是很多工具链集成时的通病本质上是环境变量传递问题。5.2 六边形地块生成结果不正确如果你运行后看到的不是蜂窝网格而是打乱排列的怪形状请重点检查get_hex_vertices和hex_to_pixel两个函数。常见错误是顶点角度从30°开始导致平顶六边形旋转了方向。另外轴向坐标转像素时sqrt(3.0)和1.5这两个系数是固定搭配不能随意更换。如果你用的是尖顶六边形公式会变成var x size * 1.5 * q var y size * sqrt(3.0) * (r q * 0.5)请根据你自己的六边形方向选择对应的公式。5.3 生成的地块不在窗口中心默认情况下根节点在场景原点(0, 0)即窗口左上角。如果你希望整个网格居中可以在_ready中移动根节点或计算偏移量。最简单的办法是把根节点的position设置为窗口大小的一半func _ready() - void: var viewport_size : get_viewport_rect().size position viewport_size / 2.0 rng.seed seed_value generate_hex_grid()这样可以避免生成的内容全部挤在左上角。6. 最佳实践与工程建议当你把这个 Demo 从“能跑”提升到“能扩展”的时候有几个工程习惯非常值得养成。6.1 分离生成逻辑与渲染逻辑在刚才的代码中我把网格生成、顶点计算、颜色分配全部塞在一个脚本里。这对几百行的 Demo 来说没问题但一旦地图功能变复杂比如要加地形属性、单位寻路、点击交互脚本就会迅速膨胀。更合理的结构是单独写一个HexGrid类负责存储地块数据。单独写一个HexRenderer类负责遍历数据并生成视觉节点。单独写一个HexMath工具类负责坐标转换和邻居计算。这样拆分后你可以在不打开渲染节点的情况下测试坐标算法也可以在不改动数据层的情况下替换渲染表现。6.2 使用种子保证可复现性程序化生成项目必须养成传递种子的习惯。调试时使用固定种子可以复现问题上线时可以随机种子保证每次体验不同。建议在项目中定义一个全局配置用来控制默认种子、地图半径、地块尺寸等参数。这样后续调整会很方便也让代码的可读性更高。6.3 性能优化思路Polygon2D节点方便但并非没有代价。每个节点都会带来额外的场景树开销。如果你要生成 10000 个地块直接用节点分发显然不够明智。这时应该转向TileMapLayer或自定义MultiMesh方案。Godot 4 的 TileMap 系统已经相当成熟你可以提前准备好几套地块纹理然后按地块类型铺到 TileMap 上。这样渲染性能会提升几个量级也方便后续添加图块动画。如果你的游戏是 3D 视角也可以考虑把地块做成MeshInstance3D并使用 GridMap 节点。6.4 善用 AI 但保持代码审查习惯Codex 这类工具确实能提升编码效率尤其是在你不熟悉某个库或语言的时候。但它也有局限性它不了解你项目里自定义脚本和节点的具体关系。它可能使用旧版 API需要你调试匹配版本。它生成的逻辑可能正确但不一定符合你的架构设计。所以我的建议是把它当成一个“能力很强的结对程序员”而不是“可以完全托付的自动化代码生成器”。每次 AI 生成代码后至少要保证自己能够读懂每一行再合入项目。7. 总结与学习路线这篇教程中我们从零做了一件事在 Godot 中程序化生成六边形地块。核心知识点包括平顶六边形的顶点计算、轴向坐标到像素坐标的转换、随机种子的使用以及用 Polygon2D 生成可见网格。同时我们也演示了如何借助 Codex 快速生成代码片段并解决了一个常见的 CLI 启动问题。如果你顺利完成了 Demo下一步可以尝试这些方向给地块增加高度值通过颜色深浅表现海拔。实现鼠标点击选中地块并显示相邻地块的调试信息。把 Polygon2D 替换成自定义 TileSet让地块拥有更丰富的纹理。结合FastNoiseLite生成更自然的地形噪声再据此分配地块类型。用 Codex 继续扩展逻辑例如实现 A* 寻路前需要的地图邻接数据结构。程序化生成的世界很大六边形地块只是第一块砖。当你掌握了网格的数学基础后再去看一些开源地图生成项目会发现很多东西都能看懂了。如果这篇文章对你有帮助可以收藏备用。也欢迎你在评论区留言说说你在 Godot 中做程序化生成时踩过的坑。