Ludo Atlas · 引擎轨道 · MonoGame / FNA¶
引擎轨道。定位:XNA 血脉的 C# 代码优先框架,MonoGame 现代演进、FNA 忠实复刻,主循环、架构与工具链全由自己组织。 配套:《技术实现手册》·《游戏设计手册》·《独立开发生存手册》·《引擎源码阅读路线》。
1. 定位与选型¶
MonoGame 与 FNA 是微软停更的 XNA 框架的两个开源延续,共享同一套世界观:C# 代码优先,没有场景编辑器,你拿到的是窗口、主循环与一组绘制、输入、音频接口,其余全部自己组织。用这套朴素工具链做成的商业作品不少,《星露谷物语》《蔚蓝》都在其列,它撑得起完整项目,前提是接受它的世界观。
两者的分野在「对原版的态度」:
| 维度 | MonoGame | FNA |
|---|---|---|
| 定位 | XNA 风格的现代框架,接口与行为允许小步演进 | 以与原版完全兼容为目标的复刻,还原度优先 |
| 适合谁 | 新项目、想用现代 .NET 工具链的人 | 移植既有 XNA 项目、要求行为逐帧一致的人 |
| 内容管线 | 自带 MGCB,资产随构建编译 | 没有内容管线,官方鼓励自建轻量转换工具 |
| 工程形态 | 每个目标平台一份工程 | 一份工程覆盖它支持的全部平台 |
| 生态 | 社区库大体以它为目标 | 更朴素,多数工具要自己接 |
| 平台 | 桌面、移动、主机(主机走注册开发者渠道) | 桌面与主机为主,移动靠社区分支 |
选择规则:新项目默认 MonoGame;手里有跑得动的 XNA 老工程、要保真移植,走 FNA;不要在同一项目里混用两者,也别指望从一个无痛迁到另一个。
1.1 适合谁¶
- C# 程序员:语言就是工作语言,.NET 生态(依赖管理、测试、持续集成)整套可用,投入的技能与职业能力同源。
- 学习向开发者:每一帧发生了什么完全透明,是理解「引擎在替你做什么」的直接素材,与《引擎源码阅读路线》互为表里。
- 移植方:既有 XNA 项目要上现代平台,FNA 能把迁移量压到最小。
- 控制欲强的作者:渲染顺序、内存、线程与平台集成都握在自己手里,没有引擎黑箱需要博弈。
- 小体量、机制聚焦的 2D 作品:工具缺口的影响面有限,代码优先换来极高的迭代速度。
1.2 不适合谁¶
- 内容量大、策划美术需要直接进编辑器做关卡的项目:没有可视化场景工具,工作流缺口无人代填。
- 依赖现成系统的项目:导航、动画状态机、界面套件大多要自己写或逐个攒。
- 主机首发且没有发行方或移植资源的团队:主机平台有资格门槛与保密要求,要按注册开发者渠道另算。
- 想要开箱即用的人:从窗口拖动、分辨率策略到存档路径,默认全不存在。
1.3 与 Unity、Godot C# 的取舍¶
| 维度 | MonoGame / FNA | Unity | Godot C# |
|---|---|---|---|
| 工作方式 | 代码即工程,无编辑器 | 编辑器为主,代码挂在对象上 | 编辑器为主,节点加脚本 |
| 学习内容 | 学 C# 与图形学常识,没有引擎术语可背 | 学 Unity 的一整套体系 | 学 Godot 的节点与场景模型 |
| 现成系统 | 几乎没有,自建或引入库 | 最多,质量参差 | 中等,官方内置一批 |
| 平台覆盖 | 桌面与移动顺畅,主机走开发者渠道 | 全平台,主机成熟 | 桌面与移动较好,主机靠第三方 |
| 许可与可控 | 开源,运行时完全可控 | 商业引擎,条款需跟踪 | 开源,许可宽松 |
| 内容协作 | 内容进数据文件,工具要自己写 | 美术策划直接进场 | 美术策划直接进场 |
一句话收束:要现成系统与内容协作,选 Unity 或 Godot;要 C# 原生、完全可控、能把技术栈读透,选 MonoGame 或 FNA。
2. 生态与工程结构¶
2.1 工具链¶
工作台是 .NET 工具链本身,编辑器只提供补全与调试:
| 工具 | 负责什么 |
|---|---|
| .NET SDK 与 dotnet 命令行 | 建模板、拉依赖、构建、运行、发布 |
| IDE(Rider、Visual Studio 或 VS Code) | 编码、调试、性能剖析 |
| MGCB(内容构建器) | 把原始资产编译成运行时格式,随构建执行 |
| 模板命令 | 生成各平台工程骨架,桌面、移动等目标各有模板 |
一条纪律:把「改代码到看到画面」的循环压到秒级。代码优先路线的全部优势都押在这上面。
2.2 目录与工程形态¶
单解决方案、按平台拆启动工程是常见形态:
game/
├── Game.Core/ # 全部游戏代码,与平台无关
├── Game.Desktop/ # 桌面启动工程,引用 Core
├── Game.Mobile/ # 移动启动工程,引用 Core
├── Content/ # 原始资产与内容管线配置
└── tools/ # 自建编辑器与转换脚本
- 全部逻辑放平台无关的 Core 工程,平台工程只保留入口与少量适配,这是解决平台差异最省力的做法;
Content/同时放原始资产与管线配置,产物按平台分开输出、不进版本控制;tools/从第一天就建:关卡编辑器、调参面板、转换脚本都是工程的一等公民。
不顺着这个约定走,短则一个月,平台分支里就会长出第二套游戏代码。
2.3 版本控制¶
- 忽略生成产物目录(编译输出与内容产物),它们都能自动重建;
- 提交内容管线配置与资产源文件,保证换台机器构建结果一致;
- 图像、音频等大文件走 Git LFS,规则在项目初期写好;
- 关卡与数据尽量用文本格式,让 diff 可读;二进制格式要另配审查手段。
2.4 生态与常用库¶
框架只给地基,上层要自己攒,常见位置与成熟选项:
| 需求 | 常见做法 |
|---|---|
| 瓦片地图与关卡 | Tiled 编辑器加社区加载库,或自建格式与编辑器 |
| 游戏内界面 | Gum 等界面库,或自绘一套最小控件 |
| 字体渲染 | 社区字体库直接读字体文件,或走内容管线烘焙 |
| 物理 | 现成的 2D 物理库,按需接入 |
| 调试面板 | Dear ImGui 的 .NET 绑定做运行时叠加界面 |
| 扩展工具集 | 社区扩展库(相机、像素图、粒子等)按需取用 |
选库纪律:先看最近提交与 issue 响应,再看许可证与目标框架;XNA 时代的老库数量庞大,能编译不代表能用。
3. 核心工作流¶
3.1 从零到能跑¶
- 环境:装 .NET SDK 与 IDE,用模板命令生成目标平台工程,先跑通自带的空窗口示例。
- 骨架:把模板拆成 Core 加平台工程,写第一个游戏类:窗口、清屏、退出的最小循环。
- 立规矩:接入版本控制,写好忽略规则与编辑器配置,此刻就建
tools/目录。 - 上画面:画一张贴图,加输入与相机,完成「角色踩在关卡上并能移动」的最小循环。
- 内容管线:先跑通一条最小管线(字体或音频各一),其余不需要转换的资产直接读原始文件更省事。
- 出包:先做桌面版发布,把「一条命令出包」写进脚本,之后所有平台复用这套脚本。
3.2 主循环自管¶
框架把游戏类交给你,循环只有两个入口:更新与绘制。四条纪律:
- 逻辑用固定步长(默认即如此),渲染与逻辑解耦;改动步长策略前先想清对物理与手感的影响;
- 单帧追赶要有上限,避免卡顿时越追越卡的雪崩;
- 帧时间分配要可见:从第一天就做帧计时显示与状态叠加面板;
- 暂停、切场景、失焦三类状态各自定义清楚循环行为,这里是 bug 的常驻区。
3.3 无编辑器与自建工具¶
这是本路线最大的隐藏工作量,估算时单独列项:
| 缺口 | 不补齐的后果 | 常见做法 |
|---|---|---|
| 关卡编辑 | 关卡写在代码里,改一堵墙也要重新编译 | 运行时调试模式摆物体并导出数据,或独立小工具读写同一格式 |
| 资产转换 | 每次换素材手动处理,迟早出错 | 脚本化批量转换,接进构建流程 |
| 调参面板 | 每个数值靠改代码重启 | 运行时叠加面板,读写配置文件 |
| 数据表 | 数值散落代码各处 | 表格或文本加转换脚本,构建时生成 |
经验法则:工具成本按小时算,手工维护成本按小时乘迭代次数算。关卡超过十张、数值超过几十个之前,工具必须就位。
3.4 打包与分发¶
| 目标 | 要点 |
|---|---|
| 桌面 | 自包含发布加安装包与商店接入;玩家机器不预装运行时的底线要守住,平台原生后端注意运行库依赖 |
| 移动 | 安卓需要工具链与签名配置,iOS 最终签名依赖 Mac 环境;包体、内存与输入形态都要单独适配 |
| 主机 | 走注册开发者渠道,库与文档保密;无渠道就提前规划第三方移植方案 |
| 浏览器 | 官方不支持,社区分支(如 KNI)提供实验性移植,成熟度自行核查 |
| 通用 | 更新器、崩溃收集、日志上报都要自己接;从发布日倒排,留出与引擎项目同量级的预算 |
桌面图形后端各有取舍(窗口系统、音频库与驱动依赖不同),发布前把选定后端在目标机型全流程过一遍。
4. 关键系统惯用法¶
4.1 架构:状态机加服务¶
- 没有内置场景系统,主流做法是自建状态栈:主菜单、对局、暂停各是一层,进入退出各自负责资源加载与清理;
- 全局服务(存档、音频、设置、输入映射)用一个显式的服务对象向下传递,别用散落的静态类;
- 每个系统一个类、一个职责、一组明确接口,目录按系统分文件;
- 状态要能在任意时刻序列化与恢复,这是存档与调试的共同地基。
4.2 输入¶
- 输入是轮询模型:每帧主动读取键盘、鼠标、手柄、触屏状态,自己维护按下、按住、松开与输入缓冲;
- 键位与动作必须隔一层映射表,运行时改键与多设备切换才不会牵一发动全身;
- 手柄死区、触屏虚拟按键、键盘焦点丢失三个细节从第一天处理,后补成本高。
4.3 渲染¶
- 二维起步用 SpriteBatch:同一批里保持同一张纹理与同一套状态,图集是批次效率的地基;
- 相机用变换矩阵实现:跟随、死区、前瞻、震屏都是一层矩阵运算;
- 分辨率与像素对齐策略(整数缩放、信箱边、高分屏滚动)提前定死,别等美术资源做完再改;
- 自定义着色器在不同图形后端用各自的编译产物,跨后端发布前逐个验证。
4.4 音频与资产¶
- 短音效常驻内存、长音乐流式播放;容量与格式策略与平台一并规划;
- 资源生命周期是托管语言的陷阱:手工创建的贴图等资源要自己释放,高频生成的对象一律池化;
- 资产加载只留一个入口(统一资源管理器),为热重载、缺省资产与引用计数留余地;
- FNA 路线没有内容管线,资产直接读原始文件的方案更自然,或按需自建转换脚本。
4.5 调试设施¶
- 帧计时、状态叠加、参数面板三件套,在第一个可玩版本之前就要有;
- 运行时控制台比断点更适合调玩法参数;
- 工具界面与游戏界面分开搭,别让调试控件混进玩家界面。
5. 性能与优化要点¶
总则:先测量再优化,以目标设备为准(方法论见《技术实现手册》§3)。
5.1 CPU 与托管内存¶
- 托管语言的瓶颈常在两处:每帧分配与虚调用;热路径避免字符串拼接、装箱、LINQ 与临时集合;
- 高频对象池化并预热,消除生成期的垃圾峰值;
- 用性能剖析器找热点,优化前后留记录;桌面顺滑不代表移动端顺滑。
5.2 绘制¶
- 绘制调用与纹理切换是首要指标:图集、排序、合批三者一起看;
- 每帧的批次断裂要能解释,无法解释的断裂就是白扔的性能;
- 过绘制对大贴图与粒子尤其贵,移动端先压这一项。
5.3 内容与内存¶
- 贴图按目标平台选压缩格式;音频上线前统一为流式友好的格式;
- 大资源按需加载、提前预取,切场景时避免一次性全压进去;
- 移动端考虑裁剪与提前编译相关的发布设置,构建产物在真机全流程过一遍。
6. 学习路线¶
按顺序走,每一步都要有可运行产物:
- C# 与 .NET 基础:先把语言、泛型、集合、异步与调试器打牢,框架救不了语言不熟的人。
- 官方文档与模板工程:把入门教程走完,跑通空窗口、贴图、声音、手柄四件事。
- 复用 XNA 时代的知识:那一批教程与书籍在接口层面仍然适用,与当前实现有出入处以实测为准。
- 三个小作品:乒乓、打砖块、带滚屏的平台跳跃,依次加上状态机、相机与关卡数据。
- 工具链专项:内容管线、瓦片地图流程、自建调参面板各写一遍,沉淀成自己的工程模板。
- 发布一件小作品:从构建脚本到商店页完整走一遍;想深入底层就对照 FNA 读实现(路线见《引擎源码阅读路线》)。
验收标准(可观察):不看教程能从空工程搭到「有输入、有状态切换、有关卡」;能解释每一帧的执行顺序;发布产物在干净机器上能直接运行。
7. 常见坑¶
- 内容管线黑洞:字体、着色器这类资产卡在转换环节,构建报错看不懂。先跑通一条最小管线,其余资产优先考虑直接读原始文件。
- 文档分散:官方文档覆盖基础,深入问题散在源码、issue 与社区聊天里。学会读源码是这条路线的基本功。
- 生态碎片化:库要么年久失修,要么只兼容两者之一。选库先核对维护状态与目标框架,别把核心系统押在弃维护的库上。
- 范围失控:没有引擎护栏,什么系统都想自己写,半年后还在造引擎。里程碑里给自研系统设硬上限。
- 主循环配错:把变步长当固定步长用,物理与手感随帧率漂移;或追赶无上限,卡顿雪崩。
- 每帧分配:字符串、装箱、闭包在更新与绘制里悄悄制造垃圾,帧率被垃圾回收啃掉。
- 资源只加载不释放:高频对象与手工创建的贴图是内存泄漏常客,从第一天就用统一入口管理。
- 平台差异后置:分辨率、缩放、路径大小写、存档目录,到测试末期才处理,返工面积很大。
- 选错血缘:新项目照搬 FNA 的老做法,或拿 MonoGame 硬搬要求逐帧一致的移植项目。按 §1 的规则选。
- 把模板工程当产品:模板只是起点,平台分支、构建脚本与内容流程都要按项目重做一遍。