??有時候我們不清楚Unity編輯器的邏輯是如何執(zhí)行的结耀,可以通過查看UnityCsReference查看相關(guān)邏輯郭蕉。有些實(shí)用的方法Unity沒有開放出來牺蹄,而做自定義工具會用到灭贷,因此可以利用反射來獲取這些隱藏的方法。
??有時我們需要給修改一些默認(rèn)邏輯,例如在烘焙光照前后執(zhí)行某些邏輯,例如執(zhí)行其他自定義烘焙邏輯叠国、彈出窗口提醒美術(shù)操作等,方便和規(guī)范美術(shù)的流程戴尸。
??修改源碼當(dāng)然是一種操作粟焊,但這樣不方便迭代,也不方便升級孙蒙、切換引擎版本项棠,對項(xiàng)目用到的引擎還要嚴(yán)格規(guī)范,這樣不是一個好的方法挎峦。
??然后想到了Hook沾乘,既然能用UnityCsReference獲取到函數(shù)的MethodInfo,那么用Hook就可以在關(guān)鍵函數(shù)的執(zhí)行前后插入自己的邏輯浑测、修改原有邏輯。
??例如Unity生成光照按鈕的方法(從UnityCsReference找的):
namespace UnityEditor
{
internal class LightingWindowLightingTab
{
public void OnGUI()
{
//something
Buttons();
}
void Buttons()
{
//something
DoBake();
}
}
}
??里面的DoBake就是我們關(guān)注的方法歪玲,如果我們想要在Bake前執(zhí)行操作迁央,就需要對DoBake函數(shù)進(jìn)行Hook。
??本來想查Unity C#如何Hook滥崩,發(fā)現(xiàn)Misaka佬已經(jīng)有現(xiàn)成的庫可用:MonoHook岖圈。
??因此寫了如下的Hook方法:
using System;
using System.Reflection;
using System.Runtime.CompilerServices;
using MonoHook;
using UnityEditor;
using UnityEngine;
#if UNITY_EDITOR
using UnityEditor.Callbacks;
/// <summary>
/// 修改烘焙邏輯
/// </summary>
[InitializeOnLoad]
public static class GenerateLightingCallback
{
private static MethodHook _hook;
static GenerateLightingCallback()
{
if (_hook == null)
{
Type type = Type.GetType("UnityEditor.LightingWindowLightingTab,UnityEditor.dll");
MethodInfo DoBakeTarget = type.GetMethod("DoBake", BindingFlags.Instance | BindingFlags.NonPublic);
MethodInfo Replacement = new Action(NewBakeMethod).Method;
MethodInfo Proxy = new Action(ProxyBakeFunction).Method;
_hook = new MethodHook(DoBakeTarget, Replacement, Proxy);
_hook.Install();
}
}
//Bake 前要做的邏輯
private static void OnBake()
{
//這里可以注冊一些方法來執(zhí)行
Debug.Log("generate lighting!");
}
#region private
[MethodImpl(MethodImplOptions.NoInlining)]
private static void NewBakeMethod()
{
OnBake();//自定義邏輯
ProxyBakeFunction();//調(diào)用原來的邏輯
}
//原方法占用,邏輯不重要钙皮,不會執(zhí)行
[MethodImpl(MethodImplOptions.NoInlining | MethodImplOptions.NoOptimization)]
private static void ProxyBakeFunction()
{
// 隨便亂寫點(diǎn)東西以占據(jù)空間
for (int i = 0; i < 100; i++)
{
UnityEngine.Debug.Log("something");
}
UnityEngine.Debug.Log(Application.targetFrameRate);
}
#endregion
}
#endif