核心哲学: 游戏本体应该只是一个 “引擎” (Engine),而所有的官方内容(塔、怪、装备)都应该被视为 “官方 Mod” (Core Mod)。 当你把官方内容也当成 Mod 来加载时,Mod 支持就自然完成了。
Tower_Fire.json 里的 "damage": 10 改成 "damage": 9999。.png / .wav / .txt。
Tower_Fire.png 替换掉,游戏里火塔就变成了皮卡丘。在 Application.streamingAssetsPath 下建立 Mods 文件夹。
Vampirefall_Data/
└── StreamingAssets/
└── Mods/
├── Core/ (官方内容)
│ ├── mod_info.json
│ ├── Towers/
│ │ ├── FireTower.json
│ │ └── FireTower.png
│ └── Localization/
│ └── en.json
└── MyCrazyMod/ (玩家Mod)
├── mod_info.json (定义依赖、版本)
├── Towers/
│ └── SuperTower.json
└── Scripts/
└── main.lua
原则: 后加载覆盖先加载 (Last Write Wins)。
Mods 目录下所有文件夹。mod_info.json 里的 order 或 dependencies 排序。
FireTower.json。FireTower.json(如果存在)。{"damage": 999},则只修改 damage 字段,保留其他字段不变 (Deep Merge)。这是一个简单的 JSON Mod 加载器伪代码。
[System.Serializable]
public class TowerDefinition {
public string id;
public float damage;
public string spritePath;
}
public class ModManager : MonoBehaviour {
// 存储所有塔的定义
public Dictionary<string, TowerDefinition> towerDatabase = new Dictionary<string, TowerDefinition>();
public void LoadAllMods() {
string modsRoot = Path.Combine(Application.streamingAssetsPath, "Mods");
var directories = Directory.GetDirectories(modsRoot);
// 1. 遍历每个 Mod 文件夹
foreach (var dir in directories) {
// 2. 加载 Towers 目录下的 JSON
var towerFiles = Directory.GetFiles(Path.Combine(dir, "Towers"), "*.json");
foreach (var file in towerFiles) {
string json = File.ReadAllText(file);
// 3. 解析
TowerDefinition def = JsonUtility.FromJson<TowerDefinition>(json);
// 4. 注册/覆盖
if (towerDatabase.ContainsKey(def.id)) {
// 简单覆盖 (实际项目建议做 Deep Merge)
towerDatabase[def.id] = def;
Debug.Log($"[Mod] Overwrote tower: {def.id}");
} else {
towerDatabase.Add(def.id, def);
Debug.Log($"[Mod] Added tower: {def.id}");
}
}
}
}
// 运行时获取图片
public Sprite LoadSprite(string partialPath) {
// 逻辑:去所有 Mod 文件夹里找这个图,返回优先级最高的那张
// ... 使用 Texture2D.LoadImage() ...
}
}
Harmony 是一个 C# 库,用于在 运行时 (Runtime) 替换或修改现有的 .NET 方法。即使你没有源码,只要你有 DLL,你就能修改它。
Harmony 不修改磁盘上的 DLL 文件,而是在内存中修改机器码。
它利用反射找到目标方法 TargetMethod,然后插入三种“补丁”:
return false 跳过原方法)。如果你决定官方支持 C# Mod (像 RimWorld 那样),你可以内置 Harmony。
0Harmony.dll 放进 Plugins。.dll。// 在游戏启动时执行
void LoadScriptMods() {
var modDlls = Directory.GetFiles(modPath, "*.dll");
foreach(var dll in modDlls) {
var assembly = Assembly.LoadFile(dll);
// Harmony 会自动扫描该 DLL 中带有 [HarmonyPatch] 标签的类并应用
var harmony = new Harmony("com.vampirefall.mod." + assembly.GetName().Name);
harmony.PatchAll(assembly);
}
}
假设官方代码里有一个计算伤害的方法:
// 官方代码
public class DamageCalculator {
public int Calculate(int baseDmg) {
return baseDmg * 2;
}
}
Modder 想把伤害改成 10倍,他只需要写一个 DLL:
// Modder 代码
[HarmonyPatch(typeof(DamageCalculator), "Calculate")]
public class DamagePatch {
// Postfix: 在原方法返回后,修改结果
static void Postfix(ref int __result) {
__result *= 10; // 把结果乘以 10
}
}
| 维度 | 优点 | 缺点 | | :— | :— | :— | | 能力 | 无限。可以修改游戏里任何一行私有代码。 | 不安全。Mod 可以轻易崩溃游戏,甚至写病毒。 | | 性能 | 极高。修改的是 JIT 后的机器码,几乎无损耗。 | 如果 100 个 Mod 同时 Patch 一个方法,调用栈会很深。 | | 维护 | 社区生态成熟 (RimWorld, Cities: Skylines 都用它)。 | 如果官方更新改了方法名,Mod 直接失效 (红字报错)。 |
对于 Vampirefall:
如果 Harmony 是手术刀,BepInEx (Bepis Injector Extensible) 就是全套的手术台。
它是一个开源的 Unity / .NET 游戏插件框架。
winhttp.dll 或 version.dll 的劫持技术。当 Windows 启动游戏 exe 时,会优先加载游戏目录下的这个“假 DLL”。BepInEx/plugins 目录下的所有用户 DLL,并自动执行它们。其实,官方什么都不用做,BepInEx 就能工作。但为了让社区更舒服,建议做以下几点:
DamageCalculator 变成了 A 类,Calculate 变成了 b 方法,Modder 会疯掉。Tower, Enemy)的方法签名尽量不要频繁改动。如果改了,社区 Mod 会全部报错。如果你想更进一步,可以直接把 BepInEx 的功能“吸纳”进来:
-enable-mods 参数,官方代码里检测到参数就加载 StreamingAssets 里的 DLL。Vampirefall.Modding.dll,里面全是接口 (ITowerMod, IEnemyMod)。Modder 引用这个 DLL 写代码,比用 Harmony 瞎猜要安全得多。"tower_fire") 而不是枚举 (Enum.TowerFire)。枚举是编译死的,字符串是灵活的。System.IO 里的删除/写入 API(除了 Mod 自己的临时文件夹),防止恶意 Mod 格式化玩家硬盘。如果你打算上 Steam,集成 Workshop 是最方便的。
SteamUGC.SubscribeItem() 下载 Mod 到本地,然后你的 ModManager 去 Steam 的下载目录加载即可。对于 Vampirefall:
ResourceManager,支持从 StreamingAssets 加载 .png 覆盖默认 Sprite。