验证环境
本页内容已按 Build 740622 对应的本机 Managed DLL 和项目配置检查;尚未在游戏内完成运行验证。
Mod 开发路线
先跑通一个小补丁,再一次加入建筑、配方和本地化。每个示例都是独立项目,不需要合并成一个大工程。
编译 → 加载 → 注册内容 → 游戏内测试 → 打包1. 编译项目
游戏 DLL 不提交到仓库。编译时把 GameManagedDir 指向本机的 Managed 目录:
$managed = "D:\SteamLibrary\steamapps\common\Oxygen Not Included\OxygenNotIncluded_Data\Managed"
dotnet build .\examples\FirstMod\FirstMod.csproj `
--configuration Debug `
-p:GameManagedDir=$managed路径按自己的安装位置修改。也可以设置环境变量:
$env:ONI_GAME_MANAGED_DIR = $managed所有示例共用 examples/Directory.Build.props,项目文件里不写死 Steam 路径。
2. 先做一个最小补丁
examples/FirstMod/ 会把原版电解器的功耗改成 1W。入口代码如下:
using HarmonyLib;
using KMod;
using UnityEngine;
namespace ONITutorial.FirstMod
{
public sealed class Mod : UserMod2
{
public override void OnLoad(Harmony harmony)
{
base.OnLoad(harmony);
Debug.Log("[ONITutorial.FirstMod] Loaded");
}
[HarmonyPatch(typeof(ElectrolyzerConfig), nameof(ElectrolyzerConfig.CreateBuildingDef))]
private static class ElectrolyzerPatch
{
private static void Postfix(ref BuildingDef __result)
{
if (__result != null)
__result.EnergyConsumptionWhenActive = 1f;
}
}
}
}这里用 Postfix 修改原方法的返回对象:
- 改初始化结果:优先考虑
Postfix。 - 改输入或阻止原方法:再考虑
Prefix。 - 记录异常或清理资源:使用
Finalizer。 - 只有普通补丁无法表达需求时,才使用
Transpiler。
编译后把 examples/FirstMod/bin/ 的内容复制到:
%USERPROFILE%\Documents\Klei\OxygenNotIncluded\mods\Dev\ONITutorial.FirstMod\启动游戏,启用 Mod,打开测试存档确认功耗变化。日志位置:
%USERPROFILE%\AppData\LocalLow\Klei\Oxygen Not Included\Player.log3. 开始添加游戏内容
| 目标 | 主要入口 | 示例或章节 |
|---|---|---|
| 新建筑 | IBuildingConfig、RegisterBuilding | 新增建筑、examples/Building/ |
| 新配方 | ComplexRecipe、fabricators | 配方系统、examples/Recipe/ |
| 新文本 | STRINGS、.po | 本地化、examples/Localization/ |
| 接入已有科技 | Db.Initialize、unlockedItemIDs | 科技树、examples/ResearchExistingTech/ |
新建筑的顺序
- 在
CreateBuildingDef()定义尺寸、材料和基础属性。 - 在
ConfigureBuildingTemplate()添加Storage、Operational、EnergyConsumer等组件。 - 在
GeneratedBuildings.LoadGeneratedBuildings中注册建筑。 - 用
ModUtil.AddBuildingToPlanScreen加入建造菜单。 - 在
Db.Initialize中加入已有科技。
完整配置看 examples/Building/BuildingConfig.cs,入口看 examples/Building/Mod.cs。先使用原版动画验证流程,功能正常后再加入自己的动画资源。
配方和本地化
配方通过 fabricators 绑定工艺台。Tag 必须和目标建筑的 PrefabTag 一致,完整代码见 examples/Recipe/Mod.cs。
本地化固定走这条流程:
RegisterForTranslation → CreateLocStringKeys → 加载 translations/<locale>.po完整代码见 examples/Localization/Mod.cs。.po 文件必须出现在最终 Mod 目录的 translations/ 下。
科技解锁
先加入已有科技,不要一开始创建新科技节点:
Tech tech = Db.Get().Techs.TryGet("BasicRefinement");
if (tech != null && !tech.unlockedItemIDs.Contains(contentId))
tech.unlockedItemIDs.Add(contentId);contentId 必须是已经注册的建筑或物品 ID。只修改科技列表,不会自动生成游戏内容。
4. 一次性检查示例
有本机游戏 DLL 后运行:
./scripts/verify-examples.ps1 `
-GameManagedDir $managed脚本会逐个编译示例,并检查 DLL 引用和 mod_info.yaml 的现代写法。
5. 打包
最终 Mod 目录至少应该有:
YourMod/
├── YourMod.dll
├── mod.yaml
├── mod_info.yaml
├── translations/ # 有翻译时才需要
└── anim/ # 有动画时才需要mod.yaml 只写展示信息:
title: "Your Mod"
description: "A short description."
staticID: "AuthorName.YourMod"现代 DLL Mod 的 mod_info.yaml:
minimumSupportedBuild: 740622
version: 1.0.0
APIVersion: 2minimumSupportedBuild 换成实际测试过的 Build。没有 DLC 限制时不要添加 DLC 字段;有依赖时使用 requiredDlcIds。
6. 出错时先查这几项
| 现象 | 检查位置 |
|---|---|
| 编译失败 | GameManagedDir 和游戏 DLL 是否匹配 |
| Mod 不显示 | DLL、mod_info.yaml 是否在同一目录 |
| 补丁没效果 | 目标方法、补丁时机、是否复制了新 DLL |
| 建筑不出现 | RegisterBuilding、建造分类、科技 ID |
| 配方不显示 | fabricators Tag 和 PrefabTag |
| 文本显示 ID | STRINGS key 和 translations 目录 |
开发时不要同时启用同一个 Mod 的 Steam、Local 和 Dev 版本,否则很难判断实际加载的是哪一份。