• (转)Unity C#热更新方案 ILRuntime学习笔记(一) Hello World


    转载请标明原文地址:https://segmentfault.com/a/11...

    一、ILRuntime介绍

    问:什么是热更新?
    答:软件在使用时就能实现更新的方式就叫做热更新。热更新无需用户重新下载安装或重启,在使用时即可更新,方便快捷体验良好。

    问:什么是ILRuntime?
    答:ILRuntime是一个C#热更新方案。ILRuntime项目为基于C#的平台(例如Unity)提供了一个纯C#实现,快速、方便且可靠的IL运行时,使得能够在不支持JIT的硬件环境(如iOS)能够实现代码的热更新

    问:lua 和 ILRuntime哪个热更新方案更好?
    答:如果你的团队更熟悉lua,就用lua。如果你的团队更熟悉C#就用ILRuntime。如果你是主程,你可以选择自己喜欢的方案,但是要肩负起填坑的责任。

    我个人的感觉是:lua在Unity中用起来很难受,不是lua这门语言不好,而是因为在Unity中官方的开发语言是C#。用lua就意味着开发者要会两种语言,学习和开发成本都高,而且因为C#是强类型、面向对象的语言。lua是弱类型,非面向对象的语言。
    lua从编程思想和代码写法都和C#有较大差距,这一点在面对越大的项目时感受越明显,项目小的时候觉得lua还好,项目做大了以后会发现lua带给你的麻烦会大于便利。
    而ILRuntime方案是基于C#的,开发语言统一,编码更容易。不过他的缺点是实际经过验证的项目还是太少了,不太成熟,可能有很多坑需要填,不像是经过很多项目验证的lua,有比较成熟的方案。

    二、下载ILRuntime

    GitHub:https://github.com/Ourpalm/IL...
    Unity Demo:https://github.com/Ourpalm/IL...
    国内码云:https://gitee.com/zhangyu800/...
    中文手册:http://ourpalm.github.io/ILRu...

    先去GitHub上点个赞,支持一下该项目,再去Unity Demo上把项目下载下来,顺便也点个赞。
    如果国外地址下载慢,可以在国内码云上下载Demo。

    ILRuntimeDemo.png

    三、导入ILRuntime

    1.解压缩Unity Demo
    打开Unity工程目录下的ProjectSettings/ProjectVersion.txt 查看工程版本。

    为了避免因为不同版本导致的兼容问题,工程版本和Unity版本尽量保持一致,我下载的Demo工程版本是2019.3.6f1, 我尽量用2019.3.6 或稍微高一点儿的版本导入。

    ProjectVersion.png

    2.目录结构

    工程导入完毕,看下目录结构。

    Demo目录:
    在Samples/ILRuntime/1.6.2/Demo/_Scenes/Examples文件夹下

    目录结构.png

    热更新加载的代码目录:
    热更新代码会从StreamingAssets目录下加载编译后的dll
    其中mdb和pdb文件都是调试时用的,发布时只需要dll。

    StreamingAssets文件夹2.png

    运行Hello World Demo:

    打开并运行 01_Hello World 场景。可以看到控制台输出了如下结果。
    01_Hello World.png

    ILR是如何工作的呢?看下HelloWorld脚本。

    using UnityEngine;
    using System.Collections;
    using System.IO;
    using ILRuntime.Runtime.Enviorment;
    
    public class HelloWorld : MonoBehaviour
    {
        //AppDomain是ILRuntime的入口,最好是在一个单例类中保存,整个游戏全局就一个,这里为了示例方便,每个例子里面都单独做了一个
        //大家在正式项目中请全局只创建一个AppDomain
        AppDomain appdomain;
    
        System.IO.MemoryStream fs;
        System.IO.MemoryStream p;
        void Start()
        {
            StartCoroutine(LoadHotFixAssembly());
        }
    
        IEnumerator LoadHotFixAssembly()
        {
            //首先实例化ILRuntime的AppDomain,AppDomain是一个应用程序域,每个AppDomain都是一个独立的沙盒
            appdomain = new ILRuntime.Runtime.Enviorment.AppDomain();
            //正常项目中应该是自行从其他地方下载dll,或者打包在AssetBundle中读取,平时开发以及为了演示方便直接从StreammingAssets中读取,
            //正式发布的时候需要大家自行从其他地方读取dll
    
            //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
            //这个DLL文件是直接编译HotFix_Project.sln生成的,已经在项目中设置好输出目录为StreamingAssets,在VS里直接编译即可生成到对应目录,无需手动拷贝
            //工程目录在AssetsSamplesILRuntime1.6DemoHotFix_Project~
            //以下加载写法只为演示,并没有处理在编辑器切换到Android平台的读取,需要自行修改
    #if UNITY_ANDROID
            WWW www = new WWW(Application.streamingAssetsPath + "/HotFix_Project.dll");
    #else
            WWW www = new WWW("file:///" + Application.streamingAssetsPath + "/HotFix_Project.dll");
    #endif
            while (!www.isDone)
                yield return null;
            if (!string.IsNullOrEmpty(www.error))
                UnityEngine.Debug.LogError(www.error);
            byte[] dll = www.bytes;
            www.Dispose();
    
            //PDB文件是调试数据库,如需要在日志中显示报错的行号,则必须提供PDB文件,不过由于会额外耗用内存,正式发布时请将PDB去掉,下面LoadAssembly的时候pdb传null即可
    #if UNITY_ANDROID
            www = new WWW(Application.streamingAssetsPath + "/HotFix_Project.pdb");
    #else
            www = new WWW("file:///" + Application.streamingAssetsPath + "/HotFix_Project.pdb");
    #endif
            while (!www.isDone)
                yield return null;
            if (!string.IsNullOrEmpty(www.error))
                UnityEngine.Debug.LogError(www.error);
            byte[] pdb = www.bytes;
            fs = new MemoryStream(dll);
            p = new MemoryStream(pdb);
            try
            {
                appdomain.LoadAssembly(fs, p, new ILRuntime.Mono.Cecil.Pdb.PdbReaderProvider());
            }
            catch
            {
                Debug.LogError("加载热更DLL失败,请确保已经通过VS打开Assets/Samples/ILRuntime/1.6/Demo/HotFix_Project/HotFix_Project.sln编译过热更DLL");
            }
    
            InitializeILRuntime();
            OnHotFixLoaded();
        }
    
        void InitializeILRuntime()
        {
    #if DEBUG && (UNITY_EDITOR || UNITY_ANDROID || UNITY_IPHONE)
            //由于Unity的Profiler接口只允许在主线程使用,为了避免出异常,需要告诉ILRuntime主线程的线程ID才能正确将函数运行耗时报告给Profiler
            appdomain.UnityMainThreadID = System.Threading.Thread.CurrentThread.ManagedThreadId;
    #endif
            //这里做一些ILRuntime的注册,HelloWorld示例暂时没有需要注册的
        }
    
        void OnHotFixLoaded()
        {
            //HelloWorld,第一次方法调用
            appdomain.Invoke("HotFix_Project.InstanceClass", "StaticFunTest", null, null);
    
        }
    
        private void OnDestroy()
        {
            if (fs != null)
                fs.Close();
            if (p != null)
                p.Close();
            fs = null;
            p = null;
        }
    
        void Update()
        {
    
        }
    }

    惊喜!注释是纯中文的,作者一定是中国人!注释很详细,看注释就行了。

    其中比较重要的代码是:

    //这个DLL文件是直接编译HotFix_Project.sln生成的,已经在项目中设置好输出目录为StreamingAssets,在VS里直接编译即可生成到对应目录,无需手动拷贝
    //工程目录在AssetsSamplesILRuntime1.6DemoHotFix_Project~
    //以下加载写法只为演示,并没有处理在编辑器切换到Android平台的读取,需要自行修改
    WWW www = new WWW("file:///" + Application.streamingAssetsPath + "/HotFix_Project.dll");
    
    //HelloWorld,第一次方法调用
    appdomain.Invoke("HotFix_Project.InstanceClass", "StaticFunTest", null, null);

    打开HotFix_Project:
    HotFix_Project 是Demo自带的C#热更新代码工程。

    工程目录在
    AssetsSamplesILRuntime1.6DemoHotFix_Project~文件夹下,用Visual Studio打开该工程。

    注意:HotFix_Project~ 后面带了个波浪号,Unity会自动忽略该目录,资源不会被导入,代码不会被编译,文件不会带进发布包。
    具体规则可以看官方文档:
    https://docs.unity3d.com/Manu...

    修改热更新代码:
    打开HotFix项目下的InstanceClass类 修改StaticFunTest方法中输出的内容:

    public static void StaticFunTest()
    {
        UnityEngine.Debug.Log("调用了热更新类的静态方法!");
    }

    生成热更新代码dll:
    右键项目 > 生成
    右键项目 生成.png

    重新运行Hello World Demo:
    查看输出结果,可以看到 我们修改的内容被输出了。

    Log.png

    通过以上步骤,了解了IRuntime的基本运行流程。
    1.热更代码生成dll
    2.Unity加载dll
    3.Unity调用dll里的方法

    三、创建自己的Hotfix工程

    从零开始创建自己的热更工程,体验完整配置流程。
    1.打开Visual Studio,新建项目。
    新建 项目.png

    2.修改项目配置
    选择类库(.NET Framework)。
    修改名称为 Hotfix
    Hotfix 项目设置.png

    修改路径为 Unity项目根目录
    Hotfix 文件夹.png

    3.添加UnityEngine的引用
    在Unity中用Visual Studio打开Unity Demo的任意一个脚本,VS会自动关联需要的Unity类库引用,在项目的引用中可以看到引用的dll路径。
    Hotfix 引用路径.png

    在我们自己的Hotfix工程引用中添加该文件。

    有两个主要的路径

    (1)在Unity安装目录下的EditorData
    我的电脑路径是:D:SDKUnity2019.4.1f1EditorDataManagedUnityEngine
    把该文件夹内所有dll文件都引入到Hotfix工程里。
    拷贝dll.png

    (2)在Unity工程根目录下的LibraryScriptAssemblies
    我的电脑路径是:
    D:ProjectsUnity3DUnity 2019.3 ProjectsILRuntimeU3DILRuntimeDemoLibraryScriptAssemblies
    添加自己需要的dll,我引用了以下两个dll。
    Assembly-CSharp.dll
    UnityEngine.UI.dll

    注:Unity 2019中已经把类库拆的很零散了,应该是为了解耦,为了以后的规划。他把类库拆成两个部分,内置的类库放在Unity安装目录下了,PackageManager包管理器下载的插件都放在Unity工程目录下了,UGUI的库从内置位置移动到PackageManager里了,可能以后要淘汰掉。

    4.编写 Hello World
    添加完引用,可以开始写代码了。
    在我们自己的Hotfix工程中添加一个类 HelloWorld.cs 并输入以下代码:

    namespace Hotfix {
    
        using UnityEngine;
    
        // 冰封百度的Blog:https://segmentfault.com/a/1190000023183723
        public class HelloWorld {
    
            public void Test() {
                Debug.Log("Hello World");
            }
    
        }
    
    }

    5.配置Hotfix.dll输出路径
    右键Hotfix工程, 选择生成选项卡,在下方的输出路径里浏览到StreamingAssets文件夹或填写相对路径:....AssetsStreamingAssets
    Hotfix 输出路径.png

    5.生成Hotfix.dll
    右键Hotfix工程,选择生成。
    生成成功后可以看到如下提示,如果失败则根据错误提示进行处理。
    Hotfix 生成成功.png

    6.修改Unity Demo中的HelloWorld脚本
    主要修改脚本中加载dll的名称,HotFix_Project改为Hotfix。
    并且添加调用热更方法的代码。

    修改后如下:

    using UnityEngine;
    using System.Collections;
    using System.IO;
    using ILRuntime.Runtime.Enviorment;
    
    public class HelloWorld : MonoBehaviour
    {
        //AppDomain是ILRuntime的入口,最好是在一个单例类中保存,整个游戏全局就一个,这里为了示例方便,每个例子里面都单独做了一个
        //大家在正式项目中请全局只创建一个AppDomain
        AppDomain appdomain;
    
        System.IO.MemoryStream fs;
        System.IO.MemoryStream p;
        void Start()
        {
            StartCoroutine(LoadHotFixAssembly());
        }
    
        IEnumerator LoadHotFixAssembly()
        {
            //首先实例化ILRuntime的AppDomain,AppDomain是一个应用程序域,每个AppDomain都是一个独立的沙盒
            appdomain = new ILRuntime.Runtime.Enviorment.AppDomain();
            //正常项目中应该是自行从其他地方下载dll,或者打包在AssetBundle中读取,平时开发以及为了演示方便直接从StreammingAssets中读取,
            //正式发布的时候需要大家自行从其他地方读取dll
    
            //!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
            //这个DLL文件是直接编译HotFix_Project.sln生成的,已经在项目中设置好输出目录为StreamingAssets,在VS里直接编译即可生成到对应目录,无需手动拷贝
            //工程目录在AssetsSamplesILRuntime1.6DemoHotFix_Project~
            //以下加载写法只为演示,并没有处理在编辑器切换到Android平台的读取,需要自行修改
    #if UNITY_ANDROID
            WWW www = new WWW(Application.streamingAssetsPath + "/Hotfix.dll");
    #else
            WWW www = new WWW("file:///" + Application.streamingAssetsPath + "/Hotfix.dll");
    #endif
            while (!www.isDone)
                yield return null;
            if (!string.IsNullOrEmpty(www.error))
                UnityEngine.Debug.LogError(www.error);
            byte[] dll = www.bytes;
            www.Dispose();
    
            //PDB文件是调试数据库,如需要在日志中显示报错的行号,则必须提供PDB文件,不过由于会额外耗用内存,正式发布时请将PDB去掉,下面LoadAssembly的时候pdb传null即可
    #if UNITY_ANDROID
            www = new WWW(Application.streamingAssetsPath + "/Hotfix.pdb");
    #else
            www = new WWW("file:///" + Application.streamingAssetsPath + "/Hotfix.pdb");
    #endif
            while (!www.isDone)
                yield return null;
            if (!string.IsNullOrEmpty(www.error))
                UnityEngine.Debug.LogError(www.error);
            byte[] pdb = www.bytes;
            fs = new MemoryStream(dll);
            p = new MemoryStream(pdb);
            try
            {
                appdomain.LoadAssembly(fs, p, new ILRuntime.Mono.Cecil.Pdb.PdbReaderProvider());
            }
            catch
            {
                Debug.LogError("加载热更DLL失败,请确保已经通过VS打开Assets/Samples/ILRuntime/1.6/Demo/HotFix_Project/HotFix_Project.sln编译过热更DLL");
            }
    
            InitializeILRuntime();
            OnHotFixLoaded();
        }
    
        void InitializeILRuntime()
        {
    #if DEBUG && (UNITY_EDITOR || UNITY_ANDROID || UNITY_IPHONE)
            //由于Unity的Profiler接口只允许在主线程使用,为了避免出异常,需要告诉ILRuntime主线程的线程ID才能正确将函数运行耗时报告给Profiler
            appdomain.UnityMainThreadID = System.Threading.Thread.CurrentThread.ManagedThreadId;
    #endif
            //这里做一些ILRuntime的注册,HelloWorld示例暂时没有需要注册的
        }
    
        void OnHotFixLoaded()
        {
            //HelloWorld,第一次方法调用
            //appdomain.Invoke("HotFix_Project.InstanceClass", "StaticFunTest", null, null);
    
            // 实例化Hotfix中的类
            object obj = appdomain.Instantiate("Hotfix.HelloWorld");
            appdomain.Invoke("Hotfix.HelloWorld", "Test", obj);
        }
    
        private void OnDestroy()
        {
            if (fs != null)
                fs.Close();
            if (p != null)
                p.Close();
            fs = null;
            p = null;
        }
    
    }

    运行Unity,可以看到输出了Hello World
    Hotfix Hello World 输出.png

    至此,ILRuntime Hello World部分已经全部完成了。

    总结:
    ILRuntime使用时,除了Hotfix工程的配置稍微麻烦一些外,其他部分都不难,Demo做的很完善,基本是开箱即用。具体使用细节建议仔细阅读官方文档:
    http://ourpalm.github.io/ILRu...

  • 相关阅读:
    Linux 文件系统相关的基本概念
    Logstash : 从 SQL Server 读取数据
    Windows 下配置 Logstash 为后台服务
    通过 Filebeat 收集 ubuntu 系统日志
    Logstash Multiple Pipelines
    零基础学编程
    2017年计划
    2016年的年终总结
    订阅《通往财富自由之路》3个月后,我做出了哪些改变
    2016年第20本:社交红利2.0
  • 原文地址:https://www.cnblogs.com/wodehao0808/p/15062307.html
Copyright © 2020-2023  润新知