FNF模组开发实战:从“vs亨利但Pico”看角色替换与数据对齐

发布时间:2026/10/15 4:19:23
FNF模组开发实战:从“vs亨利但Pico”看角色替换与数据对齐 如果你试着把一个 FNF 模组的标题改成“fnf vs 享利但是Pico”你会发现社区里这类“换成某个角色再打一遍”的模组远没有看起来那么简单。很多人以为它只是把对手贴图换掉实际动手改完才发现角色动画不播、音符没有对象、音频对不上时间轴甚至游戏直接白屏。这篇文章想讲清楚的正是一个 FNF 模组从标题到可运行之间那些真正容易被忽略的技术环节。这里先给一个明确判断FNF 模组开发的核心不是美术替换而是角色动画、谱面时间轴、音频文件三套数据的对齐。你花多少时间画素材就要花差不多的时间确保 JSON 里的每个字段都被引擎正确读取。这篇文章会从一个社区常见的“vs 享利但是Pico”类模组切入讲清楚 FNF 模组目录结构、角色数据格式、谱面 chart 格式、环境搭建、完整实现、运行验证、常见坑和工程建议。无论你是刚接触 FNF 模组的新手还是想系统理解 FNF 数据结构的开发者都可以按文中的流程跑通一个最小角色替换模组。1. 这篇文章真正要解决的问题先回答最实际的问题为什么现在的 FNF 模组社区里“XX 但是 XX”这种命名非常多因为 FNF 的模组体系允许作者复用现成谱面、音轨和角色形象只替换其中某个角色或难度就能产生一个玩法完全不同的新关卡。这类模组的改动量看似小实际涉及 FNF 引擎对角色和谱面读取的完整链路。具体来说“fnf vs 享利但是Pico”这类模组的技术本质是两件事角色替换把原谱面中的对手角色或玩家角色从 A 换成 Pico要求 Pico 的动画、血量图标、缩放、镜头位置都被正确加载。谱面兼容原“vs 享利”谱面的音符时间点、BPM、长条、事件标记必须在新角色下仍然能正确命中、播放和判分。如果你只是在图片层面把角色换掉运行时会发现很多问题比如角色一直保持闲置动画按下方向键后显示的是默认 Boyfriend 动画或者角色显示成空白的“TXT”占位符更严重的是如果 mod 里的角色 json 字段写错整个歌曲加载都会中断。因此这篇文章不打算只教你怎么“改名”而是把 FNF 角色模组的完整技术链路拆开环境、目录、素材、JSON 格式、验证、排错。读完你应该能做到理解 FNF 官方引擎和社区引擎分支的模组加载机制搭建一个能调试和打包 FNF 模组的环境创建一个包含 Pico 角色的最小模组并正确接入“vs 享利”类型的谱面遇到加载失败时有能力按结构排查而不是重新下载别人打包好的文件。如果你之前完全没有接触过 FNF 模组也不用担心本文会从目录结构讲起如果你已经做过简单换皮 mod可以直接跳到第 5 章看 chart 与角色数据的完整示例。2. FNF 模组的结构与原理角色替换背后的文件层级2.1 一个 FNF 模组由哪些文件组成FNF 的模组本质上是一组“覆盖文件”它会按照固定目录结构加载图片、音频、数据和脚本。以社区最常见的 Psych Engine 分支为例一个标准的模组目录通常长这样mods/ └── MyMod/ ├── pack.json ├── data/ │ ├── characters/ │ │ └── pico.json │ └── songs/ │ └── vs-henry/ │ ├── henry-hard.json │ ├── henry-hard-events.json │ └── ... ├── images/ │ ├── pico.png │ └── pico.xml ├── songs/ │ └── vs-henry/ │ ├── henry-hard.ogg │ └── henry-hard-inst.ogg └── scripts/ └── ...注意不同引擎分支的目录细节略有差异但核心思想一致游戏运行时会根据 mod 文件夹内的相对路径覆盖到它自带的同名资源上。也就是说你不需要修改游戏本体只需要把文件放对位置引擎启动时就会优先读取你的版本。2.2 角色Character加载的完整链路在 FNF 中一个“角色”不只是图片它是一套数据契约。当你在谱面 JSON 中写player2: pico时引擎会按以下顺序查找在data/characters/pico.json找到角色配置文件根据其中的image字段在images/目录加载对应的图集 XML/PNG根据animations数组建立“动画名称 → 图集帧区间”的映射根据空闲、向左、向下、向上、向右、跳跃等动画名称绑定输入响应在 HUD 上显示healthicon指向的血量图标。这个链路中任何一环断了就会出现“角色加载了但是不播放对应动画”或者“角色直接变成紫色报错方块”的情况。所以角色替换的工程核心其实是保证这条链路的五个环节都闭合。2.3 谱面 Chart 的角色字段与音符数据FNF 的歌曲谱面文件是一个 JSON里面保存了 BPM、玩家角色、对手角色、音符数组等信息。以一首“vs 享利但是 Pico”的 hard 难度为例最核心的角色字段在song对象中{ song: { song: henry-hard, bpm: 140, speed: 2.5, player1: bf, player2: pico, gf: gf, notes: [] } }player1玩家操纵的角色通常是 BFBoyfriend。player2对手角色这里改成pico后引擎会去加载 Pico 的角色配置。gf伴唱角色不是每首曲子都有。如果你只是想让 Pico 站在对立面核心字段就是player2。但如果你希望玩家操纵的角色变成 Pico需要把player1也改成pico并且要确保 Pico 的角色配置里包含玩家侧需要的动画比如idle、singLEFT、singDOWN、singUP、singRIGHT和跳跃动画。很多“但是 Pico”类模组实际上是把 Pico 放进玩家侧然后把原谱面的 note 排列改成 Pico 相关主题。严格来说这已经不是简单的“替换贴图”而是同时改角色配置和谱面映射。2.4 为什么不能只改图片名社区里最常见的误解是把henry.png改成pico.png就可以了。实际运行时你会遇到两个典型问题动画名称对不上原角色 XML 图集里子图名称可能叫Henry idle0000而你新角色 XML 里叫Pico idle0000引擎找不到对应帧就会播放失败。json 里的 image 路径指向旧图集角色 json 里如果写image: henry即使文件名已经改成 pico引擎仍然按 json 里的字段去加载henry.png。因此角色替换必须做到“图片、XML、角色 json、谱面 json”四个文件同步修改而不是改文件名。这也是本文后面所有实操步骤都围绕“数据对齐”展开的原因。3. FNF 模组开发环境准备与工具链3.1 方案选择源码构建 vs 现成引擎加载在开始写 mod 之前你要先确定技术路线方案适合人群优点缺点直接使用社区引擎加载 mods只做角色替换、谱面配置不需要编译环境改文件即可依赖具体引擎分支部分新特性不可用从源码构建 FNF 引擎需要自定义玩法、改引擎逻辑可控性最强能深度调试需要安装 Haxe/OpenFL 工具链构建耗时对于“vs 享利但是Pico”这种角色替换型 mod用社区引擎直接加载是完全够用的。但作为 CSDN 读者我建议至少把源码构建环境也准备一份。原因很实际当你的 mod 出现问题源码环境可以让你输出完整日志还能在关键代码处加 trace 定位变量值。3.2 最小环境清单以下是经过社区验证的常见组合版本号请以对应项目和引擎分支的 README 为准操作系统Windows 10/11 或 macOS开发机建议 8GB 以上内存。Git用于拉取 FNF 源码和模组模板。HaxeFNF 源码的核心语言安装后能用haxelib管理库。OpenFL 与 LimeHaxe 下的跨平台多媒体框架负责窗口、图形、音频。文本编辑器VS Code 或任何能高亮 Haxe/JSON 的编辑器。音频工具Audacity 或类似工具用于检查和裁剪 OGG 音轨。如果你选择源码构建安装完 Haxe 后在终端里执行haxelib install openfl lime haxelib install flixel haxelib install flixel-addons haxelib install flixel-ui然后进入项目目录git clone FNF官方或社区引擎仓库地址 cd 项目目录 lime setup lime test windows如果一切顺利系统会打开一个可运行的 FNF 窗口。第一次构建时间较长因为需要下载依赖和编译资源请耐心等待不要在中途强制关闭。3.3 目录规划建议在开始动手前建议把工作目录分成三块FNF-Project/ ├── engine/ # 引擎源码或社区引擎程序 ├── mods/ # 你正在开发的 mod 目录 └── tools/ # 音频、图片、谱面辅助工具这样可以避免把测试素材混进引擎源码减少不必要的冲突。4. 角色替换与谱面适配的核心流程拆解4.1 准备 Pico 角色素材Pico 是 FNF 社区非常熟悉的角色如果你没有现成素材通常可以在社区提供的角色包里获取。拿到素材后必须确认三样东西齐全pico.png角色全帧图集通常是一张包含所有动画帧的长图或九宫格图。pico.xmlSparrow 图集描述文件记录每个子图名称和坐标。角色配置文件pico.json定义动画、缩放、血量图标、镜头位置等。如果没有 XML只有单独的帧图你可以自己用工具生成图集和 XML。这里不推荐手工填坐标容易出错。4.2 建立模组目录并挂载在引擎的 mods 目录下新建你的 mod 文件夹例如MyPicoVsHenry然后创建以下子目录mkdir -p MyPicoVsHenry/data/characters mkdir -p MyPicoVsHenry/data/songs/henry mkdir -p MyPicoVsHenry/images mkdir -p MyPicoVsHenry/songs/henry目录建好后把pico.png和pico.xml放进images/把歌曲音频放入songs/henry/把谱面和角色配置放入data/对应位置。4.3 编写角色配置 JSON这一步是角色替换的关键。下面是一个符合常见引擎分支格式的pico.json简化示例字段含义我已经写在注释中。注意 FNF 的 JSON 不支持注释这里只是为了便于说明实际文件里不能写这些注释{ animations: [ { name: idle, prefix: Pico idle, fps: 24, loop: true }, { name: singLEFT, prefix: Pico left, fps: 24, loop: false }, { name: singDOWN, prefix: Pico down, fps: 24, loop: false }, { name: singUP, prefix: Pico up, fps: 24, loop: false }, { name: singRIGHT, prefix: Pico right, fps: 24, loop: false } ], image: pico, scale: 0.85, sing_duration: 3.85, healthicon: pico, flip_x: false }animations[].name引擎识别的动画名称。idle是空闲动画四个sing是对应方向键的唱歌动画。prefix动画在 XML 图集中的子图前缀。例如 XML 里子图名是Pico idle0000前缀就是Pico idle。fps动画帧率FNF 通常使用 24。loop是否循环播放。image图集加载名与images/pico.png对应。healthicon血量图标文件名通常位于 HUD 素材包中。关于sing_duration这个字段控制角色唱歌动画持续的时间单位是毫秒、帧还是拍不同引擎分支有不同解释。更稳妥的做法是参考你使用的引擎分支自带角色 json 里的数值。取一个与 BF 角色一致的值能减少踩坑。4.4 准备谱面 JSON 并指定角色如果你的 mod 是复用原“vs 享利”谱面那么你需要把原 JSON 中song.player2改成pico同时检查song.notes里的 note 类型是否与 Pico 角色机制冲突。谱面文件命名必须遵循引擎规则通常是data/songs/henry/henry-hard.json文件名里的hard是难度标识不能随意修改。如果引擎要求herry-hard.json和herry-hard-events.json成对出现那你需要保留事件文件否则某些谱面事件会丢失。4.5 处理音轨对齐角色替换完成不代表谱面能玩。如果“vs 享利”原版音轨里包含亨利的人声而你换成 Pico理论上应重新录制或混音 Pico 的 vocal 部分。如果暂时拿不到合适的音轨可以暂时使用原音轨但要在song.needsVoices等字段中保持原有取值避免引擎误判。音频格式必须转换为 OGG Vorbis且采样率建议保持一致。转换工具推荐 Audacity导出设置为 OGG 默认品质即可。如果音频和谱面时间轴对不上进入游戏后会出现“音符到了但没有声音”或“音轨已经到下一段但谱面还在前一段”的体验错位。5. 完整示例让 Pico 以最小成本替换到谱面中5.1 示例场景定义我们用最小化方式实现这样一个目标原谱面一首名为 “henry” 的 hard 难度歌曲。角色配置新增 Pico 角色作为对手player2。效果启动游戏后可在自由游玩中选择该歌曲对手显示为 Pico并且按方向键时 Pico 会播放对应方向的动画。5.2 文件结构总览假设你的 mod 名为PicoHenry最终文件结构如下mods/PicoHenry/ ├── pack.json ├── data/ │ ├── characters/ │ │ └── pico.json │ └── songs/ │ └── henry/ │ ├── henry-hard.json │ └── henry-hard-events.json ├── images/ │ ├── pico.png │ └── pico.xml └── songs/ └── henry/ ├── henry-hard.ogg └── henry-hard-inst.ogg5.3 pack.jsonpack.json是模组的元数据文件主要用于引擎识别模组名称、描述和优先级。简化写法如下{ name: PicoHenry, description: vs Henry but Pico, priority: 10 }priority决定资源冲突时优先加载哪个模组数字越大优先级越高。开发初期建议保持一个模组加载避免多模组资源互相覆盖。5.4 角色动画 XML 示例Pico 的图集描述文件pico.xml是 Sparrow 格式。它把一张大图中的每个动画帧都划分出坐标和尺寸TextureAtlas imagePathpico.png SubTexture namePico idle0000 x0 y0 width96 height96/ SubTexture namePico idle0001 x96 y0 width96 height96/ SubTexture namePico left0000 x192 y0 width96 height96/ SubTexture namePico left0001 x288 y0 width96 height96/ SubTexture namePico down0000 x384 y0 width96 height96/ SubTexture namePico down0001 x480 y0 width96 height96/ SubTexture namePico up0000 x576 y0 width96 height96/ SubTexture namePico up0001 x672 y0 width96 height96/ SubTexture namePico right0000 x768 y0 width96 height96/ SubTexture namePico right0001 x864 y0 width96 height96/ /TextureAtlas注意这里name必须和pico.json里的prefix一致。比如 JSON 里的singLEFT使用前缀Pico leftXML 中有Pico left0000这样引擎才能把singLEFT动画解析成 XML 里的帧序列。很多角色动画播放异常的 bug根源都出在这个“前缀对不上”上。一旦 XML 里是Pico Left而 JSON 里是Pico left大小写不同引擎就会找不到帧。5.5 谱面 JSON 角色字段示例下面是一个最小可用的谱面片段只展示角色和音符结构。实际谱面会有很多小节请用你的源谱面替换{ song: { song: henry, bpm: 140, speed: 2.5, player1: bf, player2: pico, gf: gf, notes: [ { sectionNotes: [ [0, 0, 0, ], [250, 1, 0, ], [500, 2, 0, ], [750, 3, 0, ] ] } ] } }sectionNotes中每一项数组的含义通常是第一个值音符出现的毫秒时间点第二个值轨道编号03对应左、下、上、右具体以你的引擎按键映射为准第三个值音符持续时间0 表示普通短音符大于 0 表示长条第四个值音符类型字符串通常为空。如果你的原谱面是“vs 享利”的现成 chart不需要手写这些数组你只需要把player2改为pico然后重新保存 JSON。一定要用能校验 JSON 格式的编辑器保存否则一个多余逗号就会导致加载失败。5.6 HScript/Lua 可选扩展如果引擎支持 HScript 或 Lua你还可以在scripts/目录下写脚本来实现额外机制。比如 Pico 在被击中后进入特殊状态或播放一段额外动画。下面是一个示意脚本具体 API 取决于引擎分支function onUpdate(elapsed) if opponentName pico then health math.max(health - 0.01 * elapsed, 0.2) end end这种脚本适合加防白给机制但不适合零基础用户。建议先把无脚本版本跑通再逐步加机制。6. 运行结果与效果验证6.1 启动测试启动游戏后进入自由游玩模式选择你这首henry歌曲。启动命令在你的引擎仓库目录下执行lime test windows如果引擎直接支持 mods 加载你也可以直接把编译好的游戏运行文件打开在 mods 菜单里启用PicoHenry。6.2 验证点清单不要只看“能进入歌曲”就认为 mod 成功。建议按以下顺序检查对手是否显示为 Pico进入歌曲后右侧角色应是 Pico 而不是亨利。若显示默认 BF 或空模型说明player2字段没有生效。动画是否随方向播放对手角色在唱歌时会随着音符方向播放singLEFT、singDOWN等动画。如果一直播放 idle检查角色 JSON 里的prefix是否和 XML 匹配。血量图标是否正常血量条中间显示的是 Pico 图标而不是默认图标。如果显示问号说明healthicon引用的资源不存在。音频踩点是否准确音符接近判定线时音轨中的鼓点和人声是否在同一拍。如果偏移明显回到谱面 JSON 调整 BPM 或在编辑器里微调音符时间。是否出现红色错误弹窗如果引擎弹出类似Error: Could not find image: pico的信息说明图片加载路径不对。6.3 开发模式下的日志定位在源码构建环境下终端控制台会实时输出 Haxe trace 和 Flixel 日志。当你的 mod 出现定位困难时优先看终端而不是反复重启游戏。日志里通常会给出具体缺失文件或字段名的提示。如果使用现成引擎加载 mods通常在游戏目录logs/下也能找到日志文件。定位问题时先注意以下关键词Could not find资源缺失。Invalid JSONJSON 格式错误。Animation动画匹配失败。Note音符数据读取异常。7. 常见问题与排查思路这里把 FNF 角色替换类模组最常见的坑整理成一张表遇到异常时可以直接对表排查问题现象可能原因排查方式解决方案模组列表里看不到你的 modmod 目录缺少pack.json或 pack.json 格式错误检查 mods 目录下文件夹是否包含pack.json补齐或修正 pack.json 字段进入歌曲后角色显示为黑块/紫块图集 PNG 未加载或 XML 与 PNG 文件名不对应看日志是否提示找不到 image确认images/下的文件命名与 json 的image字段一致角色一直播放空闲动画动画 prefix 与 XML 子图名称不一致对比 json 的prefix和 xml 的name统一命名注意大小写角色唱歌时播放了错误方向的动画动画列表顺序或轨道编号映射错误检查singLEFT、singDOWN等 name 是否填反按引擎源码或原版角色 json 对照修正加载歌曲时弹 JSON 解析错误文件里有多余逗号、中文引号或 BOM用 JSON 校验工具检查删除多余逗号使用英文引号另存为 UTF-8 without BOM音符对不上音轨BPM 或音频长度不匹配在 Audacity 中查看音频拍点和 chart 的 BPM 对比重新设置 chart BPM或重新导出音频长条音符无法命中note length 字段用法错误检查引擎分支对长条音符的约定使用原版 chart 中的长条片段比对格式换角色后 BF 的动画也乱了角色 json 同时被其他 mod 覆盖检查 packs 优先级调整priority或一次只启用一个 mod每个问题背后几乎都能追溯到某个“字段没对齐”。FNF 模组开发中“字段对齐”比“代码逻辑”更值得重视因为大多数引擎分支已经实现了完整逻辑你只需要用正确的数据触发它。8. 最佳实践与工程建议8.1 素材命名统一建议创建一套固定命名规则例如所有角色文件都用小写英文XML 里的子图名都采用角色名 动作名 帧号的格式且帧号补足四位。这样无论是自己排查还是交给队友维护都能减少认知负担。8.2 先做最小模组再叠加功能第一次开发时只做一个包含“一首歌 一个角色”的最小模组不要贪多。把最小链路跑通后再逐步添加事件、脚本、多难度、故事模式。否则你很难分辨是角色配置问题还是谱面问题调试会非常痛苦。8.3 保留原版 chart 备份在你把player2修改为pico之前一定先复制一份原版 JSON 到备份目录。角色替换 mod 最忌讳直接覆盖原文件一旦改错你连恢复基准都没有。建议长期保存一个“未改动原版素材”目录只读不写。8.4 避免直接修改引擎源码如果你的目标只是角色替换不要一开始就改引擎源码。源码分支更新频繁直接改源码会让你的模组无法跟随上游更新。优先用 mods 目录和脚本实现。只有当脚本 API 无法满足机制需求才考虑 fork 引擎源码维护自己的分支。8.5 注意素材授权社区模组大量使用他人的角色素材。FNF 的角色、音轨和动画都有自己的版权归属发布前记得检查素材授权。如果项目只用于个人学习问题不大如果要在社区公开分享最好使用开源或明确注明允许二次创作的角色素材。8.6 性能与体积控制Pico 的图集如果包含大量高分辨率帧会导致加载变慢和内存占用上升。建议在保证清晰度的前提下压缩 PNG控制图集宽度为 2 的幂次方尺寸。音频也建议压低码率因为一个模组如果包含多首歌曲体积会迅速膨胀。9. 总结与后续学习方向现在你已经清楚“fnf vs 享利但是Pico”这类 mod 的工程核心是让 Pico 的角色动画、谱面 chart 和音频数据在引擎里对齐。你也能理解为什么一个看似简单“换角色”的 mod会涉及data/characters、data/songs、images、songs等多个目录的协同。如果你按本文流程跑通了一个最小模组下一步建议按顺序深入三个方向谱面设计学习 chart 编辑器理解 BPM 变化、事件谱、长条、双人大按键等技术。脚本机制研究引擎分支的 HScript/Lua API尝试给 Pico 设计专属技能或状态。多难度与故事模式把单个 hard 谱面扩展到 easy/normal/hard并接入故事模式菜单让 mod 更完整。最后提醒一句FNF 模组开发天然是“改一点、测一点”的过程不要试图一次完成所有功能。每次改动画、改谱面、改音频后都运行一次确认上一个环节没有破坏再进入下一环。这样你的“但是 Pico” 版本不仅跑得通还会成为别人愿意下载的完整模组。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询