如何用 BepInEx 6 给 Unity 游戏装模组:新手完整指南
【免费下载链接】BepInExUnity / XNA game patcher and plugin framework项目地址: https://gitcode.com/GitHub_Trending/be/BepInEx
BepInEx 是面向 Unity 与 .NET 游戏的插件框架,让不给修改入口的游戏装起 Mod 就像放一个 DLL 那么简单,同时覆盖 Mono、IL2CPP 与 .NET 三种运行环境。这篇文章带你看懂原理、从零跑通一次构建安装,出问题也知道先查哪里。
项目名片
- 给谁用:想给自己的 Unity / .NET 游戏装 Mod 的玩家,以及想给游戏写插件的开发者。
- 一句话原理:在游戏正式启动前用 UnityDoorstop 截住启动流程,然后扫描并逐个加载你放进
BepInEx/plugins目录里的插件 DLL。 - 值得用的原因:Unity 模组圈事实上的标准框架,"放一个 DLL 进 plugins 目录就生效"这套玩法就是它定下来的——Mono、IL2CPP、.NET(XNA/FNA/MonoGame 等)三种环境都有对应支持。
- 诚实边界📌:目前只有 Unity Mono 有稳定发布版,IL2CPP 属于实验性质且不支持 macOS。装机前先看你的游戏是哪种运行时(下文教你判断)。
用一个比喻看懂原理:酒店的前台与管家
先打一个比方。
游戏就是一家酒店,官方没留任何"加开房间"的口子。BepInEx 干的事是:门口保安(UnityDoorstop)在前台之前先拦住你,塞给你一张"访客证"(BepInEx 预加载器);然后管家(链式加载器)拿着访客证核对"来宾名单"(插件)——证件号(GUID)格式对不对、房型(编译时对应的 BepInEx 版本)匹不匹配、他带的同伴(依赖插件)在不在名单上,都合格了才发钥匙。要是酒店原来的"来宾登记簿"丢了(IL2CPP 的情况),管家会先请个修复师(Cpp2IL)从旧材料把登记簿复原出来,再谈入住。
实际流程是这三步:
- 门口拦截:靠 UnityDoorstop(v4.5.0)通过
LD_PRELOAD这类环境变量预注入libdoorstop,在游戏主逻辑跑起来之前先执行 BepInEx 的预加载程序集(BepInEx.Unity.Mono.Preloader.dll),启动流程在这一刻被"截住"。 - 重建名单:Mono 下游戏程序集就是 C# IL 字节码,插件直接"查表"就能找到要改的方法;IL2CPP 把 C# 编译成了 C++ 原生代码,就用 Cpp2IL(v2022.1.0)先逆向还原出 C# 名单,再由 Il2CppInterop(v1.5.3)在运行时重建类型引用,插件的调用这才有了落点。
- 逐个入住:链式加载器扫描
BepInEx/plugins目录,校验每个插件的 GUID、BepInEx 版本、依赖声明,按拓扑排序排好加载顺序,再逐个构造插件实例——这就是"发钥匙"。
从零跑通:源码构建到插件生效
第 1 步:备好 .NET SDK,拉下源码
目标:把构建工具链备齐,源码拿到手。
操作:
# 确认两样工具是否就位 git --version # 应输出 git 版本号 dotnet --info # 看输出里 SDK 一栏的版本号,要求 >= 6.0 # 拉源码并进入目录 git clone https://gitcode.com/GitHub_Trending/be/BepInEx cd BepInEx验收信号:dotnet --info的SDK段版本号 ≥ 6.0,本地出现带BepInEx.sln的BepInEx目录。
避坑警示⚠️:只装 .NET运行时不算数,构建需要完整SDK;另外别被 IL2CPP 依赖的"dotnet-runtime 6.0.7"绕晕——那是以后要打包进游戏目录的运行时,跟你构建机的 SDK 是两回事。
第 2 步:一条命令构建出生成分发包
目标:一次跑完"拉依赖 → 编译 → 生成发行包"。
操作:
# Linux / macOS 用 bash 脚本 ./build.sh --target MakeDist # Windows 命令行 build.cmd --target MakeDist # Windows PowerShell(先启用脚本执行策略,否则会被拦截) ./build.ps1 --target MakeDist用--target MakeDist而不是Compile的原因:它直接在bin/dist里生成可分发的包,省掉你手动打包的环节。
验收信号:bin/dist目录里出现带版本号的分发包(zip)。
避坑警示⚠️:PowerShell 报"无法加载脚本"是执行策略拦的,先去设置里放开;三个 target(Compile/MakeDist/Publish)的差别都写在 docs/BUILDING.md 里。
第 3 步:把框架铺进游戏目录
目标:让游戏根目录长出一套符合启动要求的结构。
操作:把发行包内容解压到游戏根目录,结构应该是这样:
游戏根目录/ ├── 游戏主程序 # Windows 是 .exe,Linux 是同名可执行文件 ├── doorstop_config.ini # UnityDoorstop 的开关面板 ├── libdoorstop.so # 启动拦截库;Windows 是 .dll,macOS 是 .dylib └── BepInEx/ ├── core/ # 框架核心 DLL(Preloader、Core 等) ├── plugins/ # 你的插件 DLL 放这里 └── LogOutput.log # 首跑后由框架写出验收信号:BepInEx/core里能看到BepInEx.Unity.Mono.Preloader.dll等核心文件,游戏根目录多了doorstop_config.ini。
避坑警示⚠️:路径里带中文或特殊字符时,部分游戏加载程序集会直接失败——游戏目录尽量用纯英文路径。
第 4 步:核对 doorstop_config.ini 的三个关键项
目标:确认 Doorstop 的开关都拧对,预加载器能被正确指到。
操作:仓库里的默认配置在 Runtimes/Unity/Doorstop/,Mono 版关键项长这样:
[General] enabled = true # 总开关,false 时整个框架静默不工作 target_assembly = BepInEx\core\BepInEx.Unity.Mono.Preloader.dll # 启动时第一个执行的程序集 [UnityMono] dll_search_path_override = "BepInEx\core" # 让 Mono 优先从 BepInEx\core 找核心库验收信号:enabled、target_assembly、dll_search_path_override三项与上面一致。如果游戏是 IL2CPP,target_assembly要指向BepInEx\core\BepInEx.Unity.IL2CPP.dll,并且[Il2Cpp]段的coreclr_path、corlib_dir要指向 dotnet 运行时目录。
避坑警示⚠️:先判断游戏类型——游戏目录里有Data/Managed/文件夹是 Mono;只有原生库、没有 Managed 的多半是 IL2CPP。IL2CPP 版还不支持 macOS。
第 5 步:首跑游戏,确认插件真的加载了
目标:用日志(而不是"游戏能进主界面")证明插件加载成功。
操作:
# Linux/macOS 可用发行包里的启动脚本(它会替你处理 LD_PRELOAD 等环境细节) ./run_bepinex_mono.sh脚本本体见 Runtimes/Unity/Doorstop/run_bepinex_mono.sh,Windows 用户直接双击游戏主程序即可。
验收信号:游戏里弹出 BepInEx 控制台窗口,且BepInEx/LogOutput.log里出现Chainloader initialized和逐条的Loading [插件名]记录。
避坑警示⚠️:能正常进主界面 ≠ 插件加载成功——坏插件会被跳过,一切以日志里的插件列表为准。
出问题时的症状自查
症状一:启动后没有控制台窗口,日志很短
症状表现:游戏开着(或根本没开起来),LogOutput.log很短,没有Chainloader initialized,也看不到 BepInEx 控制台窗口。
最可能原因:BepInEx 压根没启动,问题出在 Doorstop 这一层——总开关关着,或target_assembly指向了一个不存在的路径。
验证操作:
# 扫一眼配置里两个最关键的项 grep -E "enabled|target_assembly" doorstop_config.ini # 再确认指向的文件真的存在 ls BepInEx/core/BepInEx.Unity.Mono.Preloader.dll修复办法:enabled是false就改成true;路径与文件差一个字符都对不上,改成真实路径(目录建议纯英文)后重启。
症状二:游戏正常进主界面,但插件没反应
症状表现:日志里有Could not load或Skipping字样,游戏也能进,但对应插件的效果在里面试不出来。
最可能原因:那个插件编译时引用的 BepInEx 版本和当前装的不匹配,或者它声明的依赖插件缺失 / 版本不满足要求。
验证操作:
# 找出报错行指向的是哪个插件 grep -E "Could not load|Skipping" BepInEx/LogOutput.log修复办法:把报错指向的插件 DLL 移出plugins目录重启一次,确认锅是不是它;确认后再装齐缺失的依赖,或把插件更新到匹配当前 BepInEx 大版本的构建。
症状三:Linux 上一启动就报错退掉
症状表现:跑启动脚本或直接执行可执行文件时立刻报错,提示可执行文件类型不对。
最可能原因:架构不匹配(32 位游戏装到 64 位环境),或者这是个 Windows 二进制(PE32),你给它装了 *nix 版的 BepInEx。
验证操作:
# 看一眼产物的真实类型 file 游戏主程序修复办法:输出含PE32说明是 Windows 游戏,应换 Windows 版 BepInEx(经 Wine/Proton 跑);32 位游戏则确认libdoorstop.so选的是 x86 版本。
整体自查顺序一句话:先翻日志(框架到底启动没有)→ 再核配置(开关和路径对不对)→ 最后验环境(架构与产物类型)。
核心文件导航
| 模块 / 文件 | 职责 | 什么时候看它 |
|---|---|---|
| BepInEx.Preloader.Core/ | 预加载器核心:在游戏主逻辑之前执行,接管启动流程、应用运行时修复 | 游戏还没进主画面就退出时 |
| BepInEx.Core/Bootstrap/ | 框架本体:插件发现、筛选、排序与加载全在这 | 插件被跳过、加载顺序异常时 |
| BepInEx.Core/Configuration/ | 配置系统:TOML 解析、类型转换、取值校验 | 配置项不生效、默认值不符合预期时 |
| Runtimes/Unity/ | 运行时适配层:Mono 版、IL2CPP 版、Doorstop 默认配置与启动脚本 | 判断游戏类型、改启动参数时 |
| Il2CppInteropManager.cs | IL2CPP 互操作层:负责在运行时重建类型引用 | 想深挖 IL2CPP 支持链路时 |
最值得一读的是 BepInEx.Core/Bootstrap/BaseChainloader.cs:所有插件的筛选逻辑都写在这里——GUID 是否合法、目标 BepInEx 版本是否匹配、依赖是否满足、加载顺序怎么排,每条被跳过时的日志文案都紧挨着对应的continue。插件被"跳过"时,日志里的原因基本都能在这份文件里找到出处。
写在最后
BepInEx 把"给闭源游戏加模组"这件以前需要逆向全家桶的事,收敛成了"往plugins目录放一个 DLL"——门口拦截、逐个入住、运行时适配各司其职。接下来值得关注两个方向:一是 IL2CPP 支持从实验性质走向稳定(README 目前明确只有 Unity Mono 有稳定发布);二是随底层dotnet-runtime(当前 v6.0.7)向新版本演进,IL2CPP 环境的兼容性会持续改善。想继续深入,从 docs/BUILDING.md 了解三种构建 target 的细节,从 docs/CONTRIBUTING.md 了解社区协作方式,仓库 README 里还列有官方 Discord 社区入口。
【免费下载链接】BepInExUnity / XNA game patcher and plugin framework项目地址: https://gitcode.com/GitHub_Trending/be/BepInEx
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考