u3d插件xLua[九]例8 Hotfix

讲解xLua例8 Hotfix,运行时用lua替换C#,demo和代码都是围绕这个主题的

一.运行展示

运行后打印Update in C#, tick =,点击Hotfix按钮后,打印Update in lua, tick

参考:hotfix.md

二.代码展示

HotfixTest.cs

接下来看要实现运行时用lua替换C#要做哪些关键处理,下文将介绍应该怎么做,为什么这么做也会进行必要分析

cs 复制代码
using UnityEngine;
using XLua;

namespace XLuaTest
{
    [Hotfix]
    public class HotfixTest : MonoBehaviour
    {
        LuaEnv luaenv = new LuaEnv();

        private int tick = 0;

        // Use this for initialization
        void Start()
        {
        }

        // Update is called once per frame
        void Update()
        {
            if (++tick % 50 == 0)
            {
                Debug.Log(">>>>>>>>Update in C#, tick = " + tick);
            }
        }

        void OnGUI()
        {
            if (GUI.Button(new Rect(10, 10, 300, 80), "Hotfix"))
            {
                luaenv.DoString(@"
                xlua.hotfix(CS.XLuaTest.HotfixTest, 'Update', function(self)
                    self.tick = self.tick + 1
                    if (self.tick % 50) == 0 then
                        print('<<<<<<<<Update in lua, tick = ' .. self.tick)
                    end
                end)
            ");
            }

            string chHint = @"在运行该示例之前,请细致阅读xLua文档,并执行以下步骤:

1.宏定义:添加 HOTFIX_ENABLE 到 'Edit > Project Settings > Player > Other Settings > Scripting Define Symbols'。
(注意:各平台需要分别设置)

2.生成代码:执行 'XLua > Generate Code' 菜单,等待Unity编译完成。

3.注入:执行 'XLua > Hotfix Inject In Editor' 菜单。注入成功会打印 'hotfix inject finish!' 或者 'had injected!' 。";
            string enHint = @"Read documents carefully before you run this example, then follow the steps below:

1. Define: Add 'HOTFIX_ENABLE' to 'Edit > Project Settings > Player > Other Settings > Scripting Define Symbols'.
(Note: Each platform needs to set this respectively)

2.Generate Code: Execute menu 'XLua > Generate Code', wait for Unity's compilation.


3.Inject: Execute menu 'XLua > Hotfix Inject In Editor'.There should be 'hotfix inject finish!' or 'had injected!' print in the Console if the Injection is successful.";
            GUIStyle style = GUI.skin.textArea;
            style.normal.textColor = Color.red;
            style.fontSize = 16;
            GUI.TextArea(new Rect(10, 100, 500, 290), chHint, style);
            GUI.TextArea(new Rect(10, 400, 500, 290), enHint, style);
        }
    }
}

三.实现步骤

3.1 加特性Hotfix

对可能要用Lua替换逻辑的C#类加上特性Hotfix,遵循加特性的3种方式:加标签、静态列表、动态列表;demo用的加标签,便于展示。

3.2 添加 HOTFIX_ENABLE

3.3 生成代码

执行XLua/Generate Code

3.4 执行注入操作

执行XLua/Hotfix Inject In Editor,成功会打印"hotfix inject finish!",本节是本文重点

3.4.1 注入概念

先来理解一下注入的概念:通过修改编译产物:Assembly-CSharp.dll,使得标记了hotfix特性的C#代码的执行逻辑改为:先看有没有 Lua 补丁 → 有就跑 Lua;没有再跑原来的 C#,也就是为lua替换C#做必要准备

3.4.2 注入源码

Hotfix.cs:1653-1773行

前面部分主要是计算参数,代码核心是最后部分1737-1767,可以先从这里开始看:遍历所有要注入的程序集,每次遍历通过Process这个类运行C:\ProgramFiles\Unity\Hub\Editor\2022.3.62f3c1\Editor\Data\MonoBleedingEdge\bin\mono.exe

把Tools\XLuaHotfixInject.exe作为参数

(XLuaHotfixInject.exe不能直接运行,需要用mono.exe来运行)

这样来注入的

cs 复制代码
            var injectAssemblyPaths = HotfixConfig.GetHotfixAssemblyPaths();
            var idMapFileNames = new List<string>();
            foreach (var injectAssemblyPath in injectAssemblyPaths)
            {
                args[0] = Path.Combine(assemblyDir, Path.GetFileName(injectAssemblyPath));
                if (ContainNotAsciiChar(args[0]))
                {
                    throw new Exception("project path must contain only ascii characters");
                }

                if (injectAssemblyPaths.Count > 1)
                {
                    var injectAssemblyFileName = Path.GetFileName(injectAssemblyPath);
                    args[2] = CSObjectWrapEditor.GeneratorConfig.common_path + "Resources/hotfix_id_map_" + injectAssemblyFileName.Substring(0, injectAssemblyFileName.Length - 4) + ".lua.txt";
                    idMapFileNames.Add(args[2]);
                }
                Process hotfix_injection = new Process();
                hotfix_injection.StartInfo.FileName = mono_path;
#if UNITY_5_6_OR_NEWER
                hotfix_injection.StartInfo.Arguments = "--runtime=v4.0.30319 " + inject_tool_path + " \"" + String.Join("\" \"", args.ToArray()) + "\"";
#else
                hotfix_injection.StartInfo.Arguments = inject_tool_path + " \"" + String.Join("\" \"", args.ToArray()) + "\"";
#endif
                hotfix_injection.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
                hotfix_injection.StartInfo.RedirectStandardOutput = true;
                hotfix_injection.StartInfo.UseShellExecute = false;
                hotfix_injection.StartInfo.CreateNoWindow = true;
                hotfix_injection.Start();
                UnityEngine.Debug.Log(hotfix_injection.StandardOutput.ReadToEnd());
                hotfix_injection.WaitForExit();
            }

调试时变量:

参考:Process 类

3.5 调用xlua.hotfix

用xlua.hotfix替换C#代码这个过程叫做打补丁,其定义在LuaEnv.cs的593-611

cs 复制代码
            xlua.hotfix = function(cs, field, func)
                if func == nil then func = false end
                local tbl = (type(field) == 'table') and field or {[field] = func}
                for k, v in pairs(tbl) do
                    local cflag = ''
                    if k == '.ctor' then
                        cflag = '_c'
                        k = 'ctor'
                    end
                    local f = type(v) == 'function' and v or nil
                    xlua.access(cs, cflag .. '__Hotfix0_'..k, f) -- at least one
                    pcall(function()
                        for i = 1, 99 do
                            xlua.access(cs, cflag .. '__Hotfix'..i..'_'..k, f)
                        end
                    end)
                end
                xlua.private_accessible(cs)
            end

xlua可以用lua函数替换 C# 的构造函数,函数,属性,事件的替换。lua实现都是函数。

四.Demo HotfixTest2

HotfixTest2 是热更能力清单,展示C#这些语法在lua侧怎么写:重载、out/ref、泛型、结构体、构造、属性、事件

如果要替换整个类,不需要一次次的调用 xlua.hotfix 去替换,可以整个一次完成。只要给一个 table,按 method_name = function 组织即可,来看HotfixTest2的案例:

StatefullTest.cs

cs 复制代码
using UnityEngine;

namespace XLuaTest
{
    [XLua.Hotfix]
    public class StatefullTest
    {
        public StatefullTest()
        {

        }

        public StatefullTest(int a, int b)
        {
            if (a > 0)
            {
                return;
            }

            Debug.Log("a=" + a);
            if (b > 0)
            {
                return;
            }
            else
            {
                if (a + b > 0)
                {
                    return;
                }
            }
            Debug.Log("b=" + b);
        }

        public int AProp
        {
            get;
            set;
        }

        public event System.Action<int, double> AEvent;

        public int this[string field]
        {
            get
            {
                return 1;
            }
            set
            {
            }
        }

        public void Start()
        {

        }

        void Update()
        {

        }

        public void GenericTest<T>(T a)
        {

        }

        static public void StaticFunc(int a, int b)
        {
        }
        static public void StaticFunc(string a, int b, int c)
        {
        }

        ~StatefullTest()
        {
            Debug.Log("~StatefullTest");
        }
    }
}

lua替换整个类写法:

Lua 复制代码
            xlua.hotfix(CS.XLuaTest.StatefullTest, {
                -- C#: 构造函数 StatefullTest() / StatefullTest(int,int)
                ['.ctor'] = function(csobj)
                    util.state(csobj, {evt = {}, start = 0, prop = 0})
                end;
                -- C#: 属性 AProp 的 setter(obj.AProp = v)
                set_AProp = function(self, v)
                    print('set_AProp', v)
                    self.prop = v
                end;
                -- C#: 属性 AProp 的 getter(读 obj.AProp)
                get_AProp = function(self)
                    return self.prop
                end;
                -- C#: 索引器 this[string] 的 getter(读 obj[k])
                get_Item = function(self, k)
                    print('get_Item', k)
                    return 1024
                end;
                -- C#: 索引器 this[string] 的 setter(obj[k] = v)
                set_Item = function(self, k, v)
                    print('set_Item', k, v)
                end;
                -- C#: 事件 AEvent 的 +=(add)
                add_AEvent = function(self, cb)
                    print('add_AEvent', cb)
                    table.insert(self.evt, cb)
                end;
                -- C#: 事件 AEvent 的 -=(remove)
                remove_AEvent = function(self, cb)
                   print('remove_AEvent', cb)
                   for i, v in ipairs(self.evt) do
                       if v == cb then
                           table.remove(self.evt, i)
                           break
                       end
                   end
                end;
                -- C#: 实例方法 Start()
                Start = function(self)
                    print('Start')
                    for _, cb in ipairs(self.evt) do
                        cb(self.start, 2)
                    end
                    self.start = self.start + 1
                end;
                -- C#: 静态方法 StaticFunc 的两个重载
                StaticFunc = function(a, b, c)
                   print(a, b, c)
                end;
                -- C#: 泛型实例方法 GenericTest<T>(T a)
                GenericTest = function(self, a)
                   print(self, a)
                end;
                -- C#: 析构函数 ~StatefullTest()
                Finalize = function(self)
                   print('Finalize', self)
                end
           })

五.结论

1.知道xLua提供了运行时lua替换C#代码的能力

2.理解注入的概念和实现

3.xLua项目实际不一定会用Hotfix热补丁功能,拿到xLua项目后先搜索[Hotfix和xlua.hotfix来确定是否使用热补丁功能,若用则需要掌握HotfixTest2

4.注入后每个方法入口多一次「有没有补丁」判断,有开销。业务上只给真正需要热更的类型加 Hotfix

相关推荐
淡海水1 小时前
01-07-运行时-GC深度剖析-内存分配回收与结构选择
jvm·windows·unity·gc
优梦创客4 小时前
游戏开发架构选型:第1篇|什么是架构?从进球事件看 MVC 的局限
unity·架构·游戏引擎·游戏开发
zhangphil1 天前
Unity3D(Unity)跨平台游戏引擎简介
unity
Madokaly1 天前
DOTween动画:TweenManager深度解析
unity
_ZHOURUI_H_2 天前
Unity TCP底层极限拆解:粘包、半包、CRC、序列号、双缓冲到底怎么一起工作的?
网络·网络协议·tcp/ip·游戏·unity·游戏引擎·游戏开发
KindSuper_liu2 天前
Unity 游戏网络基础:从 TCP、UDP 到 HTTP、TLS 与 Best HTTP
网络·游戏·unity
qq_170264752 天前
unity里GameObject和Texture的内存归属
java·unity·游戏引擎
还是大剑师兰特3 天前
Unity3D(Unity)完整详细介绍
unity·游戏引擎
ellis19703 天前
u3d插件xLua[七]例6 Coroutine
unity