尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

OpenFrontIO MapGenerator 实战指南:从 PNG 像素到游戏地图文件

OpenFrontIO MapGenerator 实战指南:从 PNG 像素到游戏地图文件 游戏开发后端【免费下载链接】OpenFrontIOOnline browser-based RTS game项目地址https://gitcode.com/gh_mirrors/op/OpenFrontIO点击查看免费下载导读本文档系统讲解 OpenFrontIO 仓库中的地图生成工具MapGenerator——一个用 Go 编写、把 PNG 图像像素转换为游戏地图二进制文件的命令行工具。你将掌握从安装依赖、准备image.png与info.json、运行生成器到理解manifest.json、map.bin、Maps.gen.ts等全部输出产物的完整流程并深入像素→地形映射、二进制位编码、迷你地图降采样等底层实现原理具备独立为 OpenFrontIO 创建新地图并接入游戏的能力。工具定位与核心设计MapGenerator 是 OpenFrontIO 游戏一款在线浏览器 RTS 游戏的地图内容生产线。它读取 PNG 图片将每个像素转换为一个地形瓦片最终打包成游戏引擎直接加载的二进制文件与元数据。其核心设计是地形类型主要取决于像素的 Blue蓝色通道值Red/Green 通道不参与判定。正因如此灰度图与其他任意格式的图片都能被完整支持——仓库中 map-generator/assets/maps/ 下的大多数地图源文件如world、fourislands等都是灰度 PNG。这大大降低了制图门槛制作者不需要精心调色只需用灰度明暗表达海拔即可。工具本身的目录结构如下map-generator/ ├── main.go # CLI 入口flag 解析、地图发现、并发调度 ├── map_generator.go # 核心像素解码、地形分类、迷你地图、二进制打包 ├── codegen.go # 生成 src/core/game/Maps.gen.ts 与 en.json 地图段 ├── logger.go # 基于 slog 的自定义分级日志 ├── prettier.go # 调用 npx prettier 格式化所有生成文件 ├── go.mod / go.sum └── assets/ ├── maps/map_name/ # 地图源资产image.png info.json可加图层 PNG └── test_maps/ # 测试地图资产环境准备与安装生成器是标准 Go 程序安装步骤安装 Go 环境参考 Go 官方安装文档仓库go.mod以当前 Go 版本为基准。进入生成器目录cd map-generator拉取依赖go mod download运行生成器处理全部地图go run .说明go run .只安装生成器自身的依赖github.com/gen2brain/webp等。最后一步 Prettier 格式化依赖 Node 侧依赖若尚未执行过npm ci格式化会跳过并打印警告详见下文“自动格式化”一节。创建一张新地图标准工作流地图是自动发现的只要在assets/maps/下新建一个文件夹并放入对应文件生成器就会自动处理无需任何注册步骤。info.json承载了游戏需要的全部地图元信息。完整的新地图创建流程在 map-generator/assets/maps/ 下新建文件夹assets/maps/map_name放入地图源图片assets/maps/map_name/image.png编写assets/maps/map_name/info.json字段详解见后文只生成这一张图go run . --mapsmap_name示例只生成 Four Islandsgo run . --mapsfourislands批量生成请传入逗号分隔列表go run . --mapsnorthamerica,world不带--maps时默认处理全部已定义地图包括assets/test_maps/中的测试地图其输出写入 tests/testdata/maps/。在输出目录查看结果resources/maps/map_name/。从源码看地图发现逻辑在 main.go 的discoverMaps()中实现它遍历assets/maps与assets/test_maps两个目录将每个子目录注册为一个mapEntry。也就是说“加一张地图 加一个含image.png和info.json的文件夹”。--maps参数会在 parseMapsFlag() 中被校验传入的每个名字必须存在于已发现的地图注册表否则直接报错map xxx is not defined。因此拼写错误会被立即拦截而不是生成空结果。并发与内存控制源码中还有一个 README 未展开说明但值得注意的参数--workers默认 4。loadTerrainMaps()main.go通过信号量sem : make(chan struct{}, workersFlag)限制同时处理的地图数量降低峰值内存占用。处理大图如 200 万像素级时若内存紧张可以调低该值go run . --workers2命令行参数与日志系统完整参数一览参数说明示例--maps可选逗号分隔的地图列表缺省处理全部go run . --mapsworld,eastasia--workers并发处理的地图数默认 4用于压低峰值内存go run . --workers2--log-level显式设置日志级别ALL/DEBUG/INFO默认/WARN/ERRORgo run . --log-leveldebug--verbose/-v增加日志输出并以[mapname]前缀标注来源等价于--log-levelDEBUGgo run . -v--log-performance额外输出性能建议类日志同时把级别设为DEBUGgo run . --log-performance--log-removal额外输出被移除小岛/小湖的位置与大小同时把级别设为DEBUGgo run . --log-removal注意一处文档与实现的差异README 中写作--debug-performance/--debug-removal而 main.go 实际注册的 flag 是--log-performance/--log-removal分别绑定logFlags.performance与logFlags.removal。以源码为准使用后者。日志体系基于 Go 标准库log/slog并在 logger.go 中做了定制自定义了LevelAll slog.Level(-8)低于标准DEBUG级别用于输出所有日志性能与移除日志通过 slog 属性标签PerformanceLogTag、RemovalLogTag标记默认不输出——即使--log-levelDEBUG也不会显示必须显式传入--log-performance/--log-removal才可见设置--log-levelALL时全部日志无条件输出无视上述标签过滤在DEBUG与ALL级别下日志会带有[mapname]前缀便于多图并发时区分来源见 logger.go测试地图assets/test_maps的性能日志会被静默丢弃避免测试噪音logger.go。制作 image.png像素到地形的映射规则生成器在 GenerateMap() 中完成解码与分类。对每个像素读取 RGBA 后按以下规则映射该表即源码 map_generator.go 中的权威文档输入条件地形类型Magnitude强度说明Alpha 20Water水距最近陆地的距离透明像素视为水Blue 106Water水距最近陆地的距离指定的“水”专属色值纯黑#000000alpha ≥ 20Impassable不可通行31固定实体虚空见下文Blue 140Land平原0钳制到最小强度Blue 140–158Land平原0–9按(Blue-140)/2计算Blue 159–178Land高地10–19同上公式Blue 179–200Land山地20–30同上公式Blue 200Land山地30钳制到最大强度实现细节map_generator.go像素的 16 位 RGBA 值先右移 8 位转为 8 位blue 106是预留给水的关键色纯黑三通道均为 0在 alpha ≥ 20 时判为不可通行。陆地 Magnitude 的计算为(clamp(blue, 140, 200) - 140) / 2落在 0–30 区间。Magnitude 的语义Land 的 Magnitude 海拔0–9 平原、10–19 高地、20–30 山地直接决定游戏中地形的视觉表现Water 的 Magnitude 到岸距离由 processDistToLand() 用 BFS广度优先搜索从所有岸线水体出发向四周扩散计算曼哈顿距离即 Magnitude。离岸越远水越深渲染越暗。另外紧邻不可通行地形的海水会被强制设为深水强度 20setImpassableNeighborWaterDepth()——因为不可通行地形如同地图边缘一样是“虚空”其周边水体不该出现浅水渐变否则视觉上会有一圈奇怪的亮色。不可通行地形Impassable Terrain纯黑像素#000000/rgb(0,0,0)alpha ≥ 20被编码为不可通行地形是一种实心的静态虚空不能被占领、攻击或核弹打击核弹弹道无法穿越与地图边界行为一致渲染为地图背景色使地图呈现非矩形轮廓。用途雕琢非矩形地图形状或在无水的区域之间制造天然屏障。它在二进制中被编码为isLand1 magnitude31详见下文“二进制格式”。地图尺寸归一化与自动清理生成器会自动处理两张源图的两个“脏数据”问题尺寸归一化宽高被裁剪为 4 的倍数width - (width % 4)、height - (height % 4)超出部分像素被丢弃这是迷你地图降采样整除的需要map_generator.go小地块清理面积小于30 像素的岛屿自动移除面积小于200 像素的水体湖泊自动移除。移除逻辑见 removeSmallIslands() 与 processWater()通过 BFS 找出连通块小地块按“邻居多数类型”转换majorityNeighborType()平局时优先级 Water Impassable Land。移除前后均可通过--log-removal查看每个被删地块的位置与大小。注意测试地图不做小岛清理RemoveSmall: !isTest见 main.go以保证测试数据确定性。性能建议源图与生成结果建议控制在以下范围常量定义于 map_generator.go源图面积建议200 万300 万像素宽×高全尺寸二进制中的陆地瓦片数不建议超过 300 万常见地图的陆地瓦片数平均在100 万200 万。超出范围时生成器只打印 DEBUG 级性能提示需要--log-performance才可见并不会阻止生成。编写 info.json地图元数据生成器读取assets/maps/map_name/info.json用于确定国家位置、出生坐标、旗帜、分类与其余元数据。完整示例{ id: MySampleMap, name: My Sample Map, translation_key: map.mysamplemap, categories: [europe, featured], multiplayer_frequency: 4, featured_rank: 5, layers: [ { id: roads, placement: land, nukeable: true } ], nations: [ { coordinates: [396, 364], name: United States, flag: us }, { coordinates: [512, 280], name: Canada, flag: ca } ] }仓库中可参考的真实示例fourislands含team_frequency、special_team_count、带概率的forced_modifiers、多国配置、venice含 15 国与themes、china含layers定义。字段总表字段必填说明id✅地图的CamelCaseName须与assets/maps/map_name文件夹名一致大小写不敏感会成为GameMapType枚举键name✅规范名称即GameMapType枚举值。一旦发布就绝不能更改它属于线上传输格式并存入游戏对局记录translation_key✅resources/lang/en.json 中的键必须为map.foldernamecategories✅一个或多个分类字符串见下不得重复至少一项用于地图选择器分组multiplayer_frequency地图在公共多人匹配轮换中出现的频次0或缺省表示不进轮换作为各模式频次未设置时的回退值ffa_frequencyFFA 大厅轮换权重覆盖multiplayer_frequency0表示 FFA 中排除省略则沿用前者team_frequency组队大厅轮换权重覆盖multiplayer_frequency0表示组队中排除省略则沿用前者special_frequencySpecial 大厅轮换权重覆盖multiplayer_frequency0表示 Special 中排除省略则沿用前者disabled_modifiers特殊对局中绝不为该地图掷出的修饰器键数组如[isRandomSpawn]forced_modifiers特殊对局中强制开启的修饰器数组纯键如startingGold5M或key:percentage如goldMultiplier:75表示 75% 概率计入 3 修饰器上限display_name写入 resources/lang/en.json 的英文显示名默认取name仅当显示名与规范名不同时设置如MENA、Europe (Classic)featured_rank精选网格中的位置1为第一要求categories含featured未排名的精选地图按字母序排在已排名之后special_team_count组队/特殊对局的优选队伍数参见 src/server/MapPlaylist.ts 中的SPECIAL_TEAM_MAPS必须为0或≥ 2不允许 1custom_tribes部落名数组每项是字符串或{ name: ..., coordinates: [x, y] }对象坐标为可选省略则随机出生名字必须唯一且不得与nations[].name冲突空字符串会被拒绝themes用于生成部落名的主题字符串数组layers渲染在地形与领土之间的地图图层定义数组见下nations国家对象数组见下分类Categoriescategories每项必须是以下值之一featured、new、world、continental、europe、asia、north_america、africa、south_america、oceania、antarctica、countries、cosmic、fictional、arcade、tournament跨区域的图如黑海、白令海峡可以同时列出多个加入featured会让地图出现在选择器的精选区。分类的合法性与去重校验在 codegen.go 中强制执行分类的展示顺序由 categoryOrder 决定并同步生成到MapCategory类型与mapCategoryOrder常量中。国家Nations字段必填说明name✅国家显示名coordinates[x, y]出生点坐标原点在左上角x 向右、y 向下省略则随机出生flagISO 3166 国家/地区码如us、ca、de。支持的完整列表见 resources/countries.json客户端与服务器端均通过该文件引用旗帜与国名例如 src/client/InventoryModal.ts注README 中给出的../src/client/data/countries.json路径在当前仓库中实际位于resources/countries.json引用时以实际路径为准。图层Layers图层是渲染在地形与领土之间的 PNG 覆盖层用于装饰道路、文字标签等。每个图层是assets/maps/map_name/下独立命名的 PNG文件名即id.png。字段必填说明id✅图层唯一标识同时也是 PNG 文件名不含扩展名必须为字母数字允许连字符不得为image保留名placement✅land或water——图层是落在陆地含不可通行地形还是水域瓦片上nukeable为true时图层会在核弹爆炸半径内被永久摧毁默认false真实示例china地图定义了{id: label, placement: water, nukeable: false}其对应的label.png需与image.png尺寸一致。图层的显示名存放在 resources/lang/en.json 的map_layers段按图层 id 为键玩家可以在图形设置中关闭图层。生成器在 main.go 中会校验图层 PNG 存在、placement合法且尺寸必须与主图完全一致否则报错。info.json 的强校验loadMapInfos() 在生成前会对每个非测试地图的 info.json 做全面校验常见硬性规则包括id必须是文件夹名的 UpperCamelCase大小写不敏感比对translation_key必须严格等于map.foldernamefeatured_rank 0时categories必须含featuredspecial_team_count不得为 1各 frequency 不得为负forced_modifiers/disabled_modifiers的键必须在白名单内isRandomSpawn、isCompact、isCrowded、isHardNations、startingGold1M/5M/25M、goldMultiplier、isAlliancesDisabled、isNukesDisabled、isSAMsDisabled、isPeaceTime、isWaterNukes、isDoomsdayClock带百分比的key:percentage其数值必须在 1–100custom_tribes不得重复、不得为空字符串、不得与nations[].name冲突图层id必须为字母数字允许连字符、不得为image、不得重复placement只能为land/water。任何一条不满足都会导致生成失败并给出明确错误信息相当于免费的配置检查器。输出文件详解处理一张地图后输出写入 resources/maps/map_name/包括文件说明manifest.jsonJSON 元数据全尺寸、1/4、1/16 三档地图的宽高与陆地瓦片数map.bin全尺寸二进制地图数据地形类型 Magnitude 打包map4x.bin1/4 规模边长减半二进制用于小地图map16x.bin1/16 规模边长四分之一二进制用于小地图thumbnail.webp地图 WebP 缩略图另外每次运行即使只传--maps还会固定重写两个仓库级文件src/core/game/Maps.gen.ts —— 由所有 info.json 生成的 TypeScriptGameMapType枚举、maps列表每条一个MapInfo对象resources/lang/en.json ——map段用每张地图的显示名重写。以fourislands为例其 manifest.json 会写入map1500×1500约 51.7 万陆地瓦片、map4x750×750、map16x375×375三档尺寸信息。二进制打包格式packTerrain() 将地形网格序列化为字节流每个瓦片占 1 字节按行优先y*widthx排列位布局为Bit 7陆地1/ 水0Bit 6岸线ShorelineBit 5海洋OceanBits 0–4Magnitude0–31对水瓦片存储的是距离 / 2不可通行瓦片固定编码为0b10011111isLand1、magnitude31且不计入陆地瓦片数因为它不能被占领/攻击/核弹打击。岸线标记由 processShore() 计算陆地邻水为岸线、水邻陆为岸线不可通行瓦片永远不是岸线不渲染轮廓。游戏中的瓦片颜色由地形类型与 Magnitude 动态计算海洋默认颜色定义在 src/client/render/gl/render-settings.json如oceanColor: #4785b5用户可在设置中修改地形颜色计算逻辑见 src/client/render/gl/utils/ColorUtils.ts。迷你地图与缩略图生成三层缩放通过 createMiniMap() 逐级降采样每 2×2 块合并为 1 个输出瓦片合并优先级为Water Impassable Land。水优先级最高是为了保留狭窄河流/水道——寻路算法运行在迷你地图上需要准确的水体信息。缩略图由 createMapThumbnail() 从 1/4 缩放地形生成quality0.5再经 convertToWebP() 以质量 45、Method 4 编码为 WebP。缩略图配色与游戏内明暗配色体系相互独立getThumbnailColor()不可通行与水体渲染为透明地图选择器背景透出陆地岸线为rgb(204,203,158)平原偏绿、高地偏米黄、山地为近白色灰阶。生成链路与自动格式化每次go run .的完整执行链路main.go解析 flag构建自定义 slog 日志器discoverMaps()发现全部地图loadTerrainMaps()并发处理选中地图写出map.bin/map4x.bin/map16x.bin/thumbnail.webp/manifest.json并复制、校验图层 PNGloadMapInfos()读取并校验全部非测试地图的 info.jsongenerateMapsTS()重写 src/core/game/Maps.gen.tsGameMapType枚举、GameMapName、MapCategory联合类型、mapCategoryOrder、SpecialModifierKey、MapInfo接口、CustomTribe、MapLayer、maps列表generateEnJSON()重写 resources/lang/en.json 的map与map_layers段保留非地图键如featured、random最后调用 runPrettier() 对本次写出的全部文件执行npx prettier --write。关于第 7 步生成器会把每个写出的文件交给仓库根目录的 Prettier 配置格式化npx --no-install prettier --write ...因此go run .产出的 diff 已经与npm run format一致无需手动格式化。若环境中没有npx prettier例如未执行npm ci生成仍会成功但会打印警告此时需在仓库根目录手动执行npm run format补格式化。仓库根目录的 npm 脚本与go run .等价npm run gen-maps # 等价于 cd map-generator go run . npm run format见 package.json 中的gen-maps脚本定义。更新 CREDITS 与添加旗帜版权与署名新增地图时请把许可证与署名信息补充到 CREDITS.md。若不确定地图素材的许可证能否使用应先开 issue 或在社区 Discord 中确认后再开工。新旗帜旗帜 SVG 放在 resources/flags/文件名为iso_code.svg同时需在 resources/countries.json 中登记该国家/地区。例如venice地图使用的flag: venice即对应resources/flags/venice.svg。启用进游戏生成器运行后游戏侧无需任何手动步骤一切从 info.json 自动派生GameMapType枚举与maps列表每图一个MapInfo写入 src/core/game/Maps.gen.ts。该文件由代码生成禁止手改resources/lang/en.json 的map段被重写为每张图的display_name或name其他语言的翻译通过 Crowdin 平台管理。也就是说改 info.json → 跑生成器 → 重新构建/运行游戏新地图即生效。开发工具与文档命令针对生成器本身的开发维护格式化 Go 代码go fmt .输出生成器完整文档它是 CLI 工具需-cmd显示命令相关内容不对外暴露 API故用-u -all展示所有未导出值的文档go doc -cmd -u -all已知问题go doc -http模式不会尊重其他 flag只会渲染 README。仓库根目录还提供了对应 npm 快捷脚本npm run format:map-generator与npm run docs:map-generator见 package.json。常见问题与注意事项汇总小岛/小湖自动清理 30 像素的岛、 200 像素的水体会被转换可用--log-removal查看详情尺寸裁剪宽高会被裁为 4 的倍数超出部分丢弃源图长宽请尽量设计为 4 的倍数性能红线源图面积建议 200–300 万像素全尺寸陆地瓦片不建议超过 300 万平均约 100–200 万name不可变更它是线上传输格式的一部分、会写入对局记录改名的代价远超想象translation_key格式必须精确为map.foldername否则校验直接失败图层 PNG 尺寸必须与image.png完全一致Maps.gen.ts不要手改每次运行都会整体重写手改必被覆盖格式化若npx prettier不可用生成仍成功但需手动npm run formatflag 命名性能/移除日志的实际 flag 为--log-performance/--log-removalREADME 中写作--debug-*的表述与源码不一致以源码为准。通过以上流程任何人都可以为 OpenFrontIO 制作一张可发布的地图准备灰度源图、编写元数据 JSON、运行一行命令剩下的像素解码、小地块清理、多尺度打包、代码生成与自动格式化全部由 MapGenerator 完成。赞分享游戏开发后端【免费下载链接】OpenFrontIOOnline browser-based RTS game项目地址https://gitcode.com/gh_mirrors/op/OpenFrontIO点击查看免费下载相关推荐Refly3 步把 AI 工作流想法变成可执行技能私有部署实操指南Refly3 步把 AI 工作流想法变成可执行技能私有部署实操指南 你写过的那些好用的提示词往往一次就废了放在聊天窗口里没人能复用。Refly 是一个游戏开发后端OpenFrontIO重塑实时战略游戏体验的完整指南项目概览 OpenFrontIO 是一款开源的在线实时战略游戏专注于领土控制和联盟建设。作为 WarFront.io 的分叉和重写版本这款游戏让玩家能够在基游戏开发后端终极指南如何快速掌握OpenFrontIO实时战略游戏终极指南如何快速掌握OpenFrontIO实时战略游戏 OpenFrontIO是一款基于浏览器的开源实时战略游戏让玩家在真实地理地图上展开领土扩张、联盟建设游戏开发后端上一篇如何快速下载B站4K高清视频bilibili-downloader完全指南下一篇Akka Stream RestartFlow.onFailuresWithBackoff 详解基于指数退避的 Flow 故障自动重启创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表