动态IL修补框架Harmony游戏修改实战

Harmony是一个开放源代码库,旨在在运行时替换、修饰或修改任何现有C#方法。它的主要用在用Mono语言编写的游戏和插件,但是该技术可以与任何.NET版本一起使用。它还照顾对同一方法的多次更改(它们累积而不是覆盖)。

它为每个原始方法创建DynamicMethod方法,并向其织入代码,该代码在开始和结束时调用自定义方法。它还允许您编写过滤器来处理原始的IL代码,从而可以对原始方法进行更详细的操作。

使用Yet Another Zombie Defense HD作为本文修改的对象 同样使用BepinEx加载器加载dll

分析代码

打开dnspy找到ChainRaw分析判定代码

public override bool shoot(bool remoteShoot)
	{
		if ((this.ammoLeft > 0f && this.cooldown <= 0f) || remoteShoot)
		{
			this.chainsawRunning = true;
			this.cooldown += this.cooldownTime;
			TheGame.achievements[21].canReceive = false;
			TheGame.achievements[22].canReceive = false;
			this.flashlightShakeTime = 0.05f;
			base.playShootSound();
			this.shootTime += Time.deltaTime;
			float num = 0.9f; //攻击距离
			Vector2 vector = base.Owner.Position.toVector2();
			this.target = vector + base.Owner.getShootDirection().toVector2() * num;
			float num2 = 0.6f; //攻击宽度
			Vector2 vector2 = base.Owner.getShootDirection().toVector2().perpendicular();
			Vector2 vector3 = vector + vector2 * num2;
			Vector2 vector4 = this.target + vector2 * num2;
			Vector2 vector5 = this.target + vector2 * -num2;
			Vector2 vector6 = vector + vector2 * -num2;
			foreach (Enemy enemy in TheGame.Get.Level.Enemies)
			{
				if (!enemy.isDead)
				{
					Vector2 vector7 = enemy.Position.toVector2() + new Vector2(0f, 0.1f);
					bool flag = true;
					float num3 = global::Chainsaw.orderTest(vector3, vector4, vector7);
					float num4 = global::Chainsaw.orderTest(vector4, vector5, vector7);
					if (num3 * num4 < 0f)
					{
						flag = false;
					}
					else
					{
						float num5 = global::Chainsaw.orderTest(vector5, vector6, vector7);
						float num6 = global::Chainsaw.orderTest(vector6, vector3, vector7);
						if (num4 * num5 < 0f || num5 * num6 < 0f)
						{
							flag = false;
						}
					}
					if (flag)
					{
						Vector3 vector8 = (vector + (this.target - vector).normalizedSafeCopy() * ((enemy.Position.toVector2() - vector).magnitude - 0.1f)).toVector3(base.transform.position.z);
						enemy.killedByChainsaw = true;
						enemy.getHit(5f * this.deltaTime, base.Owner.getShootDirection() * 3f, vector8, base.Owner as YAZDObject);
						enemy.killedByChainsaw = false;
						SoundModule.Get.PlayContinuousSequence(base.transform.GetChild(0), this.goreSound, base.transform.position);
					}
				}
			}
			return true;
		}
		return false;
	}
  1. 攻击距离num=0.9f(从玩家位置到target点的距离)
  2. 攻击宽度num2=0.6f(左右各延伸0.6单位宽度)
  3. 判定形状:由4个点组成的四边形:
    • vector3 (左上起点)
    • vector4 (左上终点)
    • vector5 (右下终点)
    • vector6 (右下起点)

所以需要修改num,num2,由于num,num2为局部变量,无法直接赋值,所以需要使用harmony的Transpiler工具操作IL码修改

代码 

Transpiler基础概念

  • 特殊补丁类型,直接操作IL代码
  • 必须返回IEnumerable<CodeInstruction>
[HarmonyPatch(typeof(RocketLauncher))]
class ChainSawPatches
{
    [HarmonyPatch("Awake")]
    [HarmonyPostfix]
    static void DoAttackPostfix(Chainsaw __instance)
    {
        var traverse = Traverse.Create(__instance);
        traverse.Field("effectiveRange").SetValue(15f);
        traverse.Field("reloadTime").SetValue(0.001f);
    }
}

[HarmonyPatch(typeof(Chainsaw), "shoot")]
class ChainsawPatch
{
    static void Prefix(Chainsaw __instance, ref bool remoteShoot)
    {
    }

    static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
    {
        var codes = new List<CodeInstruction>(instructions);
        for (int i = 0; i < codes.Count; i++)
        {
            if (codes[i].opcode == OpCodes.Ldc_R4 && (float)codes[i].operand == 0.9f)
            {
                codes[i].operand = 5f;
            }
            else if (codes[i].opcode == OpCodes.Ldc_R4 && (float)codes[i].operand == 0.6f)
            {
                codes[i].operand = 5f;
            }
        }
        return codes;
    }
}

完整代码如下

using System;
using HarmonyLib;
using BepInEx;
using System.Reflection.Emit;
using UnityEngine;
using BepInEx.Unity.Mono;
using System.Collections.Generic;

[BepInPlugin(guid,pname,version)]
public class Mod : BaseUnityPlugin
{
    private const string guid = "com.example.mod";
    private const string pname = "Example Mod";
    private const string version = "1.0.0";
    Harmony harmony = new Harmony(guid);
    private void Awake()
    {
        harmony.PatchAll();
    }
}

[HarmonyPatch(typeof(RocketLauncher))]
class ChainSawPatches
{
    [HarmonyPatch("Awake")]
    [HarmonyPostfix]
    static void DoAttackPostfix(Chainsaw __instance)
    {
        var traverse = Traverse.Create(__instance);
        traverse.Field("effectiveRange").SetValue(15f);
        traverse.Field("reloadTime").SetValue(0.001f);
    }
}

[HarmonyPatch(typeof(Chainsaw), "shoot")]
class ChainsawPatch
{
    static void Prefix(Chainsaw __instance, ref bool remoteShoot)
    {
    }

    static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
    {
        var codes = new List<CodeInstruction>(instructions);
        for (int i = 0; i < codes.Count; i++)
        {
            if (codes[i].opcode == OpCodes.Ldc_R4 && (float)codes[i].operand == 0.9f)
            {
                codes[i].operand = 5f;
            }
            else if (codes[i].opcode == OpCodes.Ldc_R4 && (float)codes[i].operand == 0.6f)
            {
                codes[i].operand = 5f;
            }
        }
        return codes;
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值