简介:这份资源是面向Rimworld模组开发初学者与进阶爱好者的实战型项目源码包,围绕Mod结构搭建、开发工具选型、程序集构建、C#代码编写与测试等核心环节展开,帮助读者打通从零到可运行Mod的完整链路。包内共11个文件,以cs代码文件、xml数据定义、csproj工程文件为主,辅以md说明文档、html页面及gitignore等配置项,压缩包约16KB,体量轻巧但结构完整,便于直接导入Visual Studio或JetBrains Rider进行二次开发。资源以制作一把带瘟疫效果的测试枪为案例,涵盖About目录配置、Defs数据定义、Assemblies程序集、Languages本地化翻译等模块,并附有反编译游戏代码的思路,方便理解游戏内部逻辑。目前已有209人学习下载,适合希望掌握Mod目录组织、数据驱动设计与代码调试方法的开发者参考借鉴。
1. Rimworld Mod 制作:从项目源码到能跑起来的第一个补丁
很多人第一次打开 Rimworld 的 Mod 目录,看到一堆 XML 和 DLL 混在一起,第一反应是「这玩意儿能改?」。我当初也是这么想的,直到自己动手把游戏里一个物品的堆叠上限从 75 改成 500,才发现 Rimworld 的 Mod 体系其实对新手相当友好——纯 XML 就能完成大量修改,不需要写一行 C#。但问题也在这里:网上教程要么只讲 XML 不讲项目源码结构,要么直接甩一个 GitHub 仓库让你自己看,中间缺了「怎么从零搭出一个能加载的 Mod 项目」这一步。这篇内容就是补上这一步。不管你是想做一个简单的数值调整 Mod,还是准备用 Harmony 做深度补丁,下面这套流程都是我实际跑通过、能复现的路径。适合有基本文件操作能力、想认真做一个 Mod 而不是随便改改的玩家和开发者。
2. Rimworld Mod 的项目结构:About、Assemblies、Defs 各放什么
2.1 一个能加载的 Mod 最少需要哪些文件
Rimworld 加载 Mod 的逻辑很直接:扫描 Mod 文件夹,找About/About.xml,读里面的元数据,然后根据packageId注册。如果这个文件缺失或格式错误,游戏启动时会在日志里报错,Mod 列表里根本不会出现你的条目。
最小可加载结构长这样:
MyFirstMod/ ├── About/ │ └── About.xml ├── Defs/ │ └── ThingDefs/ │ └── MyItems.xml └── Assemblies/ (纯 XML Mod 不需要这个目录)About.xml是入口,必须包含以下字段:
<?xml version="1.0" encoding="utf-8"?> <ModMetaData> <name>My First Mod</name> <author>YourName</author> <packageId>yourname.myfirstmod</packageId> <supportedVersions> <li>1.5</li> </supportedVersions> <description>一个用于测试的简单 Mod。</description> </ModMetaData>packageId的命名规则是「作者名.模组名」,全小写,不能有空格。这个 ID 是唯一的,后续做兼容性判断、Mod 排序、依赖声明都靠它。我见过有人用中文或大写字母,结果游戏直接忽略整个 Mod,日志里只给一行Invalid packageId,排查起来很费时间。
supportedVersions里写你实际测试过的版本。Rimworld 每次大版本更新都会改 Def 结构,写错版本号可能导致游戏加载时直接崩溃。如果你只测过 1.5,就只写 1.5,不要为了「兼容更多」把 1.4、1.3 都塞进去。
2.2 Defs 目录的组织方式与加载顺序
Defs下面可以按类型建子目录,比如ThingDefs、RecipeDefs、HediffDefs。游戏加载时会递归扫描Defs下所有.xml文件,所以子目录名字其实不影响加载,只影响你自己找文件。但有一个坑:同一个 Def 类型下,defName不能重复。如果你新建了一个ThingDef,defName叫Steel,而原版已经有Steel,游戏会报重复定义并跳过你的整个文件。
常见做法是给自己的 Def 加前缀,比如MyMod_Steel。如果你是想修改原版物品而不是新增,不要重新定义,而是用Patch或者xpath继承。
一个典型的ThingDef新增物品:
<?xml version="1.0" encoding="utf-8"?> <Defs> <ThingDef ParentName="ResourceBase"> <defName>MyMod_AdvancedComponent</defName> <label>高级零部件</label> <description>用于高级制造的高级零部件。</description> <graphicData> <texPath>Things/Item/Resource/AdvancedComponent</texPath> <graphicClass>Graphic_Single</graphicClass> </graphicData> <stackLimit>500</stackLimit> <statBases> <MarketValue>120</MarketValue> <Mass>0.5</Mass> </statBases> <thingCategories> <li>Manufactured</li> </thingCategories> </ThingDef> </Defs>ParentName="ResourceBase"是关键。Rimworld 的 Def 系统支持继承,ResourceBase是原版定义的一个抽象模板,包含了资源类物品的通用字段(比如selectable、category、drawerType)。继承它之后,你只需要写差异部分,不用从零拼一个完整的ThingDef。如果不写ParentName,游戏会要求你补全所有必填字段,漏一个就报错。
texPath指向的是Textures目录下的路径,不带扩展名。Rimworld 会自动找.png。如果你没有自己的贴图,可以直接引用原版的路径,比如Things/Item/Resource/Component,这样至少不会出现紫色方块。
stackLimit就是堆叠上限,原版零部件是 75,这里改成 500。这个字段在ThingDef里是可选覆盖,继承自ResourceBase的默认值会被你写的值替换。
2.3 用 Harmony 做 C# 补丁时 Assemblies 怎么放
当你需要修改游戏逻辑而不是单纯改数值时,就需要写 C# 并编译成 DLL,放进Assemblies目录。Rimworld 使用 Harmony 作为运行时补丁库,你的 DLL 需要引用0Harmony.dll、Assembly-CSharp.dll、UnityEngine.dll等。
项目文件结构会变成:
MyFirstMod/ ├── About/ │ └── About.xml ├── Assemblies/ │ └── MyFirstMod.dll ├── Defs/ │ └── ... └── Source/ └── MyFirstMod/ ├── MyFirstMod.csproj └── Patch.csSource目录不是游戏加载必需的,但你应该保留它,方便后续维护。编译时把Assembly-CSharp.dll的引用路径指向 Rimworld 安装目录下的RimWorldWin64_Data/Managed/。注意不要把这些原版 DLL 复制到你的 Mod 里一起发布,那会涉及版权问题,而且游戏加载时可能冲突。
一个最简单的 Harmony 补丁示例,修改Pawn的移动速度:
using HarmonyLib; using Verse; namespace MyFirstMod { [StaticConstructorOnStartup] public static class HarmonyInit { static HarmonyInit() { var harmony = new Harmony("yourname.myfirstmod"); harmony.PatchAll(); } } [HarmonyPatch(typeof(Pawn), nameof(Pawn.Tick))] public static class Pawn_Tick_Patch { static void Postfix(Pawn __instance) { if (__instance.IsColonist && __instance.Map != null) { // 每 tick 给殖民者加一点移动进度,实际项目中应更精确 } } } }[StaticConstructorOnStartup]保证这个静态构造函数在游戏启动时执行,harmony.PatchAll()会扫描当前程序集里所有带[HarmonyPatch]的类并应用。Postfix表示在原方法执行后插入逻辑。__instance是 Harmony 的命名约定,代表被 Patch 的实例本身。
注意:Harmony 的
PatchAll()只扫描调用它的那个程序集。如果你有多个 DLL,每个都需要自己的初始化入口,或者在一个主 DLL 里手动Patch其他程序集的方法。
3. 从项目源码到可加载 Mod:编译、打包、进游戏验证
3.1 用 dotnet 命令行编译 Rimworld Mod 的完整命令
Rimworld 的 Mod 项目通常是 .NET Framework 4.7.2 或 4.8 的类库。用 Visual Studio 当然可以,但如果你只想快速验证,dotnetCLI 更直接。前提是你已经装了 .NET SDK,并且知道 Rimworld 的安装路径。
假设 Rimworld 装在D:\SteamLibrary\steamapps\common\RimWorld,项目在D:\Mods\MyFirstMod\Source\MyFirstMod,csproj文件内容如下:
<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <TargetFramework>net472</TargetFramework> <AssemblyName>MyFirstMod</AssemblyName> <RootNamespace>MyFirstMod</RootNamespace> <LangVersion>latest</LangVersion> </PropertyGroup> <ItemGroup> <Reference Include="Assembly-CSharp"> <HintPath>D:\SteamLibrary\steamapps\common\RimWorld\RimWorldWin64_Data\Managed\Assembly-CSharp.dll</HintPath> <Private>false</Private> </Reference> <Reference Include="0Harmony"> <HintPath>D:\SteamLibrary\steamapps\common\RimWorld\RimWorldWin64_Data\Managed\0Harmony.dll</HintPath> <Private>false</Private> </Reference> <Reference Include="UnityEngine.CoreModule"> <HintPath>D:\SteamLibrary\steamapps\common\RimWorld\RimWorldWin64_Data\Managed\UnityEngine.CoreModule.dll</HintPath> <Private>false</Private> </Reference> </ItemGroup> </Project><Private>false</Private>很重要。它告诉编译器不要把这些 DLL 复制到输出目录。如果你忘了写,编译出来的Assemblies文件夹里会多出一堆原版 DLL,游戏加载时可能因为版本不一致直接崩溃。
编译命令:
cd D:\Mods\MyFirstMod\Source\MyFirstMod dotnet build -c Release -o ..\..\Assemblies-c Release用发布配置,-o指定输出目录直接指向 Mod 的Assemblies文件夹。编译成功后,MyFirstMod.dll会出现在Assemblies下。
如果你在编译时看到error CS0246: 找不到类型或命名空间名,九成是HintPath写错了,或者 Rimworld 版本更新后 DLL 名字变了。去Managed目录下dir一下,确认文件名。
3.2 打包成 Steam 创意工坊可识别的目录格式
Rimworld 的 Mod 加载不依赖压缩包,它直接读文件夹。所以「打包」其实就是确保目录结构正确,然后整个文件夹丢进Mods目录。
标准发布结构:
MyFirstMod/ ├── About/ │ ├── About.xml │ └── Preview.png (可选,创意工坊缩略图) ├── Assemblies/ │ └── MyFirstMod.dll ├── Defs/ │ └── ThingDefs/ │ └── MyItems.xml ├── Languages/ │ └── ChineseSimplified/ │ └── DefInjected/ │ └── ThingDef/ │ └── MyItems.xml └── Textures/ └── Things/ └── Item/ └── Resource/ └── AdvancedComponent.pngLanguages目录用于本地化。如果你在ThingDef里直接写了中文label,游戏里能显示,但其他语言的玩家看到的也是中文。正规做法是在Defs里用英文 key,然后在Languages/ChineseSimplified/DefInjected/ThingDef/MyItems.xml里写翻译:
<?xml version="1.0" encoding="utf-8"?> <LanguageData> <MyMod_AdvancedComponent.label>高级零部件</MyMod_AdvancedComponent.label> <MyMod_AdvancedComponent.description>用于高级制造的高级零部件。</MyMod_AdvancedComponent.description> </LanguageData>这样游戏会根据当前语言自动切换。如果你只做中文 Mod,直接写在Defs里也能跑,但后续想加英文支持就得回头改。
Preview.png是创意工坊上传时用的缩略图,尺寸建议 512x512,放在About目录下。Steam 上传工具会自动读取。
3.3 进游戏后 Mod 不显示或报错时看哪个日志
Rimworld 的日志文件在%USERPROFILE%\AppData\LocalLow\Ludeon Studios\RimWorld by Ludeon Studios\Player.log。每次启动游戏都会覆盖这个文件。如果你在 Mod 列表里看不到自己的 Mod,或者加载时红字报错,第一时间打开这个日志,搜索你的packageId或 Mod 文件夹名。
常见错误对照:
| 日志关键词 | 原因 | 解决 |
|---|---|---|
Invalid packageId | About.xml里packageId格式不对 | 改成全小写、无空格、带点号 |
Could not load file or assembly | DLL 引用了不存在的依赖 | 检查Assemblies下是否缺 DLL,或HintPath是否失效 |
Duplicate defName | 你的 Def 和原版或其他 Mod 重名 | 加前缀,或改用 Patch |
XML error | XML 语法错误,比如标签未闭合 | 用浏览器打开 XML 验证格式 |
Harmony patch failed | 目标方法签名变了 | 确认游戏版本和Assembly-CSharp.dll版本一致 |
日志里报错的行号是 XML 文件的行号,不是 C# 的。如果你在Defs里写了注释<!-- -->,行号会算上注释行,别被误导。
提示:开发阶段建议在游戏设置里打开「开发者模式」,然后在游戏内按
~打开控制台,输入log可以实时看日志,不用切出去。
4. Rimworld Mod 制作避坑:从血泪经验里挑出的 5 个翻车点
4.1 现象:Mod 加载后游戏卡在加载界面,日志无报错
原因:About.xml里supportedVersions写了多个版本,但游戏当前版本不在列表里,或者packageId和文件夹名冲突。Rimworld 在加载时会尝试解析所有 Mod 的元数据,如果某个 Mod 的About.xml格式正确但版本不匹配,它不会报错,而是静默跳过,但可能因为依赖关系导致加载流程卡住。
解决:只写你实际测试过的版本号。如果你不确定,先只写当前版本,比如<li>1.5</li>。另外,Mod 文件夹名和packageId不需要一致,但建议保持一致,方便排查。
4.2 现象:XML 里改了数值,进游戏没生效
原因:你新建了一个ThingDef,但defName和原版重复,游戏加载时用了原版的,你的被覆盖了。或者你用了Patch但xpath写错了,补丁没匹配到目标节点。
解决:先确认你的defName是唯一的。如果是修改原版,用Patch而不是重新定义。xpath可以用在线工具验证,或者先在游戏里用开发者模式的「Def 查看器」找到目标 Def 的完整路径。
4.3 现象:Harmony 补丁编译通过,但运行时没执行
原因:[StaticConstructorOnStartup]的类没有被任何地方引用,或者PatchAll()调用的程序集不对。Rimworld 只会加载Assemblies下的 DLL,但不会自动执行里面的静态构造函数,除非你用了[StaticConstructorOnStartup]并且这个类在加载的程序集里。
解决:确认HarmonyInit类在MyFirstMod.dll里,并且PatchAll()没有传参数(默认扫描当前程序集)。如果你有多个 DLL,每个都需要自己的HarmonyInit。
4.4 现象:创意工坊上传后,订阅者说 Mod 不工作
原因:你上传时只传了About和Defs,忘了Assemblies或Textures。Steam 创意工坊上传工具默认只包含你选中的文件夹,如果你在About里写了Preview.png但没上传,订阅者看到的缩略图是空白的。
解决:上传前在本地把整个 Mod 文件夹复制到 Rimworld 的Mods目录,启动游戏确认能加载、能生效。然后再用上传工具,确保所有子目录都被勾选。
4.5 现象:游戏更新后,之前能用的 Mod 报红字
原因:Rimworld 大版本更新会改Assembly-CSharp.dll里的方法签名,你的 Harmony 补丁目标方法可能被重命名或参数变了。XML Def 的结构也可能调整,比如某个字段从必填变成可选,或者反过来。
解决:每次游戏更新后,重新编译你的 DLL,并检查About.xml里的supportedVersions。如果目标方法签名变了,去Assembly-CSharp.dll里用反编译工具(比如 dnSpy)找新签名,更新[HarmonyPatch]特性。
5. 用项目源码做进阶:Patch 原版 Def 与多 Mod 兼容的写法
5.1 用 xpath 补丁修改原版物品的堆叠上限
直接重新定义ThingDef会覆盖原版,导致其他 Mod 的修改失效。正确做法是用Patch:
<?xml version="1.0" encoding="utf-8"?> <Patch> <Operation Class="PatchOperationReplace"> <xpath>/Defs/ThingDef[defName="Component"]/stackLimit</xpath> <value> <stackLimit>500</stackLimit> </value> </Operation> </Patch>PatchOperationReplace会找到defName="Component"的ThingDef,把stackLimit替换成 500。如果原版没有stackLimit字段,这个操作会失败,日志里报xpath没匹配到。更安全的写法是PatchOperationAdd或PatchOperationConditional。
xpath的语法是标准 XPath 1.0。/Defs/ThingDef[defName="Component"]表示从根节点Defs下找所有ThingDef,筛选defName等于Component的那个。注意defName是区分大小写的。
5.2 多 Mod 兼容:用 Mod 依赖和加载顺序控制
如果你的 Mod 依赖另一个 Mod 的 Def,需要在About.xml里声明:
<modDependencies> <li> <packageId>author.othermod</packageId> <displayName>Other Mod</displayName> </li> </modDependencies> <loadAfter> <li>author.othermod</li> </loadAfter>modDependencies告诉游戏:如果订阅者没装author.othermod,在 Mod 列表里给你的 Mod 标红,提示缺少依赖。loadAfter控制加载顺序,确保你的 Patch 在目标 Mod 之后执行。
如果你要修改的 Def 可能被多个 Mod 修改,用PatchOperationFindMod做条件判断:
<Operation Class="PatchOperationFindMod"> <mods> <li>author.othermod</li> </mods> <match Class="PatchOperationReplace"> <xpath>/Defs/ThingDef[defName="MyMod_Item"]/stackLimit</xpath> <value> <stackLimit>1000</stackLimit> </value> </match> <nomatch Class="PatchOperationReplace"> <xpath>/Defs/ThingDef[defName="MyMod_Item"]/stackLimit</xpath> <value> <stackLimit>500</stackLimit> </value> </nomatch> </Operation>这样如果检测到author.othermod存在,就用 1000,否则用 500。PatchOperationFindMod是 Rimworld 1.1 之后加入的,老版本没有。
5.3 验证补丁是否生效:开发者模式下的 Def 查看器
进游戏后按~打开控制台,输入defcomponent或者直接在开发者菜单里找「Def 查看器」。搜索你的defName,看stackLimit字段的值是不是你改的。如果没变,说明 Patch 没执行。
另一个方法是看日志。PatchOperation执行成功不会写日志,但失败会写Patch operation failed并附带xpath。如果你在About.xml里开了<debug>true</debug>,日志会更详细。
我自己的习惯是:每次改完 Patch,先不启动游戏,用xmllint验证 XML 格式,然后进游戏用开发者模式确认。这样能省掉很多反复重启的时间。
希望帮到你。
本文还有配套的精品资源,点击获取