每日一句:保持须臾的浪漫,理想的喧嚣,平等的热情
开发者将测试好的代码,发布到应用商店的审核平台,平台方会进行稳定性及性能测试。测试成功后,用户即可在AppStore看到应用的更新信息,用户点击应用更新后,需要先关闭应用,再进行更新
广义:无需关闭应用,不停机状态下恢复漏洞,更新资源等,重点是更新逻辑代码
狭义定义(ios热更新):无需将代码重新打包提交至AppStore,即可更新客户端的执行代码,即不用下载app 而自动更新程序
现状:苹果禁止了C#的部分反射操作,禁止JIT(即时编译,程序运行时创建并运行新代码),不允许逻辑热更新,只允许使用AssetBundle进行资源热更新
注意:2017年,苹果更新了热更新政策说明,上线后的项目一旦发现使用热更新,一样会以下架处理
缩短用户获取新版应用的客户端的流程,改善用户体验,具体到ios平台的应用上,有以下几个原因
AppStore的审核周期难控制
手机应用更新频繁
对于大型应用,更新成本太大
终极目标
不重新下载,不停机状态下完全变换一个应用内容
Android 、PC(C#)
将执行代码预编译为AssemblyDLL
将代码作为TextAsset打包进AssetBundle
运行时调用AssemblyDLL代码
更新相应的AssetBundle即可实现热更新
ios(lua)
苹果官方禁止ios下的程序热更新:JIT在ios下无效
slua:最快的Lua插件
tolua:由ulua发展而来的,第三代Lua热更新方案
xlua:特性最先进的Lua插件
ILRuntime:纯C#实现的热更新插件
链接:https://pan.baidu.com/s/1er3OBGur5mA1PdIljfq8FQ?pwd=dpvj
提取码:dpvj
解决办法删掉
C#实现的系统,因为Lua可以调用,所以完成可以换成Lua实现,因为Lua可以即时更改,即时运行,所以游戏的代码逻辑就可以随时修改
Lua调用Unity的各种API,从而实现C#开发系统同样的效果
using XLua;
public class First : MonoBehaviour
{
void Start()
{
//lua是解释型语言,所以需要获得lua的解析器
//xLua解析器获得
LuaEnv env = new LuaEnv();
//解析器运行Lua代码,把字符串当成Lua代码执行
env.DoString("print('Hello world ### !')");
//解析器释放
env.Dispose();
}
}
_______________________________________________________________________
using UnityEngine;
using XLua;
//使用Lua调用C#
public class DoString : MonoBehaviour
{
void Start()
{
LuaCallCSharpCode();
}
public void LuaCallCSharpCode()
{
LuaEnv env = new LuaEnv();
//Lua调用C#代码(cs.命名空间.类名.方法名(参数))
env.DoString("CS.UnityEngine.Debug.Log('from lua')");
env.Dispose();
}
_______________________________________________________________________
using XLua;
public class Loader : MonoBehaviour
{
void Start()
{
LuaEnv env = new LuaEnv();
//对应test.lua
//内置加载器会扫描预制的目录,查找是否存在test.lua
//xlua存在默认加载器,StreamingAssets目录下可以加载文件中
env.DoString("require('test')");
env.Dispose();
}
}
_______________________________________________________________________
接触一个新的Lua项目时,先要弄懂Lua的加载器规则,只有这样,才能弄懂项目的Lua执行流程
Xlua的单例运行环境
Xlua解析器创建销毁
Xlua加载器编写
using System.IO;
using UnityEngine;
using XLua;
public class xLuaEnv
{
private static xLuaEnv _Instance = null;
public static xLuaEnv Instance
{
get
{
if(_Instance==null)
{
_Instance = new xLuaEnv();
}
return _Instance;
}
}
private LuaEnv _Env;
//创建单例的时候,Lua运行环境,会一起被创建
private xLuaEnv()
{
_Env = new LuaEnv();
_Env.AddLoader(_ProjectLoader);
}
//创建自定义Lua加载器,这样就可以任意订制项目的Lua脚本的存储位置
private byte[] _ProjectLoader(ref string filepath)
{
string path = Application.dataPath;
path = path.Substring(0, path.Length - 7) + "/DataPath/Lua/" + filepath + ".Lua";
//因为"Application.dataPath"在上线的代码中无法获得,所以上线时,需要将Lua的存储路径,
//指向到"Application.persistentDataPath"
if(File.Exists(path))
{
return File.ReadAllBytes(path);
}
else
{
return null;
}
}
public void Free()
{
//释放LuaEnv,同时也释放单例对象,这样下次调单例对象,会再次产生Lua运行环境
_Env.Dispose();
_Instance = null;
}
public object[] DoString(string code)
{
return _Env.DoString(code);
}
}
——————————————————————————————————————
public class LuaCallStatic : MonoBehaviour
{
void Start()
{
xLuaEnv.Instance.DoString("require('C2L/LuaCallStatic')");
}
private void OnDestroy()
{
xLuaEnv.Instance.Free();
}
}
LuaCallStatic.lua
print(‘LuaCallStatic’)
using UnityEngine;
namespace HX
{
public static class TestStatic
{
public static int ID = 99;
public static string Name
{
get;
set;
}
public static string Output()
{
return "static";
}
public static void Default(string str = "abc")
{
Debug.Log(str);
}
}
public class LuaCallStatic : MonoBehaviour
{
void Start()
{
xLuaEnv.Instance.DoString("require('C2L/LuaCallStatic')");
}
private void OnDestroy()
{
xLuaEnv.Instance.Free();
}
}
}
LuaCallStatic.lua
--Lua调用静态类
--规则“cs.命名空间.类名.成员变量"
print(CS.HX.Teststatic.ID)
--给静态属性赋值
CS.HX.TestStatic.Name="admin"
print(CS.HX.Teststatic.Name)
--静态成员方法调用
--规则"CS.命名空间.类名.方法名()"
print(CS.HX.Teststatic.Output())
--使用默认值
CS.HX.Teststatic.Default()
--使用Lua传递的值
CS.HX.Teststatic.Default("def")
_______________________________________________________________________________________________
public class NPC
{
public string Name;
public int HP
{
get;
set;
}
public NPC()
{
}
public NPC(string name)
{
Name = name;
}
}
public class LuaCallObject : MonoBehaviour
{
void Start()
{
xLuaEnv.Instance.DoString("require('C2L/LuaCallObject')");
}
private void OnDestroy()
{
xLuaEnv.Instance.Free();
}
}
LuaCallObject.lua
--在当前表中调用表里—变量
--Lua实例化类
--C#NPC obj=new NPC()
--通过调用构造函数创建对象
local obj=CS.NPC()
obj.HP=100
print(obj.HP)
local obj1=CS.NPC("admin")
print(obj1.Name)
--表方法希望调用表成员变量(表:函数())
--为什么是冒号,对象引用成员变量时,会隐性调用this,等同于Lua中的self
--Lua实例化GameObject
CS.UnityEngine.GameObject("LuaCreateGo")
local go=CS.UnityEngine.GameObject("LuaCreateGo")
go:AddComponent(typeof(CS.UnityEngine.BoxCollider))
public struct TestStruct
{
public string Name;
public string Output()
{
return Name;
}
}
public class LuaCallStruct : MonoBehaviour
{
void Start()
{
xLuaEnv.Instance.DoString("require('C2L/LuaCallStruct')");
}
private void OnDestroy()
{
xLuaEnv.Instance.Free();
}
}
LuaCallStruct.lua
--结构体
--和对象调用保持一致
local obj=CS.TestStruct()
obj.Name="admin"
print(obj.Name)
print(obj:Output())
using UnityEngine;
public enum TestEnum
{
LOL=0,
Data2
}
public class LuaCallEnum : MonoBehaviour
{
void Start()
{
xLuaEnv.Instance.DoString("require('C2L/LuaCallEnum')");
}
private void OnDestroy()
{
xLuaEnv.Instance.Free();
}
}
LuaCallEnum.lua
--C# TestEnum.LoL
--CS.命名空间.枚举名.枚举值
--枚举获得是userdata自定义数据类型,获得其他语言数据类型时,就是userdata
print(type(CS.TestEnum.LOL))
print(CS.TestEnum.Data2)
--转换获得枚举值
print(CS.TestEnum._CastFrom(0))
print(CS.TestEnum._CastFrom("Data2"))
public class TestOverload
{
public static void Test(int id)
{
Debug.Log("数字类型:" + id);
}
public static void Test(string name)
{
Debug.Log("字符串类型:" + name);
}
public static void Test(int id,string name)
{
Debug.Log("两个数值:"+id+","+name);
}
}
public class LuaCallOverload : MonoBehaviour
{
void Start()
{
xLuaEnv.Instance.DoString("require('C2L/LuaCallOverload')");
}
private void OnDestroy()
{
xLuaEnv.Instance.Free();
}
}
LuaCallOverload.lua
--重载函数
--数字重载函数
CS.TestOverload.test(99)
CS.TestOverload.test("admin")
--不同参数的重载函数
CS.TestOverload.test(100,"root")
public class Father
{
public string Name = "father";
public void Talk()
{
Debug.Log("这是父类中的方法");
}
public virtual void Override()
{
Debug.Log("这是父类中的虚方法");
}
}
public class Child:Father
{
public override void Override()
{
Debug.Log("这是子类中重写方法");
}
}
public class LuaCallBase : MonoBehaviour
{
void Start()
{
xLuaEnv.Instance.DoString("require('C2L/LuaCallBase')");
}
private void OnDestroy()
{
xLuaEnv.Instance.Free();
}
}
LuaCallBase.lua
--调用Father
local father=CS.Father()
print(father.Name)
father:Override()
--调用Child
local child=CS.Child()
print(child.Name)
child:Talk()
child:Override()
public class TestExtend
{
public void Output()
{
Debug.Log("类本身带的方法");
}
}
//类扩展,需要给扩展方法编写的静态类添加LuaCallCSharp,
//否则Lua无法调用到
[LuaCallCSharp]
public static class MyExtend
{
public static void Show(this TestExtend obj)
{
Debug.Log("类扩展实现的方法");
}
}
public class LuaCallExtend : MonoBehaviour
{
void Start()
{
xLuaEnv.Instance.DoString("require('C2L/LuaCallExtend')");
}
private void OnDestroy()
{
xLuaEnv.Instance.Free();
}
}
LuaCallExtend.lua
local obj=CS.TestExtend()
obj:Output()
obj:SHow()
using UnityEngine;
public delegate void DelegateLua();
public class TestDelegate
{
public static DelegateLua Static;
public DelegateLua Dynamic;
public static void StaticFunc()
{
Debug.Log("C#静态成员函数");
}
}
public class LuaCallDelegate : MonoBehaviour
{
void Start()
{
xLuaEnv.Instance.DoString("require('C2L/LuaCallDelegate')");
}
private void OnDestroy()
{
xLuaEnv.Instance.Free();
}
}
LuaCallDelegate.lua
--委托
--C#给委托赋值
--TestDelegate.Static=TestDelegate.StaticFunc
--TestDelegate.Static+=TestDelegate.StaticFunc
--TestDelegate.Static-=TestDelegate.StaticFunc
CS.TestDelegate.Static=CS.TestDelegate.StaticFunc
CS.TestDelegate.Static()
--Lua中如果添加了函数到静态委托变量中后,委托不再使用后,记得释放
CS.TestDelegate.Static=nil
local func=function ()
print(":这是Lua函数")
end
--覆盖添加委托
CS.TestDelegate.Static=func
CS.TestDelegate.Static=CS.TestDelegate.Static+func
CS.TestDelegate.Static=CS.TestDelegate.Static-func
--调用以前应确定委托有值
if(CS.TestDelegate.Static~=nil)
then
CS.TestDelegate.Static()
end
CS.TestDelegate.Static=nil
--根据委托判定赋值方法
if(CS.TestDelegate.Static==nil)
then
CS.TestDelegate.Static=func
else
CS.TestDelegate.Static=CS.TestDelegate.Static+func
end
local obj=CS.TestDelegate()
obj.Dynamic=func
obj.Dynamic()
obj.Dynamic=nil
public delegate void EventLua();
public class TestEvent
{
public static event EventLua Static;
public static void StaticFunc()
{
Debug.Log("这是静态函数");
}
public static void CallStatic()
{
if(Static!=null)
{
Static();
}
}
public event EventLua Dynamic;
public void CallDynamic()
{
if(Dynamic!=null)
{
Dynamic();
}
}
}
public class LuaCallEvnet : MonoBehaviour
{
void Start()
{
xLuaEnv.Instance.DoString("require('C2L/LuaCallEvnet')");
}
private void OnDestroy()
{
xLuaEnv.Instance.Free();
}
}
LuaCallEvnet.lua
--Lua添加事件
CS.TestEvent.Static("+",CS.TestEvent.StaticFunc)
CS.TestEvent.CallStatic()
CS.TestEvent.Static("-",CS.TestEvent.StaticFunc)
--添加动态成员变量
local func=function ()
print("来自于Lua的回调函数")
end
local obj=CS.TestEvent()
obj:Dynamic("+",func)
obj:CallDynamic()
obj:Dynamic("-",func)
public class LuaCallGenericType : MonoBehaviour
{
void Start()
{
xLuaEnv.Instance.DoString("require('C2L/LuaCallGenericType')");
}
private void OnDestroy()
{
xLuaEnv.Instance.Free();
}
}
public class TestGenercType
{
public void Output<T>(T data)
{
Debug.Log("泛型方法:" + data.ToString());
}
public void Output(float data)
{
Output<float>(data);
}
public void Output(string data)
{
Output<string>(data);
}
}
LuaCallGenericType.lua
local obj=CS.TestGenericType()
obj:Output(99)
obj.Output("admin")
local go=CS.UnityEngine.GameObject("LuaCreate")
go:AddComponent(typeof(CS.UnityEngine.BoxCollider))
public class LuaCallOutRef : MonoBehaviour
{
void Start()
{
xLuaEnv.Instance.DoString("require('C2L/LuaCallOutRef')");
}
private void OnDestroy()
{
xLuaEnv.Instance.Free();
}
}
public class TestOutRef
{
public static string Func1()
{
return "Func1";
}
public static string Func2(string str1,out string str2)
{
str2 = "Func2 out";
return "Func2";
}
public static string Func3(string str1, ref string str2)
{
str2 = "Func3 Ref";
return "Func3";
}
public static string Func4(ref string str1,string str2)
{
str1 = "Func4 Ref";
return "Func4";
}
}
LuaCallOutRef.lua
local r1=CS.TestOutRef.Func1()
print(r1)
--C# out返回的变量,会赋值给Lua的第二个接受返回值变量
local out2
local r2,out1=CS.TestOutRef.Func2("admin",out2)
print(r2,out1,out2)
--C# ref返回的变量,会赋值给Lua的第二个接受返回值变量
local ref2
local r3,ref1=CS.TestOutRef.Func3("root",ref2)
print(r3,ref1,ref2)
--即使out ref 作为第一个参数,其结果依然会以Lua的多个返回值进行返回
local ref4
local r4,ref3=CS.TestOutRef.Func4(ref4,"test")
print(r4,ref3,ref4)