跳转至

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 从零到能跑

  1. 环境:装 .NET SDK 与 IDE,用模板命令生成目标平台工程,先跑通自带的空窗口示例。
  2. 骨架:把模板拆成 Core 加平台工程,写第一个游戏类:窗口、清屏、退出的最小循环。
  3. 立规矩:接入版本控制,写好忽略规则与编辑器配置,此刻就建 tools/ 目录。
  4. 上画面:画一张贴图,加输入与相机,完成「角色踩在关卡上并能移动」的最小循环。
  5. 内容管线:先跑通一条最小管线(字体或音频各一),其余不需要转换的资产直接读原始文件更省事。
  6. 出包:先做桌面版发布,把「一条命令出包」写进脚本,之后所有平台复用这套脚本。

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. 学习路线

按顺序走,每一步都要有可运行产物:

  1. C# 与 .NET 基础:先把语言、泛型、集合、异步与调试器打牢,框架救不了语言不熟的人。
  2. 官方文档与模板工程:把入门教程走完,跑通空窗口、贴图、声音、手柄四件事。
  3. 复用 XNA 时代的知识:那一批教程与书籍在接口层面仍然适用,与当前实现有出入处以实测为准。
  4. 三个小作品:乒乓、打砖块、带滚屏的平台跳跃,依次加上状态机、相机与关卡数据。
  5. 工具链专项:内容管线、瓦片地图流程、自建调参面板各写一遍,沉淀成自己的工程模板。
  6. 发布一件小作品:从构建脚本到商店页完整走一遍;想深入底层就对照 FNA 读实现(路线见《引擎源码阅读路线》)。

验收标准(可观察):不看教程能从空工程搭到「有输入、有状态切换、有关卡」;能解释每一帧的执行顺序;发布产物在干净机器上能直接运行。

7. 常见坑

  1. 内容管线黑洞:字体、着色器这类资产卡在转换环节,构建报错看不懂。先跑通一条最小管线,其余资产优先考虑直接读原始文件。
  2. 文档分散:官方文档覆盖基础,深入问题散在源码、issue 与社区聊天里。学会读源码是这条路线的基本功。
  3. 生态碎片化:库要么年久失修,要么只兼容两者之一。选库先核对维护状态与目标框架,别把核心系统押在弃维护的库上。
  4. 范围失控:没有引擎护栏,什么系统都想自己写,半年后还在造引擎。里程碑里给自研系统设硬上限。
  5. 主循环配错:把变步长当固定步长用,物理与手感随帧率漂移;或追赶无上限,卡顿雪崩。
  6. 每帧分配:字符串、装箱、闭包在更新与绘制里悄悄制造垃圾,帧率被垃圾回收啃掉。
  7. 资源只加载不释放:高频对象与手工创建的贴图是内存泄漏常客,从第一天就用统一入口管理。
  8. 平台差异后置:分辨率、缩放、路径大小写、存档目录,到测试末期才处理,返工面积很大。
  9. 选错血缘:新项目照搬 FNA 的老做法,或拿 MonoGame 硬搬要求逐帧一致的移植项目。按 §1 的规则选。
  10. 把模板工程当产品:模板只是起点,平台分支、构建脚本与内容流程都要按项目重做一遍。

延伸阅读

  • 引擎轨道总览:12 条轨道的清单与规划,本页是其中「MonoGame / FNA」一条。
  • 技术实现手册:核心系统、性能方法论与工程基础设施的通用版本,与 §3、§4 多处呼应。
  • 引擎源码阅读路线:把 MonoGame 源码读通作为第一次完整源码阅读的配套路线。
  • 独立开发生存手册:范围控制、排期与现金流,与 §1 的受众定位互为补充。
  • 避坑大全:与第 7 节对照阅读。
  • 资源大全:两条路线的官方入口、社区库与教程索引。