ILRuntime介绍

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

ILRuntime 官方中文手册

ILRuntime的优势

同市面上的其他热更方案相比,ILRuntime主要有以下优点:
无缝访问C#工程的现成代码,无需额外抽象脚本API
直接使用VS2015进行开发,ILRuntime的解译引擎支持.Net 4.6编译的DLL
执行效率是L#的10-20倍
选择性的CLR绑定使跨域调用更快速,绑定后跨域调用的性能能达到slua的2倍左右(从脚本调用GameObject之类的接口)
支持跨域继承
完整的泛型支持
拥有Visual Studio的调试插件,可以实现真机源码级调试。支持Visual Studio 2015 Update3 以及Visual Studio 2017和Visual Studio 2019

导入ILRuntime插件

在Unity2018以上版本中开始使用ILRuntime

ILRuntime1.6版新增了Package Manager发布,使用Unity2018以上版本可以直接通过Package Manager安装,具体方法如下
通过Unity的Window->Package Manager菜单,打开Package Manager,将上部标签页选项选择为All Packages,Advanced里勾上Show Preview Packages,等待Unity加载完包信息,应该就能在左侧列表中找到ILRuntime,点击安装即可

配置使用非安全代码

导入插件后还需要做一些设置 打开Fill->Build Settings->Player Settings->Other Settings
将允许使用不安全代码勾选上

ILRunTime的使用

热更环境配置

1、在Unity Assers文件夹下新建三个文件夹分别为Hotfix、Model、ThirdParty三个文件夹分别为热更文件夹、Unity主工程文件夹、第三方库文件夹。在每个文件夹下新建一个对应的程序集
新建程序集时需要注意三个程序集都要勾选允许不安全代码这个选项

程序集的名字自定义

热更Hotfix程序集 只是在编译器模式下使用

Model、ThirdParty程序集在任何平台都可以使用


最后点击右下角的Apply保存应用

接着就是程序集之间添加定义引用
热更程Unity.Hotfix序集 会用到主工程 第三方库 和IL库

Unity.Model

ThirdParty


ILRuntime位于 Packges->ILRuntime中

Unity中程序集定义的作用

在Hotfix,Model ThirdParty文件里新建脚本(包括子文件里面的脚本)后,该脚本就会自动编译到对应的程序集中。所以之后新加的脚本就放在Hotfix、Mode、ThirtyParty这三个文件中。

ILRuntime dll文件复制

在Assets中新建一个Editor文件夹和一个存放dll的文件夹

在Edito文件夹中新建一个子文件夹。然后新建一个脚本命名为BuildHotfixEditor

using UnityEngine;
using UnityEditor;
using System.IO;//Unity 自带特性 每次编译完脚本后都会自动编译(编译器模式下)
[InitializeOnLoad]
public class BuildHotfixEditor
{//程序集路径const string scriptAssemblies = "Library/ScriptAssemblies";//目标路径const string codeDir = "Assets/Res/Code";//hotfixdllconst string hotfixDll = "Unity.Hotfix.dll";//Hotfixpdbconst string hotfixPdb = "Unity.Hotfix.pdb";static BuildHotfixEditor() {//目标文件 (必须要添加.bytes后缀)string fixDll = Path.Combine(codeDir,$"{hotfixDll}.bytes");string fixPdb = Path.Combine(codeDir,$"{hotfixPdb}.bytes");//开始复制File.Copy(Path.Combine(scriptAssemblies,hotfixDll),fixDll,true);File.Copy(Path.Combine(scriptAssemblies,hotfixPdb),fixPdb,true);//刷新AssetDatabase.Refresh();Debug.Log("程序集拷贝完成");}}


编译完成后 回到Unity后 会看到Code文件夹了多了两个文件。以后每次编译完脚本后 那两个文件都会自动更新

接着创建一个预制件命名为Code给它添加一个脚本CodeRefrence,该脚本放到Mode模块下

using UnityEngine;
public class CodeRefrence : MonoBehaviour
{public TextAsset HotfixDll;public TextAsset HotfixPdb;}

最后将Code保存为预制件 。以后加载热更文件就从这里去加载了

通过代码加载热更dll

脚本初始化加载

首先给工程加上ILRuntime宏定义

获取缓存的bytes文件、构建AppDomain对象、通过LoadAssebly进行加载
在Mode模块中添加一个脚本HotFixManager

using System.Collections.Generic;
using UnityEngine;
using System.IO;
using ILRuntime.Mono.Cecil.Pdb;
using ILRuntime.CLR.TypeSystem;
using ILRuntime.CLR.Method;
using ILRuntime.Runtime.Intepreter;
using UnityEngine.Events;
using System;
using Unity.Model;
using ILRuntime.Runtime.Stack;
using System.Reflection;public class HotFixManager : MonoBehaviour
{public GameObject code;//内存流MemoryStream hotfixdllstream;MemoryStream hotfixpdbstream;//构建一个AppDoMainILRuntime.Runtime.Enviorment.AppDomain appDomain;private void Start(){Load();}void Load(){//获取缓存的dll pdb两个bytes文件CodeRefrence cr = code.GetComponent<CodeRefrence>();byte[] hotfixdll = cr.HotfixDll.bytes;byte[] hotfixpdb = cr.HotfixPdb.bytes;#if ILRuntime//ILRuntime模式下 将bytes文件缓存到内存流中hotfixdllstream = new MemoryStream(hotfixdll);hotfixpdbstream = new MemoryStream(hotfixpdb);//构建AppDoMain对象 通过它的LoadAssembly来经行加载appDomain = new ILRuntime.Runtime.Enviorment.AppDomain();appDomain.LoadAssembly(hotfixdllstream, hotfixpdbstream, new PdbReaderProvider());Debug.Log("ILRunTime模式下加载程序集完成");#else//普通平台下Assembly.Load(hotfixdll, hotfixpdb);
#endif}}

热更模块Init全部代码 下面出现的那些方法 都在这里

using Unity.Model;
using UnityEngine;
using UnityEngine.UI;namespace Unity.Hotfix
{public class Init :UIBase{public static void Log() {Debug.Log("Log1被调用");}public static void Log(string str){Debug.Log("Log2被调用:"+str);}public static void Log(string str,string str1){Debug.Log("Log3被调用:" + str+":"+str1);}public Init(){Debug.Log("调用无参构造函数");}public Init(string str){Debug.Log("调用有参构造函数"+str);}//要封装属性 外部才能修改 获取private string name="冰封战神";public string Name {get { return name; }set { name = value; }}public int age;public void AddNum(int a,int b){int c = a + b;Debug.Log("计算结果为:"+ c);}public void GenericFunction<T>(T t) {Debug.Log("调用了泛型方法:"+t);}public void GenericFunction<T,G>(T t,G g){Debug.Log("调用了泛型方法:参数一" + t);Debug.Log("调用了泛型方法:参数二" + g);}public void BtnOnclic() {GameObject.Find("Canvas/btn").GetComponent<Button>().onClick.AddListener(() => { Debug.Log("按钮被点击了"); });}public override void HandlerEvent(int id){Debug.Log("抽象方法被调用"+id);}public override string UIname{get{return name;}}public override void Open(string text){base.Open(text);Debug.Log("子类方法被调用 "+text);}public override int AddNum_1(int a, int b){Debug.Log("Init子类方法"+a);return base.AddNum_1(a, b);}}}

HotFix全部代码

using System.Collections.Generic;
using UnityEngine;
using System.IO;
using ILRuntime.Mono.Cecil.Pdb;
using ILRuntime.CLR.TypeSystem;
using ILRuntime.CLR.Method;
using ILRuntime.Runtime.Intepreter;
using UnityEngine.Events;
using System;
using Unity.Model;
using ILRuntime.Runtime.Stack;
using System.Reflection;public class HotFixManager : MonoBehaviour
{public GameObject code;//内存流MemoryStream hotfixdllstream;MemoryStream hotfixpdbstream;//构建一个AppDoMainILRuntime.Runtime.Enviorment.AppDomain appDomain;private void Start(){Load();}void Load(){//获取缓存的dll pdb两个bytes文件CodeRefrence cr = code.GetComponent<CodeRefrence>();byte[] hotfixdll = cr.HotfixDll.bytes;byte[] hotfixpdb = cr.HotfixPdb.bytes;#if ILRuntime//ILRuntime模式下 将bytes文件缓存到内存流中hotfixdllstream = new MemoryStream(hotfixdll);hotfixpdbstream = new MemoryStream(hotfixpdb);//构建AppDoMain对象 通过它的LoadAssembly来经行加载appDomain = new ILRuntime.Runtime.Enviorment.AppDomain();appDomain.LoadAssembly(hotfixdllstream, hotfixpdbstream, new PdbReaderProvider());Debug.Log("ILRunTime模式下加载程序集完成");RegisterDelegata();//ILRuntime 热更中支支持action 没有返回值的委托  Func 带有返回值的这两种委托appDomain.RegisterCrossBindingAdaptor(new UIBaseAdaptor()); //注册跨域继承适配器RegisterCLRMethod();//注册CLR重定向#else//普通平台下Assembly.Load(hotfixdll, hotfixpdb);
#endif}/// <summary>/// 注册跨域继承适配器/// </summary>void RegisterAdaptor() {appDomain.RegisterCrossBindingAdaptor(new UIBaseAdaptor());}/// <summary>/// 注册CLR/// </summary>unsafe void RegisterCLRMethod() {MethodInfo logMethod = typeof(Debug).GetMethod("Log", new Type[] {typeof(object)});appDomain.RegisterCLRMethodRedirection(logMethod, DLog);}/// <summary>/// CLR带参数的方法的重定向重定向/// </summary>/// <param name="__intp"></param>/// <param name="__esp"></param>/// <param name="__mStack"></param>/// <param name="__method"></param>/// <param name="isNewObj"></param>/// <returns></returns>public  unsafe static StackObject* DLog(ILIntepreter __intp, StackObject* __esp, IList<object> __mStack, CLRMethod __method, bool isNewObj){ILRuntime.Runtime.Enviorment.AppDomain __domain = __intp.AppDomain;StackObject* ptr_of_this_method;//只有一个参数,所以返回指针就是当前栈指针ESP - 1StackObject* __ret = ILIntepreter.Minus(__esp, 1);//第一个参数为ESP -1, 第二个参数为ESP - 2,以此类推ptr_of_this_method = ILIntepreter.Minus(__esp, 1);//获取参数message的值object message = StackObject.ToObject(ptr_of_this_method, __domain, __mStack);//需要清理堆栈__intp.Free(ptr_of_this_method);//如果参数类型是基础类型,例如int,可以直接通过int param = ptr_of_this_method->Value获取值,//关于具体原理和其他基础类型如何获取,请参考ILRuntime实现原理的文档。//通过ILRuntime的Debug接口获取调用热更DLL的堆栈string stackTrace = __domain.DebugService.GetStackTrace(__intp);Debug.Log(string.Format("{0}\n{1}", message, stackTrace));return __ret;}public void CallHotfixMethod() {appDomain.Invoke("Unity.Hotfix.Init","Log",null,null);appDomain.Invoke("Unity.Hotfix.Init", "Log", null,"Hello ILRuntime");appDomain.Invoke("Unity.Hotfix.Init", "Log", null,new string[] { "Hello ILRuntime","欢迎来到IL热更中"});//IType type = appDomain.LoadedTypes["Unity.Hotfix.Init"];//根据参数个数 去调用方法 List<IType> param = new List<IType>();param.Add(appDomain.GetType(typeof(string)));//参数的类型 和个数param.Add(appDomain.GetType(typeof(string)));//参数的类型 和个数IMethod method= type.GetMethod("Log", param,null);//要调用的方法 实例 需要传递的参数appDomain.Invoke(method,null,"哈哈哈");appDomain.Invoke(method,null,new string[] {"多参数调用","两个参数" });}public void CallInstantiateFunction() {appDomain.Instantiate("Unity.Hotfix.Init",null);appDomain.Instantiate("Unity.Hotfix.Init",new string[] {"你好 ILRuntime" });//实例化后 并且调用不带参数的构造函数IType type= appDomain.LoadedTypes["Unity.Hotfix.Init"];ILTypeInstance itype= ((ILType)type).Instantiate();//调用带参数的构造函数 直接将参数传递进去 就行了((ILType)type).Instantiate(new string[] {"我又来了 ILRuntime" });}public void CallMemberFunction() {  //获取实例IType type = appDomain.LoadedTypes["Unity.Hotfix.Init"];ILTypeInstance itype = ((ILType)type).Instantiate();//调用成员方法appDomain.Invoke("Unity.Hotfix.Init","AddNum",itype,new object[] { 3,5});//修改成员变量appDomain.Invoke("Unity.Hotfix.Init","set_Name",itype,new object[] { "冰封战神"});string name = (string)appDomain.Invoke("Unity.Hotfix.Init", "get_Name", itype, null);Debug.Log(name);}public void CallGenericMethod() {//获取实例IType type = appDomain.LoadedTypes["Unity.Hotfix.Init"];ILTypeInstance itype = ((ILType)type).Instantiate();//根据参数个数 去调用方法 IType[] genericArguments = new IType[2]; //泛型的参数类型 为stringgenericArguments[0] = appDomain.GetType(typeof(string));genericArguments[1] = appDomain.GetType(typeof(int));appDomain.InvokeGenericMethod("Unity.Hotfix.Init", "GenericFunction",genericArguments,itype,new object[] { "Hello ILRuntime 泛型方法调用",990});}/// <summary>/// 委托转换/// ILRuntime 热更中支支持action 没有返回值的委托  Func 带有返回值的这两种委托/// </summary>void RegisterDelegata() {//将Unity中的 UnityAction委托 转换为Action委托appDomain.DelegateManager.RegisterDelegateConvertor<UnityAction>((Uityact) =>{return new UnityAction(() =>{ ((Action)Uityact)(); });});//Action 带有一个参数 没有返回值的委托注册appDomain.DelegateManager.RegisterMethodDelegate<string>();//Func 带有返回值 和参数的委托注册appDomain.DelegateManager.RegisterFunctionDelegate<int,string,string>();// UnityAction 带有 参数的委托注册appDomain.DelegateManager.RegisterDelegateConvertor<UnityAction<String>>((act)=> { return new UnityAction<string>((arg) => { ((Action<string>)act)(arg); }); });}void BtnText() {//获取实例IType type = appDomain.LoadedTypes["Unity.Hotfix.Init"];ILTypeInstance itype = ((ILType)type).Instantiate();appDomain.Invoke("Unity.Hotfix.Init", "BtnOnclic",itype,null);}//跨域继承void InherFunction() {//创建一个UIBase实例UIBase UIbase;UIbase = appDomain.Instantiate<UIBase>("Unity.Hotfix.Init");UIbase.Open("打开");Debug.Log(UIbase.UIname);UIbase.HandlerEvent(888);UIbase.AddNum_1(10,8);}}

调用热更中的静态方法

热更新中类的限制:一定要写命名空间,不要直接继承Mono
在热更模块中 新建与i个Init脚本,打开脚本 把命名空间设置为Unity.Hotfix

namespace Unity.Hotfix
{public class Init{public static void Log() {Debug.Log("Log1被调用");}public static void Log(string str){Debug.Log("Log2被调用:"+str);}public static void Log(string str,string str1){Debug.Log("Log3被调用:" + str+":"+str1);}}}

在刚才添加的HotFixManager脚本中新加一个调用热更中静态方法的方法

 public void CallHotfixMethod() {//调用无参数静态方法,appdomain.Invoke("类名", "方法名", 对象引用, 参数列表);appDomain.Invoke("Unity.Hotfix.Init","Log",null,null);appDomain.Invoke("Unity.Hotfix.Init", "Log", null,"Hello ILRuntime");appDomain.Invoke("Unity.Hotfix.Init", "Log", null,new string[] { "Hello ILRuntime","欢迎来到IL热更中"});//通过IMethod调用方法//预先获得IMethod,可以减低每次调用查找方法耗用的时间IType type = appDomain.LoadedTypes["Unity.Hotfix.Init"];//参数类型列表List<IType> param = new List<IType>();param.Add(appDomain.GetType(typeof(string)));//参数的类型 和个数param.Add(appDomain.GetType(typeof(string)));//参数的类型 和个数//根据方法名称和参数个数获取方法IMethod method= type.GetMethod("Log", param,null);//要调用的方法 实例 需要传递的参数appDomain.Invoke(method,null,"哈哈哈");appDomain.Invoke(method,null,new string[] {"多参数调用","两个参数" });}

对热更中的类经行实例化

 public void CallInstantiateFunction() {appDomain.Instantiate("Unity.Hotfix.Init",null);//调用无参构造方法appDomain.Instantiate("Unity.Hotfix.Init",new string[] {"你好 ILRuntime" });//调用有参构造方法//实例化后 并且调用不带参数的构造函数IType type= appDomain.LoadedTypes["Unity.Hotfix.Init"];ILTypeInstance itype= ((ILType)type).Instantiate();//调用带参数的构造函数 直接将参数传递进去 就行了((ILType)type).Instantiate(new string[] {"我又来了 ILRuntime" });}

调用实例中的变量和方法

public void CallMemberFunction() {  //获取实例IType type = appDomain.LoadedTypes["Unity.Hotfix.Init"];ILTypeInstance itype = ((ILType)type).Instantiate();//调用成员方法appDomain.Invoke("Unity.Hotfix.Init","AddNum",itype,new object[] { 3,5});//修改成员变量appDomain.Invoke("Unity.Hotfix.Init","set_Name",itype,new object[] { "冰封战神"});//获取成员变量string name = (string)appDomain.Invoke("Unity.Hotfix.Init", "get_Name", itype, null);Debug.Log(name);}

调用热更中的泛型方法

 public void CallGenericMethod() {//获取实例IType type = appDomain.LoadedTypes["Unity.Hotfix.Init"];ILTypeInstance itype = ((ILType)type).Instantiate();//根据参数个数 去调用方法 IType[] genericArguments = new IType[2]; //泛型的参数类型 为stringgenericArguments[0] = appDomain.GetType(typeof(string));genericArguments[1] = appDomain.GetType(typeof(int));appDomain.InvokeGenericMethod("Unity.Hotfix.Init", "GenericFunction",genericArguments,itype,new object[] { "Hello ILRuntime 泛型方法调用",990});}

按钮点击事件监听与委托转换

 void BtnText() {//获取实例IType type = appDomain.LoadedTypes["Unity.Hotfix.Init"];ILTypeInstance itype = ((ILType)type).Instantiate();appDomain.Invoke("Unity.Hotfix.Init", "BtnOnclic",itype,null);}
 /// <summary>/// 委托转换/// ILRuntime 热更中支支持action 没有返回值的委托  Func 带有返回值的这两种委托/// </summary>void RegisterDelegata() {//将Unity中的 UnityAction委托 转换为Action委托appDomain.DelegateManager.RegisterDelegateConvertor<UnityAction>((Uityact) =>{return new UnityAction(() =>{ ((Action)Uityact)(); });});//Action 带有一个参数 没有返回值的委托注册appDomain.DelegateManager.RegisterMethodDelegate<string>();//Func 带有返回值 和参数的委托注册  最后一个时返回值类型 前面的是参数类型appDomain.DelegateManager.RegisterFunctionDelegate<int,string,string>();// UnityAction 带有 参数的委托注册appDomain.DelegateManager.RegisterDelegateConvertor<UnityAction<String>>((act)=> { return new UnityAction<string>((arg) => { ((Action<string>)act)(arg); }); });}

要在dll加载完成后 经行注册调用

跨域继承适配器


using UnityEngine;namespace Unity.Model
{public abstract class UIBase {private string Uiname="萧索之刃";public virtual string UIname{get { return UIname; }set { Uiname = value; }}public virtual void Open(string text){Debug.Log("父类的方法被调用"+text);}public abstract void HandlerEvent(int id);public virtual int AddNum_1(int a,int b){int c = a + b;Debug.Log("UIBase"+c);return c;}}}
namespace Unity.Hotfix
{public class Init :UIBase{public override void HandlerEvent(int id){Debug.Log("抽象方法被调用"+id);}public override string UIname{get{return name;}}public override void Open(string text){base.Open(text);Debug.Log("子类方法被调用 "+text);}public override int AddNum_1(int a, int b){Debug.Log("Init子类方法"+a);return base.AddNum_1(a, b);}}
}
using ILRuntime.Runtime.Enviorment;
using ILRuntime.Runtime.Intepreter;
using System;using UnityEngine;
//适配器
public class UIBaseAdaptor : CrossBindingAdaptor
{//定义访问方法的方法信息//一个参数的方法static CrossBindingMethodInfo<string> mOpen = new CrossBindingMethodInfo<string>("Open");static CrossBindingMethodInfo<int> mhanderEvent = new CrossBindingMethodInfo<int>("HandlerEvent");static CrossBindingMethodInfo<string> set_mUiname = new CrossBindingMethodInfo<string>("set_UIname");//有一个返回值的方法static CrossBindingFunctionInfo<string> get_mUiname = new CrossBindingFunctionInfo<string>("get_UIname");//func委托 最后一个是返回值类型 前面的是参数类型static CrossBindingFunctionInfo<int,int,int> mAdd = new CrossBindingFunctionInfo<int,int,int>("AddNum_1");public override Type BaseCLRType{get{return typeof(Unity.Model.UIBase);//这里是想要继承的类型}}public override Type AdaptorType{get{return typeof(Adapter);}}public override object CreateCLRInstance(ILRuntime.Runtime.Enviorment.AppDomain appdomain, ILTypeInstance instance){return new Adapter(appdomain, instance);}public class Adapter : Unity.Model.UIBase, CrossBindingAdaptorType{ILTypeInstance instance;ILRuntime.Runtime.Enviorment.AppDomain appDomain;//必须要提供一个无参的构造方法public Adapter() { }public Adapter(ILRuntime.Runtime.Enviorment.AppDomain appDomain, ILTypeInstance instance){this.appDomain = appDomain;this.instance = instance;}public ILTypeInstance ILInstance{get{return instance;}}//下面将 所有的函数重载一遍 并中转到热更内//虚方法必须判断当前是否已经再调用中 否则如果脚本类中调用base.Open(text) 就会无限循环 最终导致爆栈public override void Open(string text){if (mOpen.CheckShouldInvokeBase(this.instance))base.Open(text);elsemOpen.Invoke(this.instance,text);}public override void HandlerEvent(int id){mhanderEvent.Invoke(this.instance,id);Debug.Log("适配器中调用");}public override string UIname{get{if (get_mUiname.CheckShouldInvokeBase(this.instance))return base.UIname;elsereturn get_mUiname.Invoke(this.instance);}set{if (set_mUiname.CheckShouldInvokeBase(this.instance))base.UIname = value;elseset_mUiname.Invoke(this.instance,value);}}public override int AddNum_1(int a, int b){if (mAdd.CheckShouldInvokeBase(this.instance))return base.AddNum_1(a, b);elsereturn mAdd.Invoke(this.instance,a,b);}}
}

CLR重定向

  public  unsafe static StackObject* DLog(ILIntepreter __intp, StackObject* __esp, IList<object> __mStack, CLRMethod __method, bool isNewObj){ILRuntime.Runtime.Enviorment.AppDomain __domain = __intp.AppDomain;StackObject* ptr_of_this_method;//只有一个参数,所以返回指针就是当前栈指针ESP - 1StackObject* __ret = ILIntepreter.Minus(__esp, 1);//第一个参数为ESP -1, 第二个参数为ESP - 2,以此类推ptr_of_this_method = ILIntepreter.Minus(__esp, 1);//获取参数message的值object message = StackObject.ToObject(ptr_of_this_method, __domain, __mStack);//需要清理堆栈__intp.Free(ptr_of_this_method);//如果参数类型是基础类型,例如int,可以直接通过int param = ptr_of_this_method->Value获取值,//关于具体原理和其他基础类型如何获取,请参考ILRuntime实现原理的文档。//通过ILRuntime的Debug接口获取调用热更DLL的堆栈string stackTrace = __domain.DebugService.GetStackTrace(__intp);Debug.Log(string.Format("{0}\n{1}", message, stackTrace));return __ret;}
注册CLR重定向 同样需要在Load方法中调用unsafe void RegisterCLRMethod() {MethodInfo logMethod = typeof(Debug).GetMethod("Log", new Type[] {typeof(object)});appDomain.RegisterCLRMethodRedirection(logMethod, DLog);}

Unity热更新之ILRuntime相关推荐

  1. [Unity热更新]04.卸载方式

    [Unity热更新]04.卸载方式 参考链接: https://www.jianshu.com/p/b37ee8cea04c 1.AssetBundle.Unload(false):释放AssetBu ...

  2. C# 实现 rtc_通过Xlua实现unity热更新的一个小例子

    通过Xlua实现unity热更新的一个小例子 一.介绍 ​ 热更新是指在更新游戏资源或者逻辑的时候不需要开发者将游戏再打包.发布.玩家重新下载安装包,而是可以通过将需要更新的资源打包成AssetBun ...

  3. unity案例 mysql lua_通过Xlua实现unity热更新的一个小例子

    通过Xlua实现unity热更新的一个小例子 一.介绍 ​ 热更新是指在更新游戏资源或者逻辑的时候不需要开发者将游戏再打包.发布.玩家重新下载安装包,而是可以通过将需要更新的资源打包成AssetBun ...

  4. Unity热更新机制

    前言 游戏上线后,难免会有一些测试阶段没发现的bug,bug这东西,可大可小. 如果出现重大bug,而又没有热更技术,那么我们为了修复bug就只能强制玩家去商店下载新包,那造成的玩家流失是非常可怕的. ...

  5. Unity 热更新方案之——ILRuntime

    文章目录 前言 一.ILRuntime是什么? 二.ILRuntime使用 1.跨域委托 2.跨域继承 3.CLR绑定与重定向 前言 做游戏离不开热更新,目前市面上热更新方案用的比较多的是Lua(XL ...

  6. ILRuntime Unity热更新

    在新的项目中,使用到了ILRuntime的热更新方式,不同于XLua等,这种方式的热更新是由纯C#实现的,所以就不需要客户端懂Lua的代码.更详细的介绍可以看官方的文档. 官方的介绍及文档为:http ...

  7. Java和U3D比较,Unity热更方案 ILRuntime 和 toLua的比较

    前言 目前市面上流行的热更方案就是lua系列和ILRuntime,选取哪一种需要根据自己的项目进行比对. 无论是ILRuntime还是toLua都是市面上有在用到的热更方案.直观上来讲,都可以通过把代 ...

  8. Unity热更新技术整理

    一.热更新学习介绍 1.什么是热更新 举例来说: 游戏上线后,玩家下载第一个版本(70M左右或者更大),在运营的过程中,如果需要更换UI显示,或者修改游戏的逻辑,这个时候,如果不使用热更新,就需要重新 ...

  9. unity热更新json_Unity3D热更新 CSHotFix入门教程之HelloWorld

    一.了解工程. "Assets"主工程相关:"HotFix"热更新Vs工程:"UnityEngineLibaray"是Unity对应版本的d ...

最新文章

  1. 高内聚、低耦合的含义是什么?
  2. 疯狂python讲义
  3. 【数据结构】B-Tree, B+Tree, B*树介绍
  4. [答疑]-中断流程举例:在TEE侧时产生了FIQ,回到REE后为啥又产生了IRQ
  5. Linux Shell常用技巧(五)
  6. 高效程序员的45个习惯 pdf_如何培养孩子高效学习?养成高效学习的7 个习惯。建议收藏...
  7. swoole 协程channel乱测
  8. linux驱动 自旋锁
  9. java汉字如何通过字节传输,求助,java中怎么用字节流读写汉字
  10. 阿里开发者们的第16个感悟:让阅读源码成为习惯
  11. HDU2082 找单词【母函数】
  12. Vue的单页应用中如何引用单独的样式文件
  13. 智能优化算法:水基湍流优化算法-附代码
  14. I2C详解(2) I2C总线的规范以及用户手册(1) I2C 总线协议
  15. Fences中关于Explorer非正常关闭问题
  16. java excel 转 图片_Java中excel转换为jpg/png图片 采用aspose-cells-18.6.jar
  17. 计算机科学类专升本复习之“C语言结构体”详解(初稿)
  18. cesium添加高德影像图
  19. 总结——硬件工程师面试容易碰到的问题
  20. sqlDbx连接mysql 及乱码

热门文章

  1. centos7 安装Edge Chrome浏览器
  2. python 字典 遍历字典元素
  3. 网络硬件补充知识汇总
  4. 双十一买什么蓝牙耳机好?2022热销蓝牙耳机排行榜
  5. 【译】.NET 7 中的性能改进(八)
  6. MySQL 获取日期函数
  7. 如何实现Android指纹登录
  8. 安装算量软件删除计算图元功能
  9. 游戏封包加密方案解析
  10. 爱丽丝钢琴-Native Instruments Alicia’s Keys 1.5 Kontakt