首页 > 其他分享 >使用Harmony检测Unity.Object的隐式转换

使用Harmony检测Unity.Object的隐式转换

时间:2023-12-08 23:13:06浏览次数:36  
标签:StackTrace Object System Unity bool Harmony var typeof

简介

Unity是一个非常优秀的引擎,但其中有些设计在提供方便的同时也埋下了隐患,比如BroadcastMessage以及UnityEngine.Object中的隐式转换。

本文通过使用Harmony注入C#代码,达到检测隐式转换的效果,提供了替换Unity引擎C#代码的思路。

Harmony

Harmony - a library for patching, replacing and decorating .NET methods during runtime.

Harmony库的作用就是在运行时替换或者装饰.NET函数(Hook钩子或者AOP切面编程),也就是说我们需要和M$IL语言打交道。它官网上的文章介绍了原理以及入门教程

这里我们选择使用手动补丁的方式注入,一方面是提供了更多灵活性,另一方面是如果存在多个隐式转换,那它们的函数签名会是一样的,我们需要通过反射获取返回值的类型来区分。

// add null checks to the following lines, they are omitted for clarity
// when possible, don't use string and instead use nameof(...)
var original = typeof(TheClass).GetMethod("TheMethod");
var prefix = typeof(MyPatchClass1).GetMethod("SomeMethod");
var postfix = typeof(MyPatchClass2).GetMethod("SomeMethod");

harmony.Patch(original, new HarmonyMethod(prefix), new HarmonyMethod(postfix));

DotPeek查看函数名

既然是需要用反射获取函数信息,那我们需要知道函数名称,那隐式转换的函数名称是什么呢? 难道函数名就叫bool?

由于反射需要加载程序集,而程序集是C#编译后的产物,也就是说,反射操作的其实是IL代码。此时我们需要看下编译后 隐式转换函数变成了什么。

这里我们知道了函数名以及函数签名,我们只需要捏造一个相同签名的函数(IL代码),并使用InitializeOnLoad在Unity加载程序集的时候将其替换掉即可。

private static void PatchImplicitConversion()
{
    var method = GetImplicitMethod(typeof(UnityEngine.Object), typeof(bool));

    var patched = typeof(UnityPatcher).GetMethod(nameof(PatchBoolConversion));

    var harmony = new Harmony("com.company.project.product");
    var assembly = typeof(UnityEngine.Object).Assembly;
    harmony.Patch(method, patched);
    Debug.Log("PatchImplicitConversion patch succed");
}
public static bool PatchBoolConversion(UnityEngine.Object exists)
{
  Debug.LogWarning($"WARNING: invoking implicit conversion to bool! {invokingAssembly}");
  return exists != null;
}

区分调用来源


但是一旦注入成功,我们就会发现Unity引擎到处都在使用隐式转换,我们只需要关注自己的代码。那该怎么办呢?

A StackFrame is created and pushed on the call stack for every function call made during the execution of a thread. The stack frame always includes MethodBase information, and optionally includes file name, line number, and column number information.

Diagnostics命名空间为我们提供了StackTrace,然后我们可以通过GetFrame获得对应层级的StackFrame栈帧。如果调用方所在的程序集为Unity引擎,则不显示日志,否则显示日志。

System.Diagnostics.StackTrace stackTrace = new System.Diagnostics.StackTrace();
System.Diagnostics.StackFrame stackFrame = stackTrace.GetFrame(2);

var invokingAssembly = stackFrame.GetMethod().DeclaringType.Assembly.FullName;
bool isUnity = invokingAssembly.Contains("Unity");

if (!isUnity)
    Debug.LogWarning($"WARNING: invoking implicit conversion to bool! {invokingAssembly}");

性能优化

这样我们能够发现自己编写的代码是否调用了 隐式转换,但是实践检验这样性能不太行,编辑器使用时有迟滞的感觉。
这是因为Unity有很多地方都用了这个隐式转换,并且StackTrace的开销很大,两者结合导致了现在的情况。要么不使用StackTrace,要么减少调用次数。

这里我们采用了每隔几次隐式转换,检查一次StackTrace的方法。

private static int i = 0;
public static bool PatchBoolConversion(UnityEngine.Object exists)
{
    i++;
    if(i % 20 == 0)
    {
        System.Diagnostics.StackTrace stackTrace = new System.Diagnostics.StackTrace();
        System.Diagnostics.StackFrame stackFrame = stackTrace.GetFrame(2);

        var invokingAssembly = stackFrame.GetMethod().DeclaringType.Assembly.FullName;
        bool isUnity = invokingAssembly.Contains("Unity");

        if (!isUnity)
            Debug.LogWarning($"WARNING: invoking implicit conversion to bool! {invokingAssembly}");
    }
    return exists != null;
}

最终代码

using HarmonyLib;
using System;
using System.Reflection;
using UnityEditor;
using UnityEngine;

[InitializeOnLoad]
public class UnityPatcher
{
    static UnityPatcher()
    {
        DoPatch();
    }

    private static void DoPatch()
    {
        PatchImplicitConversion();
    }

    private static void PatchImplicitConversion()
    {
        var method = GetImplicitMethod(typeof(UnityEngine.Object), typeof(bool));
        var patched = typeof(UnityPatcher).GetMethod(nameof(PatchBoolConversion));

        var harmony = new Harmony("com.company.project.product");
        var assembly = typeof(UnityEngine.Object).Assembly;
        harmony.Patch(method, new HarmonyMethod(patched));
        Debug.Log("PatchImplicitConversion patch succed");
    }

    private static MethodInfo GetImplicitMethod(Type type, Type returnType)
    {
        var methodList = type.GetMethods();
        foreach (var method in methodList)
        {
            if (method.Name == "op_Implicit" && method.ReturnType == returnType)
            {
                return method;
            }
        }
        return null;
    }

    private static int i = 0;
    public static bool PatchBoolConversion(UnityEngine.Object exists)
    {
        i++;
        if(i % 20 == 0)
        {
            System.Diagnostics.StackTrace stackTrace = new System.Diagnostics.StackTrace();
            System.Diagnostics.StackFrame stackFrame = stackTrace.GetFrame(2);

            var invokingAssembly = stackFrame.GetMethod().DeclaringType.Assembly.FullName;
            bool isUnity = invokingAssembly.Contains("Unity");

            if (!isUnity)
                Debug.LogWarning($"WARNING: invoking implicit conversion to bool! {invokingAssembly}");
        }
        return exists != null;
    }
}

标签:StackTrace,Object,System,Unity,bool,Harmony,var,typeof
From: https://www.cnblogs.com/dewxin/p/17887204.html

相关文章

  • 《钢岚》今日首发,成为首款基于HarmonyOS NEXT开发的战棋新游
    今日,紫龙游戏旗下BlackJack工作室全新战棋旗舰作品《钢岚》在华为游戏中心首发上线,并宣布《钢岚》完成鸿蒙原生应用开发,成为基于HarmonyOSNEXT开发的首款战棋新游,不但进一步丰富了鸿蒙生态战棋品类游戏内容,也是鸿蒙生态游戏内容建设的重要进展,为鸿蒙生态注入更多新鲜血液。作为......
  • HarmonyOS应用开发者高级认证
    HarmonyOS应用开发者高级认证考试简介掌握鸿蒙的核心概念和端云一体化开发、数据、网络、媒体、并发、分布式、多设备协同等关键技术能力,具备独立设计和开发鸿蒙应用能力。及格分数:80+(满分100)次数限制:每月3次注意事项:禁止切屏博文说明:本博文的初衷是帮助大家查漏补缺,......
  • Unity3D 开发中的触摸事件和键盘事件详解
    前言Unity3D是一款强大的游戏开发引擎,可以用于开发各种类型的游戏,包括PC、移动设备和虚拟现实等平台。在Unity3D开发中,触摸事件和键盘事件是非常重要的交互方式。本文将详细介绍Unity3D开发中的触摸事件和键盘事件,包括技术详解和代码实现。对惹,这里有一个游戏开发交流小组,希望大......
  • Unity3D 个性化时间进度条实现详解
    前言时间进度条在游戏开发中是一个常见的UI元素,它可以用来显示游戏中某个任务或者动作的剩余时间。在Unity3D中,我们可以通过一些技术手段来实现一个个性化的时间进度条,以满足游戏开发的需求。本文将详细介绍如何使用Unity3D实现个性化的时间进度条,包括技术详解和代码实现。一、技......
  • HarmonyOS 开发入门(三)
    HarmonyOS开发入门(三)日常逼逼叨在开发入门(一)和开发入门(二)中我们描述了HarmonyOS开发的语言ArKTs以及Ts简单的入门级语法操作以及开发环境的搭建,接下来我们进入第三部分:HarmonyOS基础组件的开发,有任何说的不合理的地方,希望各位看官老爷批评指正......
  • UE蓝图类型转换object引用
    一.常用的记住1.角色之间,获取玩家角色,玩家控制器2.动画蓝图,获取玩家动画3.在游戏模式中记录变量,方便类型转换。但是会影响运行速度4.playerstate优先与gamemode可以使用二.对象发生关系1.重叠对象,触发,打击,引出引脚actor2.getallactor寻找,(getwigdet控件也可以这样找),......
  • Native Drawing开发指导,实现HarmonyOS基本图形和字体的绘制
    场景介绍Native Drawing模块提供了一系列的接口用于基本图形和字体的绘制。常见的应用场景举例:● 2D图形绘制。● 文本绘制。接口说明接口名描述OH_Drawing_BitmapCreate (void)创建一个位图对象。OH_Drawing_BitmapBuild (OH_Drawing_Bitmap *, const uint32_t width, c......
  • 实战最新鸿蒙系统的短视频App:教你一步步掌握HarmonyOS的几个要点
    鸿蒙(HarmonyOS)是华为公司推出的全场景分布式操作系统,具备高效、安全和灵活的特性。本文将分享如何实战鸿蒙系统的智能家居控制App,并教你一步步掌握HarmonyOS的关键要点,并提供相关示例代码。一、了解HarmonyOS的核心概念在实战HarmonyOS智能家居控制App之前,首先需要了解HarmonyOS的......
  • Unity 实现一个FPS游戏的全过程
    Unity是一款功能强大的游戏引擎,它提供了各种各样的工具和功能,以帮助开发者轻松地创建精美的3D游戏和应用程序。在本文中,我们将使用Unity实现一个FPS游戏的全过程,从场景设计、角色控制、敌人AI到最终的打包发布。对啦!这里有个游戏开发交流小组里面聚集了一帮热爱学习游戏的零基础......
  • Unity shader实现水波的技术原理
    Unityshader是Unity引擎中非常重要的一个部分,它可以用来实现各种各样的特效效果,其中包括水波效果。本文将详细讲解Unityshader实现水波的技术原理,并给出相关的Shader代码实现。对啦!这里有个游戏开发交流小组里面聚集了一帮热爱学习游戏的零基础小白,也有一些正在从事游戏......