1. 项目概述:为什么你需要BepInEx?
如果你玩过一些基于Unity引擎开发的PC游戏,比如《雨中冒险2》、《星露谷物语》的某些大型Mod,或者想给《太吾绘卷》添加一些自定义功能,那你很可能已经间接接触过BepInEx了。简单来说,BepInEx是一个专门为Unity游戏(也包括XNA、FNA等框架的游戏)设计的插件框架和补丁工具。它的核心作用,是让你能够在不修改游戏原始文件的前提下,向游戏里“注入”你自己编写的代码,从而实现修改游戏逻辑、添加新功能、修复Bug,也就是我们常说的“Mod制作”。
为什么这件事很重要?对于玩家和Mod开发者而言,直接修改游戏本体的DLL文件是极其危险且不稳定的操作,一次更新就可能让所有修改失效。BepInEx提供了一套标准化的“插槽”和加载机制,它会在游戏启动时先行介入,建立一个安全、隔离的环境来加载和管理你的插件。这意味着你的Mod可以独立于游戏本体,更容易安装、卸载,也更容易兼容其他Mod。对于开发者来说,它抽象掉了底层复杂的注入和补丁流程,让你可以更专注于Mod功能本身的逻辑实现。现在,我们将在5分钟内,从零开始,带你走通从下载到运行第一个BepInEx插件的完整流程。
2. 环境准备与核心概念解析
在动手之前,我们需要先理清几个关键概念,这能帮你避免后续很多困惑。BepInEx不仅仅是一个加载器,它是一整套工具链。
2.1 BepInEx的组成模块
一个标准的BepInEx发布包通常包含以下核心部分:
- BepInEx/core: 这是框架的心脏,包含了插件加载、日志管理、配置管理等核心运行时库。你的插件将依赖这些库。
- doorstop_config.ini: 这是BepInEx的“入口点”配置文件。它通过一个名为
winhttp.dll(Windows下)的代理DLL,在游戏启动时劫持控制权,将执行流程引导至BepInEx。这是实现无侵入式注入的关键。 - BepInEx/plugins: 这是默认的插件存放目录。你编写或下载的插件(通常是
.dll文件)放在这里,游戏启动时会被自动加载。 - BepInEx/config: 插件生成的配置文件会存放在这里,方便玩家按需调整Mod参数。
- BepInEx/patchers: 用于存放“补丁器”(Patcher)插件。这类插件在游戏程序集加载到内存后、但尚未执行前运行,用于对游戏代码进行更底层的、全局性的修改,普通插件开发者较少直接使用。
- BepInEx/logs: 运行日志输出目录。当你的插件出现问题时,查看这里的
LogOutput.log是首要的排查手段。
2.2 版本选择:Mono vs IL2CPP
这是新手最容易踩坑的地方。Unity游戏有两种主要的脚本后端:Mono和IL2CPP。它们直接影响你该下载哪个版本的BepInEx。
- Unity Mono: 较老的、使用即时编译(JIT)的后端。游戏代码以
.dll程序集形式存在,可以被直接分析和修改。绝大多数使用BepInEx的Mod游戏都基于此后端。针对它的BepInEx版本最成熟、最稳定。 - Unity IL2CPP: 较新的、使用提前编译(AOT)的后端。游戏代码被转换为C++,再编译为本地机器码,原始.NET程序集信息大量丢失,修改难度剧增。BepInEx为此提供了实验性支持(BepInEx 6及以上版本),但需要额外的工具(如Cpp2IL)来反编译和生成伪程序集,流程更复杂,兼容性也可能存在问题。
如何判断你的游戏是哪种后端?
- 查看游戏安装目录。如果存在
<游戏名>_Data/Managed/文件夹,并且里面有Assembly-CSharp.dll等文件,那基本就是Mono后端。 - 如果存在
<游戏名>_Data/Il2CppData/等文件夹,并且Managed文件夹内空空如也或只有少量系统DLL,那很可能就是IL2CPP后端。 - 也可以查阅游戏社区或Mod社区的说明。
对于本次快速上手指南,我们将以最普遍、最稳定的Unity Mono游戏为目标,使用BepInEx 5版本进行演示。这是社区生态最完善、文档最全的搭配。
注意:请务必从GitHub的BepInEx官方仓库(Releases页面)或游戏Mod社区指定的地址下载BepInEx。使用来路不明的打包版本可能有安全风险。
3. 5分钟极速安装与部署实战
理论清晰后,我们进入实战环节。整个过程就像把一份工具包解压到正确的位置。
3.1 第一步:定位游戏根目录
找到你的游戏安装位置。例如,Steam游戏可以在库中右键游戏属性,在“本地文件”选项卡中点击“浏览本地文件”。这个打开的文件夹,就是游戏的根目录(通常包含游戏主程序.exe和<游戏名>_Data文件夹)。
3.2 第二步:下载并解压BepInEx
- 访问 BepInEx 在 GitHub 的 Releases 页面。
- 对于 Mono 游戏,下载
BepInEx_x64_5.4.23.5.zip这样的文件(版本号可能更新,选择最新的5.x版本)。x64对应64位游戏,如果你的游戏是32位的,则选择x86版本。 - 将下载的ZIP文件中的所有内容,直接解压到上一步找到的游戏根目录下。解压时,确保文件结构得以保留。
- 解压后,你的游戏根目录下应该会新增
BepInEx、doorstop_config.ini、winhttp.dll等文件和文件夹。
3.3 第三步:首次运行与验证
- 像平常一样,通过Steam或游戏启动器启动游戏。第一次运行会稍慢一些,因为BepInEx在进行初始化工作。
- 游戏正常启动并进入主菜单后,将其关闭。
- 再次打开游戏根目录下的
BepInEx文件夹。如果你看到里面新生成了config、cache、plugins等文件夹,并且logs文件夹下产生了LogOutput.log文件,那么恭喜你,BepInEx框架已经成功安装并运行了!
这个日志文件至关重要。用文本编辑器打开它,如果看到类似[Message: BepInEx] BepInEx 5.4.23.5 - <游戏名>以及后续加载核心组件的成功信息,没有大量红色错误,就证明框架运转正常。
3.4 第四步:安装你的第一个插件
现在,框架已经就绪,可以“装载”功能模块了。
- 去你喜欢的游戏Mod网站(如Nexus Mods, GitHub等)找到一个为你的游戏制作的、基于BepInEx的插件。它通常是一个
.dll文件,有时会附带一个说明文档(README)。 - 根据插件作者的说明,通常只需要将
.dll文件复制到BepInEx/plugins文件夹下。有些插件可能需要在plugins下再建立以作者命名的子文件夹(例如BepInEx/plugins/AuthorName/PluginName.dll),请遵循具体插件的安装指南。 - 再次启动游戏。插件应该会自动加载。很多插件会在游戏内通过按某个功能键(如F1、F2)来打开配置面板,或者在屏幕一角显示加载成功的提示。
至此,你已经在5分钟内完成了从零到一的BepInEx框架部署和插件安装。但这只是开始,如果你想从“使用者”变为“创造者”,下面我们将深入插件开发的核心。
4. 开发环境搭建与第一个Hello World插件
要编写BepInEx插件,你需要一个基础的.NET开发环境。我们以Visual Studio 2022社区版(免费)为例。
4.1 创建插件项目
- 打开Visual Studio,创建新项目,选择“类库(.NET Framework)”模板。注意,目标框架版本至关重要。你需要查看游戏使用的Unity版本。一个安全的通用选择是.NET Framework 4.7.2或.NET 4.8,因为大多数Unity Mono游戏都兼容此版本。选择错误的框架版本会导致插件无法加载。
- 为项目起名,例如
MyFirstBepInExPlugin。 - 创建项目后,你需要通过NuGet包管理器添加必要的引用。右键项目 -> “管理NuGet程序包”。搜索并安装
BepInEx.Core。这是插件的核心依赖。切勿安装BepInEx或BepInEx.Base,那些是完整框架包,用于开发BepInEx本身而非插件。
4.2 编写插件核心代码
安装完BepInEx.Core后,你就可以开始编写插件了。一个最基础的插件结构如下:
using BepInEx; using BepInEx.Logging; using HarmonyLib; using UnityEngine; // 1. 定义插件元数据 [BepInPlugin(PluginGUID, PluginName, PluginVersion)] public class MyFirstPlugin : BaseUnityPlugin // 2. 继承BaseUnityPlugin { // 3. 定义插件常量 public const string PluginGUID = "com.myname.myfirstplugin"; public const string PluginName = "我的第一个插件"; public const string PluginVersion = "1.0.0"; // 4. 内部日志记录器 internal static ManualLogSource Log; // 5. Awake方法:插件加载时的入口点 private void Awake() { // 初始化日志记录器 Log = Logger; // 输出一条日志,证明插件已加载 Log.LogInfo($"插件 {PluginName} v{PluginVersion} 加载成功!"); // 尝试应用Harmony补丁(如果需要修改游戏代码) Harmony.CreateAndPatchAll(typeof(MyFirstPlugin).Assembly); // 这里可以添加你的初始化代码,例如订阅游戏事件、创建GameObject等 } // 6. Update方法(可选):如果继承了BaseUnityPlugin,可以像MonoBehaviour一样使用Update // private void Update() { ... } }代码解析:
[BepInPlugin]属性:这是插件的“身份证”,BepInEx通过它来识别和加载插件。PluginGUID必须是全局唯一的,通常使用反向域名格式。- 继承
BaseUnityPlugin:这为你提供了日志记录器 (Logger)、配置 (Config) 等便利功能,并允许你使用Awake(),Start(),Update()等Unity生命周期方法。 Awake()方法:这是插件主要的初始化入口。在这里进行日志输出、Harmony补丁应用、事件订阅等操作。- Harmony:这是一个强大的库,用于在运行时对游戏原有方法进行打补丁(前置、后置、绕行)。它是实现大多数游戏功能修改的核心工具。上面代码中
Harmony.CreateAndPatchAll会搜索当前程序集中所有标注了Harmony属性的类并自动应用补丁。
4.3 编译与部署
- 在Visual Studio中按
F6或点击“生成解决方案”来编译项目。 - 编译成功后,在项目目录的
bin/Debug或bin/Release文件夹下找到生成的.dll文件(例如MyFirstBepInExPlugin.dll)。 - 将这个
.dll文件复制到目标游戏的BepInEx/plugins目录下。 - 启动游戏,查看
BepInEx/logs/LogOutput.log。你应该能看到类似[Info : MyFirstPlugin] 插件 我的第一个插件 v1.0.0 加载成功!的日志输出。恭喜,你的第一个插件已经生效了!
这个插件目前除了打印日志什么也没做,但它已经搭建起了所有必要的基础。接下来,我们将赋予它真正的“力量”。
5. 核心技能:使用Harmony修改游戏行为
Harmony是BepInEx生态中最强大的武器之一。它允许你在不拥有游戏源代码的情况下,修改其方法的执行逻辑。其核心思想是“打补丁”。
5.1 Harmony补丁类型
主要有三种补丁:
- 前缀补丁 (Prefix):在目标方法执行前运行。可以读取/修改方法的参数,甚至可以完全跳过原始方法的执行。
- 后缀补丁 (Postfix):在目标方法执行后运行。可以读取/修改方法的返回值,或者访问执行后的状态。
- 绕行补丁 (Transpiler):最复杂的一种,直接操作目标方法的IL指令(中间语言),进行最底层的修改。通常用于修改算法逻辑或绕过某些检查。
5.2 实战:修改玩家金币数量
假设我们想修改一个游戏,让玩家每次获得金币时,实际获得双倍。我们首先需要知道游戏内处理金币增加的方法是什么。这需要借助一些工具(如dnSpy, ILSpy)来反编译游戏的Assembly-CSharp.dll进行分析。假设我们找到了一个方法:
// 假设的游戏原始代码 public class PlayerInventory { public void AddGold(int amount) { this.gold += amount; UpdateGoldUI(); } }我们的目标是修改AddGold方法,让传入的amount翻倍。
- 创建Harmony补丁类在插件项目中新建一个类,例如
GoldPatch。
using HarmonyLib; [HarmonyPatch(typeof(PlayerInventory))] // 指定要修补的类 [HarmonyPatch(nameof(PlayerInventory.AddGold))] // 指定要修补的方法 class GoldPatch { // 这是一个前缀补丁 [HarmonyPrefix] static void Prefix(ref int amount) // 参数必须与原始方法匹配,使用ref关键字可以修改传入值 { // 将金币数量翻倍 amount *= 2; // 可以在这里记录日志 MyFirstPlugin.Log.LogInfo($"金币获取翻倍生效!原始值已修改为: {amount}"); } }应用补丁回到我们插件主类的
Awake()方法中,确保调用了Harmony.CreateAndPatchAll。它会自动发现并应用所有带有[HarmonyPatch]属性的类。测试编译新的插件DLL,替换旧的,启动游戏。当游戏内调用
PlayerInventory.AddGold(100)时,由于前缀补丁的介入,实际传入方法的amount会变成200,玩家最终获得200金币。
重要心得:使用Harmony时,稳定性和兼容性是首要考虑因素。你的补丁必须能够优雅地处理所有可能的输入和游戏状态。一个糟糕的补丁可能导致游戏崩溃或与其他Mod冲突。始终在补丁方法内部进行空值检查、边界检查,并尽量使用
try-catch包裹可能出错的逻辑。此外,补丁方法的命名(Prefix,Postfix)和签名(参数、返回值)必须严格遵循Harmony的约定,否则补丁将无法正确应用。
6. 插件配置与用户交互
一个成熟的插件应该允许用户自定义其行为。BepInEx提供了内置的配置系统。
6.1 创建与绑定配置项
在插件主类的Awake()方法中,我们可以定义配置:
private void Awake() { Log = Logger; // 1. 定义配置:金币倍数,默认值2.0,描述信息 Config.Bind<float>("通用设置", // 配置章节 "GoldMultiplier", // 配置项键名 2.0f, // 默认值 "获取金币的倍数。设置为1.0即为原版。"); // 描述 // 2. 应用Harmony补丁 Harmony.CreateAndPatchAll(typeof(MyFirstPlugin).Assembly); }6.2 在补丁中使用配置
修改之前的GoldPatch类,从配置中读取倍数:
[HarmonyPatch(typeof(PlayerInventory))] [HarmonyPatch(nameof(PlayerInventory.AddGold))] class GoldPatch { [HarmonyPrefix] static void Prefix(ref int amount) { // 从插件的Config中获取配置值 // 注意:这里需要能访问到插件实例的Config。一种常见做法是将Config设为静态,或在补丁类中通过插件实例访问。 // 假设我们在MyFirstPlugin类中公开了一个静态的ConfigEntry引用: // public static ConfigEntry<float> GoldMultiplierConfig; // 并在Awake中初始化:GoldMultiplierConfig = Config.Bind<float>(...); float multiplier = MyFirstPlugin.GoldMultiplierConfig.Value; int originalAmount = amount; amount = (int)(amount * multiplier); MyFirstPlugin.Log.LogInfo($"金币获取修改:{originalAmount} x {multiplier} = {amount}"); } }6.3 生成与编辑配置文件
插件首次运行后,会在BepInEx/config目录下生成一个以你的PluginGUID命名的.cfg文件(例如com.myname.myfirstplugin.cfg)。用户可以用任何文本编辑器打开它进行修改:
[通用设置] ## 获取金币的倍数。设置为1.0即为原版。 # Setting type: Single # Default value: 2 GoldMultiplier = 3.5修改后保存,无需重启游戏,很多插件支持配置热重载。BepInEx的配置系统会自动处理值的读取和类型转换。
7. 进阶话题与调试技巧
当你开始开发更复杂的插件时,会遇到更多挑战。
7.1 处理游戏事件
许多游戏逻辑由事件驱动。你可以通过查找游戏中的事件(如OnPlayerDamaged,OnItemPurchased)并订阅它们来注入逻辑。这通常需要结合Harmony补丁来挂钩事件触发器,或者使用反射来访问游戏内部的事件委托。
7.2 创建游戏内UI
对于需要复杂交互的Mod,你可能需要创建图形界面。在Unity Mono游戏中,你可以直接使用Unity的IMGUI(即时模式GUI)或更高级的UI系统。
- IMGUI:简单快速,适合调试面板和简单配置。在插件类的
OnGUI()方法中调用GUI.Label(),GUI.Button()等即可。 - UGUI:更现代、更强大,可以创建复杂的界面。但这通常需要你通过AssetBundle加载UI预制体,或者使用像
UnityExplorer这样的第三方库来辅助创建,过程更为复杂。
7.3 调试与日志排查
日志是你的第一道防线。合理使用Log.LogDebug,Log.LogInfo,Log.LogWarning,Log.LogError在不同场景输出信息。
- 开启开发者控制台:有些游戏内置控制台,或者可以通过其他Mod(如
DebugToolkit)启用。控制台可以实时显示日志和运行命令。 - 使用Visual Studio附加调试:对于深度调试,你可以将Visual Studio的调试器附加到游戏进程上。这需要启用Unity脚本调试,并在游戏启动时传入特定参数,流程较为复杂,但对于查找复杂Bug无可替代。
- 分析崩溃日志:如果游戏崩溃,查看
BepInEx/logs/LogOutput.log末尾的堆栈跟踪信息,它能精准定位到是哪一行插件代码导致了问题。
7.4 确保兼容性
- 版本检查:在插件的
Awake()开始时,检查游戏版本和BepInEx版本,如果不符合要求,可以提前退出并给出友好提示。 - 依赖管理:如果你的插件需要其他插件(例如,依赖某个库Mod),可以使用
[BepInDependency]属性来声明依赖,BepInEx会确保依赖项先于你的插件加载。 - Harmony ID:在创建
Harmony实例时,传入一个唯一的ID(如new Harmony(PluginGUID)),这有助于在多个Mod使用Harmony时管理补丁。
8. 常见问题与故障排除实录
在实际操作中,你几乎一定会遇到下面这些问题。这里是我踩过坑后总结的速查表。
| 问题现象 | 可能原因 | 排查步骤与解决方案 |
|---|---|---|
游戏启动后无任何Mod生效,BepInEx文件夹未生成。 | 1. BepInEx文件未正确解压到游戏根目录。 2. doorstop_config.ini配置错误。3. 游戏使用了IL2CPP但安装了Mono版BepInEx。 | 1. 检查游戏根目录下是否有winhttp.dll和doorstop_config.ini。2. 检查 doorstop_config.ini中的targetAssembly路径是否正确指向BepInEx/core/BepInEx.Preloader.dll。3. 确认游戏脚本后端,下载对应版本的BepInEx。 |
游戏启动崩溃,日志中出现TypeLoadException或FileNotFoundException。 | 1. 插件依赖的DLL缺失(如未安装BepInEx.Core)。2. 插件编译的目标.NET框架版本与游戏不兼容。 3. Harmony补丁签名错误。 | 1. 使用ILSpy查看插件DLL的引用,确保所有依赖(特别是0Harmony.dll,BepInEx.dll)存在于游戏的BepInEx/core或plugins目录。2. 将插件项目目标框架改为 .NET Framework 4.7.2或游戏使用的版本。3. 仔细检查Harmony补丁方法的参数类型、名称是否完全匹配原方法。 |
| 插件日志显示加载成功,但功能无效。 | 1. Harmony补丁未正确应用(类/方法名错误,补丁类型错误)。 2. 插件初始化代码有逻辑错误,提前返回。 3. 游戏代码更新,方法签名已改变。 | 1. 在日志中搜索Harmony,查看补丁应用是否成功。使用Harmony.DEBUG = true;输出更详细的调试信息。2. 在插件 Awake和补丁方法开始处添加日志,确认代码执行路径。3. 使用反编译工具重新确认游戏目标方法的完整签名(包括参数类型和返回类型)。 |
| 修改配置文件后,插件行为未改变。 | 1. 插件未实现配置热重载逻辑。 2. 插件代码中缓存了旧的配置值,未重新读取。 3. 配置文件格式错误,导致读取失败。 | 1. 对于需要热重载的配置,监听Config.SettingChanged事件,或在相关逻辑处每次从ConfigEntry.Value实时读取。2. 检查 .cfg文件,确保节名、键名正确,且值类型匹配(如字符串不要漏了引号)。 |
| 与其他Mod冲突,游戏行为异常或崩溃。 | 1. 多个Mod修补了同一个游戏方法,且逻辑冲突。 2. 修改了共享的静态状态,未考虑其他Mod。 | 1. 尝试单独启用你的Mod和冲突Mod,定位问题。 2. 在设计补丁时尽量保持“最小影响”,优先使用后缀补丁读取结果,而非前缀补丁覆盖逻辑。考虑使用 [HarmonyPriority]属性调整补丁执行顺序。 |
| 在IL2CPP游戏上,BepInEx 6安装后插件不加载。 | 1. 未正确生成或放置“伪程序集”。 2. 插件未针对IL2CPP环境编译。 | 1. 确保按照BepInEx 6的IL2CPP指南,使用BepInEx/unity-libs中的工具生成了必要的Managed文件夹和伪DLL。2. 将插件项目目标框架改为 .NET Standard 2.1或.NET Core,并确保引用了适用于IL2CPP的BepInEx库。 |
开发BepInEx插件是一个不断探索和解决问题的过程。从简单的日志输出到复杂的游戏逻辑重写,每一步都需要耐心和对工具链的熟悉。最宝贵的经验往往来自于社区:多阅读热门开源Mod的代码,在相关的Discord频道或论坛中提问,你会发现整个Modding社区充满了乐于分享的开发者。记住,清晰的日志、细致的测试和对游戏代码的尊重,是制作出稳定、受欢迎插件的基石。当你看到其他玩家在使用你创造的Mod时,那种成就感绝对是驱动你继续深入的最佳动力。