Skip to content

验证环境

本页内容已按 Build 740622 对应的本机 Managed DLL 和项目配置检查;尚未在游戏内完成运行验证。

Mod 开发路线

先跑通一个小补丁,再一次加入建筑、配方和本地化。每个示例都是独立项目,不需要合并成一个大工程。

text
编译 → 加载 → 注册内容 → 游戏内测试 → 打包

1. 编译项目

游戏 DLL 不提交到仓库。编译时把 GameManagedDir 指向本机的 Managed 目录:

powershell
$managed = "D:\SteamLibrary\steamapps\common\Oxygen Not Included\OxygenNotIncluded_Data\Managed"

dotnet build .\examples\FirstMod\FirstMod.csproj `
  --configuration Debug `
  -p:GameManagedDir=$managed

路径按自己的安装位置修改。也可以设置环境变量:

powershell
$env:ONI_GAME_MANAGED_DIR = $managed

所有示例共用 examples/Directory.Build.props,项目文件里不写死 Steam 路径。

2. 先做一个最小补丁

examples/FirstMod/ 会把原版电解器的功耗改成 1W。入口代码如下:

csharp
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/ 的内容复制到:

text
%USERPROFILE%\Documents\Klei\OxygenNotIncluded\mods\Dev\ONITutorial.FirstMod\

启动游戏,启用 Mod,打开测试存档确认功耗变化。日志位置:

text
%USERPROFILE%\AppData\LocalLow\Klei\Oxygen Not Included\Player.log

3. 开始添加游戏内容

目标主要入口示例或章节
新建筑IBuildingConfigRegisterBuilding新增建筑examples/Building/
新配方ComplexRecipefabricators配方系统examples/Recipe/
新文本STRINGS.po本地化examples/Localization/
接入已有科技Db.InitializeunlockedItemIDs科技树examples/ResearchExistingTech/

新建筑的顺序

  1. CreateBuildingDef() 定义尺寸、材料和基础属性。
  2. ConfigureBuildingTemplate() 添加 StorageOperationalEnergyConsumer 等组件。
  3. GeneratedBuildings.LoadGeneratedBuildings 中注册建筑。
  4. ModUtil.AddBuildingToPlanScreen 加入建造菜单。
  5. Db.Initialize 中加入已有科技。

完整配置看 examples/Building/BuildingConfig.cs,入口看 examples/Building/Mod.cs。先使用原版动画验证流程,功能正常后再加入自己的动画资源。

配方和本地化

配方通过 fabricators 绑定工艺台。Tag 必须和目标建筑的 PrefabTag 一致,完整代码见 examples/Recipe/Mod.cs

本地化固定走这条流程:

text
RegisterForTranslation → CreateLocStringKeys → 加载 translations/<locale>.po

完整代码见 examples/Localization/Mod.cs.po 文件必须出现在最终 Mod 目录的 translations/ 下。

科技解锁

先加入已有科技,不要一开始创建新科技节点:

csharp
Tech tech = Db.Get().Techs.TryGet("BasicRefinement");
if (tech != null && !tech.unlockedItemIDs.Contains(contentId))
    tech.unlockedItemIDs.Add(contentId);

contentId 必须是已经注册的建筑或物品 ID。只修改科技列表,不会自动生成游戏内容。

4. 一次性检查示例

有本机游戏 DLL 后运行:

powershell
./scripts/verify-examples.ps1 `
  -GameManagedDir $managed

脚本会逐个编译示例,并检查 DLL 引用和 mod_info.yaml 的现代写法。

5. 打包

最终 Mod 目录至少应该有:

text
YourMod/
├── YourMod.dll
├── mod.yaml
├── mod_info.yaml
├── translations/    # 有翻译时才需要
└── anim/            # 有动画时才需要

mod.yaml 只写展示信息:

yaml
title: "Your Mod"
description: "A short description."
staticID: "AuthorName.YourMod"

现代 DLL Mod 的 mod_info.yaml

yaml
minimumSupportedBuild: 740622
version: 1.0.0
APIVersion: 2

minimumSupportedBuild 换成实际测试过的 Build。没有 DLC 限制时不要添加 DLC 字段;有依赖时使用 requiredDlcIds

详细规则见 Mod 打包与发布多版本兼容性

6. 出错时先查这几项

现象检查位置
编译失败GameManagedDir 和游戏 DLL 是否匹配
Mod 不显示DLL、mod_info.yaml 是否在同一目录
补丁没效果目标方法、补丁时机、是否复制了新 DLL
建筑不出现RegisterBuilding、建造分类、科技 ID
配方不显示fabricators Tag 和 PrefabTag
文本显示 IDSTRINGS key 和 translations 目录

开发时不要同时启用同一个 Mod 的 Steam、Local 和 Dev 版本,否则很难判断实际加载的是哪一份。