mod应置于独立目录(如mods/mymod1/),用前缀隔离资源id、assemblyloadcontext隔离dll、叠加式patch改配置;资源可热替换,脚本逻辑需可插拔;配置解析须容错处理;mod执行须沙箱化调度并严格监控。

MOD 文件路径怎么组织才不会和原游戏冲突
MOD 文件必须和原游戏资源分离,否则更新游戏会清空 MOD。最稳妥的方式是让 MOD 放在独立目录(比如 Mods/MyMod1/),加载时用前缀或命名空间隔离资源 ID。别把 MOD 文件直接扔进 Data/ 或 Assets/ 里——那是自找麻烦。
常见错误:用 Directory.GetFiles("Assets/", "*.asset") 一把梭扫全目录,结果 MOD 和原版同名文件互相覆盖,加载顺序一变就崩溃。
- 所有 MOD 目录加唯一标识前缀,例如
mod_MyMod1_,避免资源 ID 冲突 - 用
AssemblyLoadContext隔离 MOD 的 DLL(如果含脚本),防止类型重复注册 - 禁止 MOD 覆盖核心配置文件(如
GameConfig.json),改用叠加式 patch 机制
如何安全地重载已加载的 MOD 资源
C# 没有真正的“卸载程序集”能力,AssemblyLoadContext.Unload() 在 .NET Core 3.0+ 才可用,且要求所有类型完全脱离引用,否则抛 InvalidOperationException: Cannot unload an assembly loaded into the default context。
所以别指望热重载 C# 脚本类;对资源(贴图、配置、音频)可以安全替换,但对行为逻辑,得设计成“可插拔组件”,运行时切换实例,而非替换类型本身。
- 用
WeakReference管理资源实例,方便 GC 回收旧对象 - 配置类统一走
JsonSerializer.Deserialize<t>()</t>,不缓存静态实例 - 若 MOD 含
ScriptableObject(Unity 场景),必须调用Resources.UnloadUnusedAssets()+ 手动DestroyImmediate()
MOD 配置文件解析时怎么处理版本兼容与字段缺失
用户会随便改 JSON,字段少一个、拼错一个、类型写成字符串却该是整数——这些都会让 JsonSerializer.Deserialize<t>()</t> 直接抛 JsonException,整个 MOD 加载失败。
不能靠 try-catch 整个反序列化过程来兜底,得让解析本身具备容错性。.NET 6+ 的 JsonSerializerOptions.PropertyNameCaseInsensitive = true 是基础,但远远不够。
- 所有配置类字段加
[JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)],允许空值 - 关键字段用可空类型(
int?、string),配合??提供默认值 - 用
JsonDocument.Parse()先粗筛结构,校验必要字段是否存在、类型是否合法,再进强类型反序列化
Unity 项目中如何避免 MOD 脚本干扰主线程调度
MOD 作者可能随手写个 while(true) { Thread.Sleep(1); },或者在 Update() 里做 heavy IO,直接拖垮帧率。你不能信任任何外部代码的写法。
Unity 的 Coroutine 和 JobSystem 都无法跨 Assembly 安全调度,所以 MOD 行为必须被“沙箱化”:不是限制语法,而是限制执行上下文。
- 禁止 MOD 直接访问
UnityEngine.Time.deltaTime或Input,统一走你暴露的只读接口(如IModContext.TickTime) - 所有 MOD 的
Update()-类回调,必须注册到你自己的调度器(ModUpdateScheduler.Run()),并设硬性超时(如 2ms/frame) - DLL 加载后检查 IL 中是否有
Thread.Start、Task.Factory.StartNew等高危调用(可用System.Reflection.Metadata静态扫描)
真正难的不是加载 MOD,而是当它出错时,你能准确定位是哪个 MOD 的哪行逻辑卡住了主线程——日志得带上下文堆栈、加载时间戳、Assembly 签名哈希,缺一不可。











