ILRuntime Unity hot update

Disclaimer: This article is a blogger original article, follow the CC 4.0 BY-SA copyright agreement, reproduced, please attach the original source link and this statement.
This link: https://blog.csdn.net/wangjiangrong/article/details/90294366

In the new project, the use of heat to update ILRuntime embodiment, unlike XLua the like, in this way the hot update is implemented pure C # , so Lua code does not need to know the client. A more detailed description can see the official documents.

The official presentation and document: http://ourpalm.github.io/ILRuntime/public/v1/guide/index.html

Currently roughly understood as follows: The game is divided into two parts, Unity and Hotfix. Logic game in which the main part of which is likely to require more hot parts which are written in Hotfix, and then export it for use Unity as Hotfix.dll file. After the game on-line if the code needs to be updated, only you need to modify the code Hotfix, and then generate a new file Hotfix.dll can heat up more.

Then we use a simple Demo to implement such a process. Idea is, the original UI logic Hotfix classes in which, since there is no inheritance MonoBehaviour, it is realized by an interface with a Start, Update the like, and then the corresponding call portion Unity, Hotfix achieved in life cycle. While finding corresponding components GameObject.Find by the method of operation.

 

Creating Unity engineering environment

First we create a new Unity project, then the project will be part of the import ILRuntime need them, that is the official Demo in Mono.Cecil.20, Mono.Cecil.Pdb, ILRuntime three folders (delete ILRuntime / Adapters / Editor)

Check the player settings -> Other Settings -> Allow 'unsafe' Code option

 

Creating Hotfix engineering environment

Open our VS, File -> New -> Project, create a C # class library named Hotfix, as shown in

Then the solution -> Right at the reference, add a reference, as

Which UnityEngine.dll, UnityEngine.UI.dll and UnityEngine.CoreModule.dll three files in the installation directory Unity, Unity Assembly-CSharp.dll project created above Library / ScriptAssemblies directory

Note: UnityEngine.CoreModule.dll is only after the release of Unity2017.2, the previous version can not be added, if UnityEngine.dll newspaper can not find the wrong thing at the official demo, the re-referenced dll file in the correct directory It can be.

 

Creating an interface with the adapter, and implement an interface

First, we can create a simple interface in Unity, for the life cycle of processing Hotfix

public interface IUI
{
    void Start();
    void Update();
}

Then Hotfix project, create a new class Main.cs, implement the interface IUI

namespace Hotfix
{
    //IUI为unity中的接口,所以要在unity中实现一个继承适配器
    public class MainUI:IUI
    {
        public void Start()
        {

        }

        public void Update()
        {

        }
    }
}

Because IUI is the Unity interface, and MainUI are Hotfix class, there is a ILRuntime in cross-domain inherited the concept of official document mentioned that if you want to inherit a Unity project in the main class in which the heat more DLL project, or to achieve a main project in the interface, you need to implement a project in Unity primary inheritance adapter.

So we create a class InterfaceIUIAdaptor.cs in Unity, implementation inheritance adapter (modified according to the document code)

using ILRuntime.CLR.Method;
using ILRuntime.Runtime.Enviorment;
using ILRuntime.Runtime.Intepreter;
using System;

public class InterfaceIUIAdaptor : CrossBindingAdaptor
{
    public override Type BaseCLRType {
        get {
            return typeof(IUI);//这是你想继承的那个类
        }
    }

    public override Type AdaptorType {
        get {
            return typeof(Adaptor);//这是实际的适配器类
        }
    }

    public override object CreateCLRInstance(ILRuntime.Runtime.Enviorment.AppDomain appdomain, ILTypeInstance instance)
    {
        return new Adaptor(appdomain, instance);//创建一个新的实例
    }

    //实际的适配器类需要继承你想继承的那个类,并且实现CrossBindingAdaptorType接口
    public class Adaptor : IUI, CrossBindingAdaptorType
    {
        ILTypeInstance instance;
        ILRuntime.Runtime.Enviorment.AppDomain appdomain;

        IMethod m_Start;
        bool m_StartGot;

        IMethod m_Update;
        bool m_UpdateGot;

        public Adaptor()
        {

        }

        public Adaptor(ILRuntime.Runtime.Enviorment.AppDomain appdomain, ILTypeInstance instance)
        {
            this.appdomain = appdomain;
            this.instance = instance;
        }

        public ILTypeInstance ILInstance { get { return instance; } }

        //你需要重写所有你希望在热更脚本里面重写的方法,并且将控制权转到脚本里去
        public void Start()
        {
            if (!m_StartGot)
            {
                m_Start = instance.Type.GetMethod("Start", 0);
                m_StartGot = true;
            }
            if (m_Start != null)
            {
                appdomain.Invoke(m_Start, instance, null);//没有参数建议显式传递null为参数列表,否则会自动new object[0]导致GC Alloc
            }
        }

        public void Update()
        {
            if (!m_UpdateGot)
            {
                m_Update = instance.Type.GetMethod("Update", 0);
                m_UpdateGot = true;
            }
            if (m_Update != null)
            {
                appdomain.Invoke(m_Update, instance, null);
            }
        }
    }
}

 

读取Hotfix.dll文件,并执行其内部操作

首先我们将Hotfix工程中,解决方案右键生成,生成Hotfix.dll和Hotfix.pdb两个文件,将这两个文件拷贝到Unity的StreamingAssets目录下。

然后我们创建一个新的类 Launch.cs,在这里面我们首先读取上面的两个Hotfix文件,然后进行一些ILRuntime的预设置,例如绑定继承适配器,注册委托等。最后我们要在里面找到Hotfix中实现IUI接口的类,因为这些类就是我们的UI逻辑类,然后在自己的Start,Update等生命周期方法中,调用Hotfix中IUI类对应的方法。代码如下:

using ILRuntime.Runtime.Enviorment;
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using UnityEngine;

public class Launch : MonoBehaviour
{
    List<Action> DllUIUpdateList = new List<Action>();

    ILRuntime.Runtime.Enviorment.AppDomain appdomain;
    void Start()
    {
        StartCoroutine(LoadILRuntime());
    }

    IEnumerator LoadILRuntime()
    {
        //读取dll文件
        appdomain = new ILRuntime.Runtime.Enviorment.AppDomain();

        WWW www = new WWW(Application.streamingAssetsPath + "/Hotfix.dll");

        while (!www.isDone)
        {
            yield return null;
        }
        if (!string.IsNullOrEmpty(www.error))
        {
            Debug.LogError(www.error);
        }
        byte[] dll = www.bytes;
        www.Dispose();

        www = new WWW(Application.streamingAssetsPath + "/Hotfix.pdb");

        while (!www.isDone)
        {
            yield return null;
        }
        if (!string.IsNullOrEmpty(www.error))
        {
            Debug.LogError(www.error);
        }
        byte[] pdb = www.bytes;
        using (System.IO.MemoryStream fs = new MemoryStream(dll))
        {
            using (System.IO.MemoryStream p = new MemoryStream(pdb))
            {
                appdomain.LoadAssembly(fs, p, new Mono.Cecil.Pdb.PdbReaderProvider());
            }
        }

        OnILRuntimeInit();

        OnILRuntimeInitialized();
    }

    void Update()
    {
        if (DllUIUpdateList.Count > 0)
        {
            foreach(var update in DllUIUpdateList)
            {
                update();
            }
        }
    }

    void OnILRuntimeInit()
    {
        //跨域继承绑定适配器
        appdomain.RegisterCrossBindingAdaptor(new InterfaceIUIAdaptor());
        //Button点击事件的委托注册
        appdomain.DelegateManager.RegisterDelegateConvertor<UnityEngine.Events.UnityAction>((act) =>
        {
            return new UnityEngine.Events.UnityAction(() =>
            {
                ((Action)act)();
            });
        });
    }

    void OnILRuntimeInitialized()
    {        
        //获取Hotfix.dll内部定义的类
        List<Type> allTypes = new List<Type>();
        var values = appdomain.LoadedTypes.Values.ToList();
        foreach (var v in values)
        {
            allTypes.Add(v.ReflectionType);
        }
        //去重
        allTypes = allTypes.Distinct().ToList();

        DllUIUpdateList.Clear();
        foreach (var v in allTypes)
        {
            //找到实现IUI接口的类 Adaptor 前面写的适配器IUI的类
            if (v.IsClass && v.GetInterface("Adaptor") != null)
            {
                //生成实例
                var gs = appdomain.Instantiate<IUI>(v.FullName);

                //调用接口方法
                gs.Start();
                DllUIUpdateList.Add(gs.Update);
            }
        }
    }
}

注:代码中有一个委托注册的功能,是因为在Hotfix中调用UGUI的Button的onCkick,需要生成委托转换器,否则会报错

 

搭建UI及实现UI逻辑

Demo中,简单的在场景中创建一个简单的Button和Text,然后将Launch.cs挂到Canvas上即可。然后在我们之前Hotfix中创建的MainUI.cs中添加我们的UI逻辑:

using UnityEngine;
using UnityEngine.UI;

namespace Hotfix
{
    //IUI为unity中的接口,所以要在unity中实现一个继承适配器
    public class MainUI:IUI
    {
        Button m_btn;
        Text m_text;
        int count = 0;
        bool isClick = false;

        public void Start()
        {
            m_btn = GameObject.Find("Canvas/Button").GetComponent<Button>();
            m_text = GameObject.Find("Canvas/Text").GetComponent<Text>();
            m_text.text = "MainUI Start";

            //点击事件的委托需要在unity中实现委托转换器
            m_btn.onClick.AddListener(BtnClick);
        }

        public void Update()
        {
            if (isClick)
            {
                if (count % 20 == 0)
                {
                    m_text.text = "MainUI Update" + count / 20;
                }
                count++;
            }
        }

        void BtnClick()
        {
            isClick = true;
        }
    }
}

然后重新生成下dll文件,将原来Unity StreamingAssets下的文件替换掉即可(以后修改逻辑亦是如此,达到热更的效果)。

 

运行效果如下:

Guess you like

Origin blog.csdn.net/wangjiangrong/article/details/90294366
Recommended