Unity游戏Mod开发指南:MelonLoader插件加载器原理与实战
1. 项目概述:为什么你需要一个插件加载器?
如果你是一个Unity游戏的深度玩家,或者是一个对游戏机制有自己想法的Mod开发者,那么你一定遇到过这样的困境:面对一个你热爱的游戏,你想修改它的UI、增加新的功能、或者仅仅是调整一些数值来获得更好的体验,却发现无从下手。游戏本身是封闭的,没有提供官方的修改接口。这时候,一个强大、稳定且易用的插件加载器,就成了连接你的创意与游戏世界的桥梁。MelonLoader,正是近年来在Unity游戏Modding社区中崛起,并迅速成为事实标准的那个“桥梁”。
简单来说,MelonLoader是一个运行在Unity引擎游戏之上的“中间件”。它不像传统的注入式修改器那样粗暴地改写内存,而是以一种更优雅、更稳定的方式,在游戏启动的早期就介入,为后续加载由你或社区开发者编写的插件(通常是以.dll文件形式存在的程序集)提供一个安全的沙箱环境。你可以把它想象成游戏的一个“扩展坞”,所有符合规范的插件都能安全地“停靠”在这里,并按照既定的规则与游戏本体进行交互。
它的核心价值在于“标准化”和“社区化”。在MelonLoader出现之前,每个游戏的Mod加载方式可能都不同,开发者需要针对特定游戏版本重复造轮子,玩家安装Mod也步骤繁琐、容易冲突。MelonLoader统一了接口,使得插件开发者可以专注于功能实现,而玩家则可以像管理软件一样,通过简单的拖放来管理自己的Mod列表。从《英灵神殿》(Valheim)到《腐蚀》(Rust),从《星露谷物语》的某些扩展框架到大量独立游戏,你都能看到它的身影。它解决的,正是Unity游戏Mod生态碎片化、入门门槛高的核心痛点。
2. MelonLoader核心架构与工作原理拆解
要熟练使用甚至为它开发插件,理解其内部是如何工作的至关重要。这能帮助你在遇到问题时快速定位,而不是停留在盲目尝试的层面。
2.1 启动流程:从游戏EXE到插件世界
MelonLoader的启动过程是一场精密的“接力赛”。当你双击游戏的可执行文件(.exe)时,故事并没有直接交给Unity引擎。
- 引导阶段:游戏EXE文件实际上被轻微修改(或通过其他引导器),使其最先加载的不是Unity的运行时,而是MelonLoader的引导程序(Bootstrap)。这个引导程序通常是一个名为
version.dll或winhttp.dll的文件(利用Windows的DLL搜索顺序劫持),它非常轻量,只负责最基础的准备工作。 - 环境初始化:引导程序会检测当前运行的Unity引擎版本,并加载对应版本的MelonLoader核心组件。这个核心组件是用C++/CLI或纯C++编写的本地模块,它的任务是挂钩(Hook)Unity引擎底层的关键函数,例如程序集加载、场景管理等。这里的一个关键技巧是:MelonLoader选择在Unity初始化其脚本引擎(Mono或IL2CPP)之前完成挂钩,这确保了它能捕获到游戏所有原始脚本的加载过程,为后续的插件加载铺平道路。
- 插件加载阶段:当Unity开始加载游戏自身的托管程序集(Assembly-CSharp.dll等)时,被挂钩的函数会通知MelonLoader。此时,MelonLoader的托管部分(用C#编写)开始工作。它会扫描游戏目录下的
Mods文件夹,加载所有有效的插件程序集(.dll)。每个插件都必须包含一个继承自MelonMod的主类,并在类上标记[assembly: MelonInfo(...)]等特性(Attribute)来提供元数据。 - 生命周期管理:所有插件加载完毕后,MelonLoader会按照一定的顺序(可通过依赖关系配置)调用每个插件的
OnInitializeMelon、OnSceneWasLoaded等生命周期方法。从此,插件代码正式与游戏代码并行运行,可以监听游戏事件、修改游戏对象、调用游戏方法,实现各种功能。
注意:对于使用IL2CPP后端编译的游戏(现代Unity手游和许多PC游戏为了性能和安全性会采用),其原理类似,但更为复杂。MelonLoader需要通过生成一个额外的“解释器”层(如通过
Il2CppAssemblyUnhollower项目生成的桥接程序集)来将C++编译的IL2CPP代码“映射”回C#世界,让插件能够识别和调用游戏中的类和方法。这个过程通常被称为“Unhollowing”。
2.2 关键组件与文件结构
一个标准的、安装了MelonLoader的游戏目录会包含以下关键部分,理解它们有助于故障排查:
游戏根目录/ ├── (游戏原始文件,如Game.exe, UnityPlayer.dll) ├── MelonLoader/ │ ├── Managed/ # 托管依赖库,如MelonLoader.dll, 0Harmony.dll │ ├── Native/ # 本地库,对应不同平台(Windows x86/x64) │ ├── Il2CppAssemblies/ # 仅IL2CPP游戏:生成的桥接程序集 │ └── Logs/ # 运行日志,排查问题的第一现场 ├── Mods/ # 【用户】放置插件.dll文件的位置 │ ├── MyAwesomeMod.dll │ └── MyAwesomeMod.dependencies.json (可选,声明依赖) ├── UserData/ # 【插件】存放配置、数据的位置 │ └── (各插件创建的文件夹和文件) └── Plugins/ # 存放其他本地插件(非MelonLoader插件)MelonLoader/Managed/0Harmony.dll:这是LibHarmony库,是MelonLoader实现方法修改(即“打补丁”)的基石。插件通过它来安全地修改游戏原有方法的行为,是实现大多数功能的核心手段。Mods/文件夹:这是你作为用户唯一需要经常接触的文件夹。安装Mod就是把你下载的.dll文件(及其可能的依赖库)放进这里。一个常见的坑是:有些Mod发布时是一个压缩包,解压后是一个包含.dll文件的文件夹。你必须将.dll文件直接放入Mods/根目录,而不是把整个文件夹放进去(除非Mod作者明确说明是支持子目录的)。UserData/文件夹:良好的插件会在这里创建自己的子目录来保存配置文件(如MyAwesomeMod.cfg)和持久化数据。这保证了Mod配置不会污染游戏原始目录,也便于管理。
3. 从零开始:MelonLoader的安装与配置实战
理论说得再多,不如亲手装一遍。下面我们以一款假设的、使用Mono后端编译的PC版Unity游戏MyUnityGame.exe为例,进行全流程安装演示。
3.1 安装准备:工具与判断
首先,你需要准备两样东西:
- MelonLoader安装器:最推荐的是从GitHub官方仓库发布的
MelonLoader.Installer.exe。这是最安全、最官方的渠道。 - 你的目标游戏:确保游戏是完整的,并且至少成功运行过一次(以生成必要的初始文件)。
关键第一步:判断游戏后端。在安装前,你必须知道游戏使用的是Mono还是IL2CPP。一个简单的方法是查看游戏目录:
- 如果存在
GameName_Data/Managed/Assembly-CSharp.dll,这很可能是Mono后端。 - 如果存在
GameName_Data/Il2CppData等文件夹,并且Managed文件夹里是空的或只有少量UnityEngine.*等库,这基本就是IL2CPP后端。 - 更准确的方法是使用专门的检测工具,如
UnityEX或直接查看MelonLoader安装器自动检测的结果。
3.2 执行安装:步步为营
- 运行安装器:双击
MelonLoader.Installer.exe。大多数情况下,安装器会自动检测到你电脑上最近运行过的Unity游戏并列出。如果没找到,点击Select手动浏览到你的MyUnityGame.exe文件。 - 版本选择:安装器会自动检测游戏使用的Unity版本,并为你推荐一个兼容的MelonLoader版本。除非你明确知道需要其他版本,否则请始终使用安装器推荐的“Latest Stable”(最新稳定版)。对于IL2CPP游戏,安装器还会自动处理Unhollowing所需的组件下载。
- 执行安装:点击
Install。安装器会完成以下工作:- 在游戏目录创建
MelonLoader文件夹及其子结构。 - 将核心文件(
version.dll、MelonLoader.dll等)复制到相应位置。 - 对于IL2CPP游戏,会下载并运行
Il2CppAssemblyUnhollower,这个过程可能需要几分钟,请耐心等待。 - 备份原始的游戏启动文件(通常会给
.exe文件加上.orig后缀)。
- 在游戏目录创建
- 验证安装:安装完成后,直接运行
MyUnityGame.exe。如果安装成功,你会看到一个MelonLoader的控制台窗口(如果游戏本身不是控制台程序,MelonLoader会为其创建一个)首先弹出,里面滚动着加载日志。随后游戏主窗口出现。在游戏主菜单或某个界面的角落,你通常能看到MelonLoader的版本水印。这表明MelonLoader已成功加载。
实操心得:安装过程最常遇到的问题就是“游戏闪退”。此时请务必打开
MelonLoader/Logs文件夹,查看最新的日志文件。日志的开头就会明确标出错误,例如“未找到支持的Unity版本”、“DLL加载失败”等。根据错误信息去MelonLoader的Wiki或社区搜索,99%的问题都能找到解决方案。
3.3 基础配置:让加载器更顺手
首次运行后,MelonLoader文件夹下会生成一个MelonLoader.cfg文件。用文本编辑器打开它,你会看到很多配置项。这里介绍几个对新手最重要的:
[MelonLoader] ; 是否显示控制台窗口。调试Mod时务必开启,发布后可关闭。 ConsoleMode = 1 ; 0=无, 1=标准, 2=仅外部 ; 是否在游戏界面上显示MelonLoader的水印和Mod列表。 HideConsole = false ; 是否隐藏游戏自身的控制台(如果游戏有) HideWarnings = false ; 是否隐藏警告信息(不建议) ; 是否启用Quit Fix。某些游戏退出时可能卡住,开启此选项尝试修复。 QuitFix = true [Il2Cpp] ; 仅IL2CPP游戏:是否生成Unhollowing后的程序集。开发插件时需要开启。 GenerateAssemblies = false ; 普通玩家保持false以加快启动速度对于普通玩家,保持默认配置即可。如果你是Mod开发者,则需要开启GenerateAssemblies并可能调整日志级别。
4. 插件的安装、管理与冲突解决
MelonLoader本身只是一个平台,真正的乐趣来自于各式各样的插件(Mod)。
4.1 如何寻找和安装插件
- 来源:最主流的平台是
nexusmods.com,很多热门游戏的MelonLoader Mod都会发布在这里。其次是GitHub,许多开发者会在自己的仓库发布。一些游戏也会有专门的Discord社区或论坛版块。 - 安装:99%的MelonLoader Mod都是一个或多个
.dll文件。安装方法极其简单:- 关闭游戏。
- 将下载的
.dll文件复制到游戏的Mods文件夹内。 - 重新启动游戏。MelonLoader会在启动时自动加载它。
- 依赖管理:一些复杂的Mod可能需要其他库的支持,例如
Newtonsoft.Json(用于处理JSON配置)或UnityEngine.UI(用于创建UI)。负责任的作者会在发布页明确写明依赖项。你需要将这些依赖的.dll文件同样放入Mods文件夹。一个重要的技巧是:MelonLoader的Managed文件夹里已经包含了一些常用库(如0Harmony,Newtonsoft.Json)。如果Mod依赖的库版本与内置的兼容,你通常不需要额外放置;如果不兼容,则需要将特定版本的依赖库放入Mods文件夹,因为Mods目录的加载优先级更高。
4.2 插件管理实践:排序与禁用
当你的Mods文件夹里有了几十个插件后,管理就变得重要了。
- 加载顺序:MelonLoader默认按文件名的字母顺序加载插件。如果Mod A依赖于Mod B(例如,A需要调用B提供的API),那么B必须在A之前加载。你可以通过修改文件名来调整顺序(例如在B前加
01_,在A前加02_),但更好的方法是使用依赖声明。 - 依赖声明:Mod开发者可以在其主类上使用
[assembly: MelonDependency(...)]特性,或提供一个ModName.dependencies.json文件来声明依赖。MelonLoader会据此自动调整加载顺序。 - 禁用插件:不想删除但又想临时禁用一个插件?很简单,只需将插件
.dll文件的扩展名改为.dll.off或.disabled,MelonLoader就会忽略它。这是测试Mod冲突时的常用手段。
4.3 冲突排查与解决指南
“游戏崩溃了”、“这个Mod没效果”、“两个Mod一起用就报错”——这些都是Mod冲突的典型症状。请按照以下步骤系统排查:
- 二分法隔离:这是最有效的办法。将一半的Mod移出
Mods文件夹(或改为.off扩展名),运行游戏测试。如果问题消失,说明问题出在被移出的那一半Mod里;如果问题依旧,则出在剩下的那一半。不断对半分割,直到定位到具体的一个或几个冲突Mod。 - 查看日志:
MelonLoader/Logs下的日志文件是金矿。搜索 “ERROR”、“Exception” 或 “Failed to load” 等关键词。错误信息通常会直接指向有问题的Mod和具体的异常堆栈,这能极大缩小排查范围。 - 常见冲突类型:
- 同一功能重复修改:两个Mod都尝试修改游戏的同一个方法(例如,都去修改“玩家生命值计算”函数),且处理逻辑冲突。这通常需要通过修改Mod配置,或联系作者寻求兼容性补丁来解决。
- 资源(Asset)冲突:两个Mod都尝试加载或替换同一个游戏内部资源(如图片、模型)。这比较棘手,可能需要你手动取舍。
- 依赖库版本冲突:Mod A需要
Newtonsoft.Json 12.0.0,而Mod B自带或需要13.0.0。解决方法是将更高或更兼容的版本放入Mods文件夹,并确保它被正确加载。
- 利用控制台:游戏内按
F1键(默认,可在配置中修改)可以开关MelonLoader的控制台。在这里你可以输入melonloader.modinfo查看所有已加载Mod的详细信息,包括版本、作者和依赖关系,对于手动分析冲突非常有帮助。
5. 插件开发入门:创建你的第一个MelonMod
如果你想从使用者变为创造者,那么开发自己的MelonLoader插件会带来巨大的成就感。这里我们勾勒出一个最简单的“Hello World” Mod的开发流程。
5.1 开发环境搭建
- 安装.NET SDK:你需要安装 .NET 6.0 或更高版本的SDK。这是编译C#代码的基础。
- 安装IDE:推荐使用 Visual Studio 2022 或 JetBrains Rider,它们对C#和游戏Mod开发支持良好。
- 获取游戏程序集:
- 对于Mono游戏:直接从
GameName_Data/Managed/复制Assembly-CSharp.dll和必要的UnityEngine.*.dll到你的项目参考目录。 - 对于IL2CPP游戏:你需要先让MelonLoader生成桥接程序集。在
MelonLoader.cfg中设置GenerateAssemblies = true,然后运行一次游戏。成功后,在MelonLoader/Il2CppAssemblies/文件夹下会生成一系列Assembly-CSharp*.dll等文件,这些就是你可以引用的“游戏代码”。
- 对于Mono游戏:直接从
5.2 创建项目与编写代码
- 创建类库项目:在IDE中新建一个
.NET Class Library项目,目标框架选择.NET 6.0。 - 添加引用:
- 引用
MelonLoader.dll(位于游戏目录的MelonLoader/Managed/下)。 - 引用游戏程序集(上一步获取的
Assembly-CSharp.dll)。 - 引用必要的Unity引擎DLL(如
UnityEngine.CoreModule.dll)。
- 引用
- 编写主Mod类:
using MelonLoader; using UnityEngine; // 必须的程序集特性,用于声明Mod信息 [assembly: MelonInfo(typeof(MyFirstMod.MyFirstMod), "MyFirstMod", "1.0.0", "YourName")] [assembly: MelonGame("GameDeveloper", "GameName")] // 可选,指定游戏 namespace MyFirstMod { public class MyFirstMod : MelonMod // 主类必须继承自MelonMod { // 初始化方法,在Mod加载后、游戏开始前调用 public override void OnInitializeMelon() { LoggerInstance.Msg("我的第一个Mod已加载!Hello from MyFirstMod!"); // 这里可以初始化配置、设置快捷键等 } // 每帧更新方法(慎用,性能敏感) public override void OnUpdate() { // 示例:按下F6键在控制台打印消息 if (Input.GetKeyDown(KeyCode.F6)) { LoggerInstance.Msg("你按下了F6键!"); // 尝试获取玩家对象(假设游戏有一个Player的静态实例) // var player = Player.Instance; // if (player != null) LoggerInstance.Msg($"玩家位置:{player.transform.position}"); } } // 场景加载完成后的回调 public override void OnSceneWasLoaded(int buildIndex, string sceneName) { LoggerInstance.Msg($"场景加载完毕:{sceneName} (索引:{buildIndex})"); } } }- 编译与测试:编译项目得到
.dll文件。将其复制到游戏的Mods文件夹,运行游戏。打开控制台(按F1),你应该能看到你的加载信息,并且按下F6键会有对应输出。
5.3 深入一步:使用Harmony进行方法修补
真正的Mod功能往往需要修改游戏原有的行为,这就要用到Harmony库。假设我们想修改玩家收到伤害时的计算。
using HarmonyLib; // 引入Harmony命名空间 namespace MyFirstMod { public class MyFirstMod : MelonMod { private static HarmonyLib.Harmony _harmony; public override void OnInitializeMelon() { LoggerInstance.Msg("伤害修改Mod加载!"); // 创建Harmony实例,参数是一个唯一的ID(建议用Mod名称) _harmony = new HarmonyLib.Harmony("com.yourname.mymod"); // 应用所有标记了[HarmonyPatch]的补丁 _harmony.PatchAll(); } public override void OnDeinitializeMelon() { // 游戏关闭或Mod卸载时,清理所有补丁(好习惯) _harmony?.UnpatchSelf(); } } // 使用HarmonyPatch特性来声明我们要修补哪个方法 [HarmonyPatch(typeof(Player))] // 假设Player类在游戏程序集中 [HarmonyPatch("TakeDamage")] // 方法名 public static class Patch_Player_TakeDamage { // Prefix补丁:在原方法执行前运行 static bool Prefix(ref float damageAmount, Player __instance) { // 我们的逻辑:将受到的伤害减半 damageAmount *= 0.5f; LoggerInstance.Msg($"玩家 {__instance.name} 受到伤害,已减半至 {damageAmount}"); // 返回true,让原方法继续执行(此时damageAmount已被我们修改) // 如果返回false,则会完全跳过原方法的执行 return true; } // Postfix补丁:在原方法执行后运行 static void Postfix(float damageAmount, Player __instance) { LoggerInstance.Msg($"伤害处理完毕,玩家当前生命值:{__instance.health}"); } } }这就是MelonLoader插件开发的核心模式:通过Harmony在游戏代码的关键位置插入你自己的逻辑。你需要使用类似dnSpy这样的反编译工具去分析游戏代码,找到你想要修改的类和方法,然后设计你的补丁。
6. 高级话题与性能优化
当你的Mod列表越来越长,或者你开发的Mod功能越来越复杂时,就需要关注以下高级话题。
6.1 配置系统与数据持久化
一个好的Mod应该允许用户配置。MelonLoader内置了基于Toml格式的配置系统,非常易用。
using MelonLoader; public class MyConfigMod : MelonMod { // 定义一个配置类 public class MySettings { public bool IsFeatureEnabled { get; set; } = true; public float DamageMultiplier { get; set; } = 1.0f; public string GreetingMessage { get; set; } = "Hello!"; } public static MySettings Settings { get; private set; } public override void OnInitializeMelon() { // 加载或创建配置。配置文件会保存在 UserData/MyConfigMod.cfg Settings = MelonPreferences.GetCategory<MySettings>("MyConfigMod"); LoggerInstance.Msg($"功能已启用:{Settings.IsFeatureEnabled}"); } }用户可以在游戏内通过MelonLoader的配置界面(默认F3键)实时修改这些值,无需重启游戏。
6.2 UI集成:在游戏内创建界面
对于需要复杂交互的Mod,一个图形界面是必要的。你可以使用Unity原生的IMGUI(OnGUI)快速绘制简单界面,但对于复杂UI,推荐使用UnityEngine.UI(uGUI) 或社区流行的框架如UnityExplorer的API。
使用IMGUI的简单示例:
private bool _showWindow = false; private Rect _windowRect = new Rect(20, 20, 400, 200); public override void OnGUI() { if (!_showWindow) return; _windowRect = GUI.Window(0, _windowRect, DrawWindow, "我的Mod设置"); } void DrawWindow(int windowID) { if (GUI.Button(new Rect(10, 30, 100, 30), "点击我")) { LoggerInstance.Msg("按钮被点击!"); } GUI.DragWindow(); } public override void OnUpdate() { if (Input.GetKeyDown(KeyCode.F7)) _showWindow = !_showWindow; }6.3 性能考量与最佳实践
- 慎用
OnUpdate:这是每帧都会调用的方法。在这里面执行复杂的计算或频繁的查找(如GameObject.Find)会严重拖累游戏性能。尽量使用事件驱动(如监听游戏事件)或在OnUpdate中加帧率限制。 - 缓存引用:如果你需要频繁访问某个游戏对象或组件,在初始化时找到它并缓存起来,而不是每帧都去查找。
- Harmony补丁要精简:Prefix/Postfix补丁中的代码应尽可能高效。避免在补丁中分配大量新对象(new),以免引发垃圾回收(GC)导致卡顿。
- 做好异常处理:你的Mod代码不应该导致游戏崩溃。用
try-catch包裹可能有风险的操作,并在catch块中记录错误日志,而不是让异常抛给游戏。 - 测试、测试、再测试:在不同的游戏场景、不同的Mod组合下测试你的插件。内存泄漏和性能问题往往在长时间运行后才会显现。
7. 故障排除与社区资源
即使遵循了所有指南,你依然可能遇到问题。这里是最后的防线。
7.1 常见错误与解决方案速查表
| 现象 | 可能原因 | 解决方案 |
|---|---|---|
游戏启动即崩溃,日志显示Unity Version Not Supported | MelonLoader不支持该Unity版本或游戏使用了未知的IL2CPP变体。 | 1. 检查MelonLoader版本是否过旧,更新至最新。 2. 前往MelonLoader的GitHub或Discord,查看该游戏是否有特殊的安装说明或分支版本。 |
| 游戏启动后黑屏或卡在初始界面 | 某个Mod加载失败或与当前游戏版本不兼容。 | 1. 查看日志文件,找到加载失败的Mod。 2. 使用“二分法”禁用一半Mod来定位问题Mod。 3. 检查该Mod的发布页,确认其支持当前游戏版本。 |
| 特定Mod功能不生效 | Mod代码未执行;Harmony补丁未正确应用;依赖的游戏方法已更改。 | 1. 在日志中搜索该Mod名,看是否有加载错误。 2. 检查Mod的依赖项是否全部安装。 3. 游戏更新后,Mod可能需要更新以适配新的代码。 |
| 按快捷键(如F1/F3)无反应 | 快捷键被游戏本身或其他Mod占用;MelonLoader配置被修改。 | 1. 检查MelonLoader.cfg中的Preferences部分,确认快捷键设置。2. 尝试在无其他Mod的环境下测试,排除冲突。 |
| 游戏运行一段时间后崩溃 | 内存泄漏;Mod代码中存在无限循环或资源未释放。 | 1. 通过逐一禁用Mod来定位有问题的Mod。 2. 关注日志中崩溃前的警告或错误信息。 3. 如果是自己开发的Mod,检查事件订阅是否在Mod卸载时取消订阅。 |
7.2 不可或缺的社区与工具
- 官方资源:
- GitHub仓库:获取最新版本、安装器、源代码和官方文档。
- Discord服务器:最活跃的社区。在这里你可以直接向开发者提问,寻找特定游戏的Mod频道,获取即时帮助。
- 开发工具:
- dnSpy / ILSpy:反编译游戏
Assembly-CSharp.dll的必备工具,用于分析游戏代码结构。 - Unity Explorer:一个强大的运行时调试与探索Mod,可以查看游戏场景层次结构、对象属性、调用方法等,是开发者的“瑞士军刀”。
- MelonLoader Mod Template:GitHub上有许多Visual Studio项目模板,能帮你快速搭建开发环境。
- dnSpy / ILSpy:反编译游戏
- 学习资源:
- 阅读其他开源Mod的代码是最好的学习方式。
- MelonLoader的Wiki提供了基础的API文档。
- Harmony库的官方文档详细解释了各种打补丁的方式(Prefix, Postfix, Transpiler等)。
最后,我想分享一点个人体会:MelonLoader生态的繁荣,离不开每一位使用者和开发者的共同维护。作为用户,在遇到问题时,先查看日志、阅读Mod说明、尝试自己排查,然后再到社区礼貌地提问。作为开发者,编写清晰的文档、声明明确的依赖、处理好异常,能让你的Mod更受欢迎。这个工具的强大之处,在于它把修改游戏的能力,从少数“黑客”手中,交到了每一个有想法的玩家和创作者手里。无论是想微调体验,还是创造全新的游戏玩法,这条路已经铺好,剩下的就是你的创意和动手能力了。
