Unity游戏Mod/插件制作教程06 - Harmony补丁基础
前言
通过之前的教程,我们已经知道如何编写基本的插件,如果你有C#和Unity的基础,这个时候已经可以做出一些功能了,比如通过按键修改游戏数据之类的。但是,这有很大的局限性,因为通常情况下,我们并不想通过按键来调用我们的功能,我们想让大多数的功能都是加载之后就不需要管了,或者想做一些普通情况下比较难以操作的事情。这个时候,通过Harmony进行补丁可以解决我们绝大多数的需求。
Harmony的github链接 https://github.com/pardeike/Harmony 详细信息可以在github查看。
Harmony中使用最频繁的两个地方就是前置补丁和后置补丁,也是最简单的,本篇文章主要就讲这两种。一些特殊的需求需要修改函数本身也是可以的,Harmony支持修改函数的IL码,不过这个就不在基础的范畴了,以后有机会的话会放在进阶篇来讲。
HarmonyPatch特性
要对游戏中的方法进行补丁,首先我们需要确定一个目标,这里我准备了一个类,我们就以这个类为例子,对它进行补丁。
public class People
{
public string Name{ get; set; };
public int Age{ get; set; };
public People(string name, int age)
{
Name = name;
Age = age;
}
public void Sleep()
{
Console.WriteLine("睡觉");
}
public void Say()
{
Console.WriteLine("Hello");
}
public void Say(string content)
{
Console.WriteLine(content);
}
public void Say(int content)
{
Console.WriteLine(content);
}
}
这是一个简单的People类,有两个属性,分别是姓名和年龄,一个构造函数,一个Sleep方法,还有3个说话的方法,使用了3种重载。
以Sleep方法为例,我们写一个最简单的补丁。
[HarmonyPatch(typeof(People), "Sleep")]
class PeopleSleepPatch
{
public static void Postfix(People __instance)
{
Console.WriteLine(__instance.Name + "睡觉了");
}
}
这里的HarmonyPatch特性就用于确定补丁目标,这个特性的参数可以写在一排也可以分成几排写。例子中的两个参数分别是要补丁的类型,还有要补丁的方法的名字。这是最简单的情况,实际上我们还经常会遇到其他几种情况。比如,Say方法有3个重载,如何确定要补丁哪一个?属性要怎么补丁?我们再来看几个例子。
[HarmonyPatch(typeof(People), "Say", new Type[] { })]
[HarmonyPatch(typeof(People), "Say", new Type[] { typeof(string) })]
[HarmonyPatch(typeof(People), "Say", new Type[] { typeof(int) })]
如此,面对有重载的情况,我们只需要在添加一个参数,这个参数是一个Type数组,我们按顺序将参数类型填入即可。
[HarmonyPatch(typeof(People), "Name", MethodType.Getter]
[HarmonyPatch(typeof(People), "Age", MethodType.Setter]
[HarmonyPatch(typeof(People), MethodType.Constructor]
面对属性和构造函数,我们可以使用MethodType枚举来当作参数。需要注意的是,补丁构造函数时,函数名不能写dnSpy中看到的.ctor,而是应该直接省略不写函数名。
补丁方法
既然已经可以确定补丁目标了,接下来让我们了解一下最基础最常用的两种补丁方法,Prefix(前置补丁)、Postfix(后置补丁)。
先说后置补丁,这是最简单的,它在补丁目标运行结束之后运行,上面示例中的就是后置补丁,可以使用__result参数接收目标的返回值。
然后是前置补丁,顾名思义,它是在补丁目标运行之前运行的,这个相对复杂一点。因为我们可以选择是否执行原方法。我们来看两个例子。
[HarmonyPatch(typeof(People), "Say", new Type[] { typeof(string) })]
class PeopleSayPatch
{
public static bool Prefix(ref string content)
{
content = "要说的内容已被修改";
return true;
}
}
[HarmonyPatch(typeof(People), "Name", MethodType.Getter)]
class PeopleNamePatch
{
public static bool Prefix(ref string __result)
{
__result = "张三";
return false; //拦截原方法,直接使用我们给出的结果
}
}
第一个例子,我们将content的值修改为了我们自己想要的值,然后返回true表示让原函数继续执行。第二个例子,我们直接将最终结果修改,然后返回false,表示阻止原函数执行。如果你搞不明白IL代码,不知道如何修改函数本体,也可以通过前置补丁的方式自己计算结果然后修改。
补丁参数
在上面的例子中,我们有时候使用了__instance,有时候使用了__result,想必读者还留有疑问,为什么要这么写。其实,这是Harmony作者为我们定好的获取方法信息的方式。
大概情况如下:
补丁方法必须是静态方法
Prefix需要返回void或者bool类型(void即不拦截)
Postfix需要返回void类型,或者返回的类型要与第一个参数一致(直通模式)
如果原方法不是静态方法,则可以使用名为__instance(两个下划线)的参数来访问对象实例
可以使用名为__result(两个下划线)的参数来访问方法的返回值,如果是Prefix,则得到返回值的默认值
可以使用名为__state(两个下划线)的参数在Prefix补丁中存储任意类型的值,然后在Postfix中使用它,你有责任在Prefix中初始化它的值
可以使用与原方法中同名的参数来访问对应的参数,如果你要写入非引用类型,记得使用ref关键字
补丁使用的参数必须严格对应类型(或者使用object类型)和名字
我们的补丁只需要定义我们需要用到的参数,不用把所有参数都写上
要允许补丁重用,可以使用名为__originalMethod(两个下划线)的参数注入原始方法
Transpilers还有一些可选参数,我们这里不做探讨,想了解可以访问Harmony的wiki。
自动补丁
补丁的情况我们大体介绍完了,但是我们现在只是写了补丁,还没有对游戏进行补丁,其实很简单,我们只要在插件加载的时候,加上一句代码就好。
new Harmony("me.xiaoye97.plugin.Tutorial").PatchAll(); //以作者输入的字符串作为ID,对程序集中所有找到的补丁方法进行补丁。
或者
Harmony.CreateAndPatchAll(typeof(PluginTutorial)); //Harmony以类名为ID进行补丁,并且只补丁此类下的方法。
除了自动补丁之外,还可以进行手动补丁,可以更加细微的控制,就不在基础教程中说了,读者可以通过GitHub继续了解,以后的教程如果遇到需要手动补丁的情况我再继续讲解。
Unity游戏Mod/插件制作教程06 - Harmony补丁基础相关推荐
- Unity游戏Mod/插件制作教程05 - 插件实例2: 简单功能实现
这一次的教程进行一个小小的功能实现,完整的制作一个插件.以Mirror这个游戏为例,插件的目标是当玩家按下空格时,有一定概率为玩家增加金钱,或者扣除玩家金钱.概率.增加的金钱.扣除的金钱都由配置文件决 ...
- Unity游戏Mod/插件制作教程01 - BepInEx的安装和使用
前言 本章节为没有使用过BepInEx的同学进行BepInEx的安装和使用方面的介绍,如果你之前已经使用过并了解如何使用,可以直接跳过本章节. BepInEx下载 BepInEx的Github链接 h ...
- Unity游戏Mod/插件制作教程03 - 插件实例1: HelloWorld
准备工作 作为编程类的教程,果然第一个需要来一个传统项目--HelloWolrd. 在开始之前,我先贴一个链接,这是BepInex官方的开发手册 https://bepinex.github.io/b ...
- Unity游戏Mod/插件制作教程02 - 开发环境准备
前言 虽然本教程的目标读者是有C#基础的玩家,但是作为流程,基础的开发软件部分我还是要记录一下. 安装VisualStudio VisualStudio是我们开发插件最重要的工具,也许你习惯其他开发. ...
- WordPress插件制作教程概述
接下来的一段时间里,开始为大家讲解WordPress插件制作系列教程,这篇主要是对WordPress插件的一些介绍和说明,还有一些我们需要注意的地方,以及需要掌握的知识. WordPress插件允许你 ...
- Unity游戏开发官方入门教程:飞机大战(六)——创建子弹
Unity版本:Unity 2018.2.14f1 原视频链接:https://unity3d.com/cn/learn/tutorials/s/space-shooter-tutorial 教程目录 ...
- Unity游戏开发官方入门教程:飞机大战(二)——创建飞船对象
Unity版本:Unity 2018.2.14f1 原视频链接:https://unity3d.com/cn/learn/tutorials/s/space-shooter-tutorial 教程目录 ...
- Unity游戏开发官方入门教程:飞机大战(五)——实现飞船控制脚本
Unity版本:Unity 2018.2.14f1 原视频链接:https://unity3d.com/cn/learn/tutorials/s/space-shooter-tutorial 教程目录 ...
- Unity游戏开发官方入门教程:飞机大战(十)——敌人的爆炸和移动
Unity版本:Unity 2018.2.14f1 原视频链接:https://unity3d.com/cn/learn/tutorials/s/space-shooter-tutorial 教程目录 ...
最新文章
- Dataset:数据集集合(综合性)——机器学习、深度学习算法中常用数据集大集合(建议收藏,持续更新)
- box-sizing:border-box
- 赵鹏计算机专业,安徽大学计算机科学与技术学院导师介绍:赵鹏
- luogu P1330 封锁阳光大学
- linux系统中/etc/syslog.conf文件解读
- linux server添加硬盘,新增硬盘扩充linux ftp server空间
- linux修改grub权限,linux下肿么修改grub.cfg
- 怎么监听linux防火墙,linux怎么查看防火墙是否开启并清除防火墙规则?
- 【Python】Python列表基本操作
- 下列有关计算机图形的叙述中错误的是,计算机图形学(9-16)-中国大学mooc-题库零氪...
- 网狐荣耀6701/6801服务端 子游戏编译 部署
- 如何理解c和c ++的复杂类型声明
- OpenCV x64 vs2010 下打开摄像头录制视频写成avi(代码为转载)
- 微信小程序chooseMedia应用
- 腾讯企业邮箱开发(非官方开发文档方式
- ThinkPHP拼团拼购h5单商户商城[可对接公众号]非常棒的一款h5拼团商城源码
- 如此可爱,焉能不爱?—我与在线作业不得不说的故事
- Python:实现字符串Z 函数或 Z 算法(附完整源码)
- 北邮博导孙松林:5G 新物种开启新时代
- R语言矩阵新增一列并提前作为第一列