灰色网站欣赏,seo查询网址,网站怎么拿百度收入,沂源网页定制目录 一、xLua概述1.1xLua简介1.2xLua安装 二、Lua文件加载2.1执行字符串2.2加载Lua文件2.3自定义loader 三、xLua文件配置3.1打标签3.2静态列表3.3动态列表 四、Lua与C#交互4.1 C#访问Lua4.1.1 获取一个全局基本数据类型4.1.2 访问一个全局的table4.1.3 访问一个全局的functio… 目录 一、xLua概述1.1xLua简介1.2xLua安装 二、Lua文件加载2.1执行字符串2.2加载Lua文件2.3自定义loader 三、xLua文件配置3.1打标签3.2静态列表3.3动态列表 四、Lua与C#交互4.1 C#访问Lua4.1.1 获取一个全局基本数据类型4.1.2 访问一个全局的table4.1.3 访问一个全局的function 4.2 Lua调用C#4.2.1 new C#对象4.2.2 访问C#静态属性方法4.2.3 访问C#成员属性方法4.2.4 访问父类属性方法4.2.5 参数的输入输出属性outref4.2.6 重载方法4.2.7 操作符4.2.8 参数带默认值的方法4.2.9 可变参数方法4.2.10 使用Extension methods4.2.11 泛化模版方法4.2.12 枚举类型4.2.13 delegate使用调用-4.2.14 event4.2.15 C#复杂类型和table的自动转换4.2.16 “强”转4.2.17 C#与Lua交互原理 五、xLua热更新5.1 使用方式5.2 约束5.3 API5.4 Hotfix Flag5.5 使用建议 六、热更新大致流程 一、xLua概述
1.1xLua简介
xLua是由腾讯维护的一个开源项目xLua为Unity、 .Net、 Mono等C#环境增加Lua脚本编程的能力借助xLua这些Lua代码可以方便的和C#相互调用。自2016年初推广以来已经应用于十多款腾讯自研游戏因其良好性能、易用性、扩展性而广受好评。现在腾讯已经将xLua开源到GitHub。 git下载地址https://github.com/Tencent/xLua xLua教程地址https://github.com/Tencent/xLua/blob/master/Assets/XLua/Doc/XLua%E6%95%99%E7%A8%8B.md
xLua在功能、性能、易用性都有不少突破这几方面分别最具代表性的是
可以运行时把C#实现方法操作符属性事件等等替换成lua实现出色的GC优化自定义struct枚举在Lua和C#间传递无C# gc alloc编辑器下无需生成代码开发更轻量
除此之外xLua另一特色功能就是代码热补丁。非常适合前期没有规划使用Lua进行逻辑开发后期又需要在iOS这种平台获得代码热更新能力的项目。
1.2xLua安装 首先下载最新版xLua ,然后解压到你想解压到的位置 xLua-master工程打开后你将会看到一个Assets目录点击到Assets目录会看到以下几个文件 将 xLua-master 工程中Assets目录下的文件复制到你的Unity工程的Assets目录下即可 加载完后菜单栏有 xlua 菜单表示导入成功 在菜单栏中 xlua 菜单添加 Hotfix Inject In Editor 选项 菜单栏中 xlua 菜单会显示 Hotfix Inject In Editor 选项 如果希望安装到其它目录可参看https://github.com/Tencent/xLua/blob/master/Assets/XLua/Doc/faq.md xLua API可参见链接https://github.com/Tencent/xLua/blob/master/Assets/XLua/Doc/XLua_API.md
示例代码
using UnityEngine;
//引入 Xlua 命名空间
using XLua;public class HotFixScript : MonoBehaviour
{//lua 环境变量private LuaEnv luaEnv;private void Start(){//创建lua运行环境luaEnv new LuaEnv();luaEnv.DoString(print(Hellow World 1));}private void OnDestroy(){//释放lua环境luaEnv.Dispose();}
}编写完脚本挂载到游戏物体上先按 Generate Code 选项生成代码再按 Hotfix Inject In Editor 选项将补丁注入编辑器运行结果
二、Lua文件加载
2.1执行字符串
最基本是直接用LuaEnv.DoString执行一个字符串当然字符串得符合Lua语法
比如luaenv.DoString(“print(‘hello world’)”)
完整代码见XLua\Tutorial\LoadLuaScript\ByString目录。
但这种方式并不建议更建议下面介绍这种方法加载Lua文件。
2.2加载Lua文件
用lua的require函数即可比如DoString(“require ‘byfile’)
require实际上是调一个个的loader去加载有一个成功就不再往下尝试全失败则报文件找不到。
完整代码见XLua\Tutorial\LoadLuaScript\ByFile目录。
2.3自定义loader
在xLua加自定义loader是很简单的只涉及到一个接口
public delegate byte[] CustomLoader(ref string filepath);
public void LuaEnv.AddLoader(CustomLoader loader)
通过AddLoader可以注册个回调该回调参数是字符串lua代码里头调用require时参数将会透传给回调回调中就可以根据这个参数去加载指定文件如果需要支持调试需要把filepath修改为真实路径传出。该回调返回值是一个byte数组如果为空表示该loader找不到否则则为lua文件的内容。
有了这个就简单了用IIPS的IFS没问题。写个loader调用IIPS的接口读文件内容即可。文件已经加密没问题自己写loader读取文件解密后返回即可。 示例代码
using System.Collections;
using System.Collections.Generic;
using System.IO;
using UnityEditor;
using UnityEngine;
using XLua;public class LuaStudy : MonoBehaviour
{private LuaEnv luaEnv;private void Start(){//创建lua运行环境luaEnv new LuaEnv();LuaLoaderFun();VisitLua_Table();VisitLua_Function();}private void OnDestroy(){//释放lua环境luaEnv.Dispose();}/// summary/// 加载及调用lua脚本/// /summaryvoid LuaLoaderFun(){//执行lua的输出语句luaEnv.DoString(print(Hellow World 1));//在lua中调用C#方法luaEnv.DoString(CS.UnityEngine.Debug.Log(Hellow World 2));//加载运行Resources文件中的lua脚本TextAsset ta Resources.LoadTextAsset(HellowWorld.lua);luaEnv.DoString(ta.text);//加载运行helloworld.lua.txt 默认从Resources文件中进行加载luaEnv.DoString(require HellowWorld);// 添加自定义的Loader方法//添加了一个自定义的Loader返回null并且DoString里面添加的是一个不存在的lua文件。它会返回错误。//luaEnv.AddLoader(MyLoader_1);//luaEnv.DoString(require xxx);// 添加自定义的Loader方法//添加了一个自定义的Loader返回lua语句的二进制并且DoString里面添加的是一个不存在的lua文件。它会执行自定义的Loader的输出。//luaEnv.AddLoader(MyLoader_2);//luaEnv.DoString(require xxx);//通过自定义Loader加载指定目录的Lua脚本luaEnv.AddLoader(MyLoader_3);luaEnv.DoString(require Lua_StreamingAssets);}/// summary/// 访问获取lua属性和表/// /summaryvoid VisitLua_Table() {//调用lua脚本才能访问属性luaEnv.DoString(require LuaTest);//获取lua脚本中的全局变量int num luaEnv.Global.Getint(num);//获取lua里面的全局变量numstring name luaEnv.Global.Getstring(name);//获取lua里面的全局变量nameDebug.Log(num: num name: name);//访问lua中的table映射到classPerson personTemp luaEnv.Global.GetPerson(person);Debug.Log(personTemp.name personTemp.age);//这种方式的访问修改age的值不会影响到lua里面的表的属性personTemp.name wang;personTemp.age 50;//修改后试一下Person personTemp2 luaEnv.Global.GetPerson(person);Debug.Log(personTemp2.name personTemp2.age);//访问lua中的table映射到interface//映射到interface修改p中的属性lua中的原table也会发生变化IPerson personTemp3 luaEnv.Global.GetIPerson(person);Debug.Log(personTemp2.name personTemp2.age);//通过LuaTable访问tableListobject list luaEnv.Global.GetListobject(person);foreach (object o in list){print(o);}//通过LuaTable类 比较慢LuaTable luaTable luaEnv.Global.GetLuaTable(person);print(luaTable.Getstring(name));print(luaTable.Getint(age));}/// summary/// 访问lua中的全局方法/// /summaryvoid VisitLua_Function(){//调用lua脚本才能访问属性luaEnv.DoString(require LuaTest);//使用委托来获取Lua中的全局函数Add add luaEnv.Global.GetAdd(add);add(2, 4);//通过LuaFunction访问Lua中的全局函数LuaFunction luaFunction luaEnv.Global.GetLuaFunction(add);luaFunction.Call(2, 4);}private byte[] MyLoader_1(ref string filePath){print(filePath);return null;}private byte[] MyLoader_2(ref string filePath) {string s print(123);return System.Text.Encoding.UTF8.GetBytes(s);}private byte[] MyLoader_3(ref string filePath) {string path Application.streamingAssetsPath / filePath .lua.txt;return System.Text.Encoding.UTF8.GetBytes(File.ReadAllText(path));}class Person{public string name;public int age;}//打标签[CSharpCallLua]public interface IPerson{string name { get; set; }int age { get; set; }}//打标签[CSharpCallLua]delegate void Add(int a, int b);}
完整示例见XLua\Tutorial\LoadLuaScript\Loader。
三、xLua文件配置
xLua所有的配置都支持三种方式打标签静态列表动态列表。 配置有两必须两建议
列表方式均必须是静态的字段/属性列表方式均必须放置一个静态类建议使用标签方式建议列表方式配置放Editor目录如果是Hotfix配置 - - 并且类位于Assembly-CSharp.dll之外的其他dll必须放Editor目录
3.1打标签
xLua用白名单来指明生成哪些代码而白名单通过attribute来配置比如你想从lua
调用c#的某个类希望生成适配代码你可以为这个类型打一个LuaCallCSharp标签
[LuaCallCSharp]
publicclassA
{}该方式方便但在il2cpp下会增加不少的代码量不建议使用。
常用标签如下所示 XLua.LuaCallCSharp为一个C#类型加了这个配置xLua会生成这个类型的适配代码否则将会尝试用性能较低的反射方式来访问提供给Lua调用。 XLua.CSharpCallLua如果希望Lua文件中的逻辑能够被C#调用可为Lua重的函数等打上该标签。 XLua.Hotfix用于标记需要进行热更新的文件。
除此之外xLua还提供了其他标签具体可参见链接https://github.com/Tencent/xLua/blob/master/Assets/XLua/Doc/configure.md
3.2静态列表
有时我们无法直接给一个类型打标签比如系统api没有源码的库或者实例化的泛化类型那么你可以在一个静态类里声明一个静态字段该字段的类型除BlackList和AdditionalProperties之外只要实现了IEnumerable就可以了这两个例外后面具体会说然后为这字段加上标签
[LuaCallCSharp]
public static ListType mymodule_lua_call_cs_list new ListType()
{typeof(GameObject),typeof(Dictionarystring, int),
};该字段需要放置一个静态类里头建议放置Editor目录。
3.3动态列表
声明一个静态属性打上相应的标签即可。
[Hotfix]
public static ListType by_property
{get{return (from type in Assembly.Load(Assembly-CSharp).GetTypes()where type.Namespace XXXXselect type).ToList();}
}Getter是代码你可以实现很多效果比如按名称空间配置按程序集配置等等。
该属性需要放置一个静态类里头建议放置Editor目录。
四、Lua与C#交互
4.1 C#访问Lua
4.1.1 获取一个全局基本数据类型
访问LuaEnv.Global就可以了上面有个模版Get方法可指定返回的类型。
luaenv.Global.Getint(a)
luaenv.Global.Getstring(b)
luaenv.Global.Getbool(c)4.1.2 访问一个全局的table
1、映射到普通class或struct
定义一个class有对应于table的字段的public属性而且有无参数构造函数即可比如对于{f1 100, f2 100}可以定义一个包含public int f1;public int f2;的class。这种方式下xLua会帮你new一个实例并把对应的字段赋值过去。table的属性可以多于或者少于class的属性。可以嵌套其它复杂类型。
要注意的是这个过程是值拷贝如果class比较复杂代价会比较大。而且修改class的字段值不会同步到table反过来也不会。这个功能可以通过把类型加到GCOptimize生成降低开销。
2、映射到一个interface
这种方式依赖于生成代码如果没生成代码会抛InvalidCastException异常代码生成器会生成这个interface的实例如果get一个属性生成代码会get对应的table字段如果set属性也会设置对应的字段。甚至可以通过interface的方法访问lua的函数。
3、更轻量级的by value方式映射到DictionaryList
不想定义class或者interface的话可以考虑用这个前提table下key和value的类型都是一致的。
4、另外一种by ref方式映射到LuaTable类
这种方式好处是不需要生成代码但也有一些问题比如慢比方式2要慢一个数量级比如没有类型检查。
4.1.3 访问一个全局的function
1、映射到delegate
这种是建议的方式性能好很多而且类型安全。缺点是要生成代码如果没生成代码会抛InvalidCastException异常。
delegate要怎样声明呢
对于function的每个参数就声明一个输入类型的参数。
多返回值要怎么处理从左往右映射到c#的输出参数输出参数包括返回值out参数ref参数。
参数、返回值类型支持哪些呢都支持各种复杂类型outref修饰的甚至可以返回另外一个delegate。
delegate的使用就更简单了直接像个函数那样用就可以了。
2、映射到LuaFunction
这种方式的优缺点刚好和第一种相反。
使用也简单LuaFunction上有个变参的Call函数可以传任意类型任意个数的参数返回值是object的数组对应于lua的多返回值。
4.2 Lua调用C#
4.2.1 new C#对象
在C#这样new一个对象
var newGameObj new UnityEngine.GameObject();对应到Lua是这样
local newGameObj CS.UnityEngine.GameObject()基本类似除了
lua里头没有new关键字
所有C#相关的都放到CS下包括构造函数静态成员属性、方法
如果有多个构造函数呢放心xlua支持重载比如你要调用GameObject的带一个string参数的构造函数这么写
local newGameObj2 CS.UnityEngine.GameObject(helloworld)4.2.2 访问C#静态属性方法
读静态属性
CS.UnityEngine.Time.deltaTime写静态属性
CS.UnityEngine.Time.timeScale 0.5调用静态方法
CS.UnityEngine.GameObject.Find(helloworld)小技巧如果需要经常访问的类可以先用局部变量引用后访问除了减少敲代码的时间还能提高性能
local GameObject CS.UnityEngine.GameObject
GameObject.Find(helloworld)4.2.3 访问C#成员属性方法
读成员属性
testobj.DMF写成员属性
testobj.DMF 1024调用成员方法
注意调用成员方法第一个参数需要传该对象建议用冒号语法糖如下
testobj:DMFunc()4.2.4 访问父类属性方法
xlua支持通过派生类访问基类的静态属性静态方法通过派生类实例访问基
类的成员属性成员方法。
4.2.5 参数的输入输出属性outref
Lua调用测的参数处理规则C#的普通参数算一个输入形参ref修饰的算一个输入形参
out修饰的不算然后从左往右对应lua 调用测的实参列表
Lua调用测的返回值处理规则C#函数的返回值如果有的话算一个返回值out算一个返回值ref算一个返回值然后从左往右对应lua的多返回值。
4.2.6 重载方法
直接通过不同的参数类型进行重载函数的访问例如
testobj:TestFunc(100)
testobj:TestFunc(‘hello’)
将分别访问整数参数的TestFunc和字符串参数的TestFunc。
注意xlua只一定程度上支持重载函数的调用因为lua的类型远远不如C#丰富存在一
对多的情况比如C#的intfloatdouble都对应于lua的number上面的例子中
TestFunc如果有这些重载参数第一行将无法区分开来只能调用到其中一个生成代码
中排前面的那个。
4.2.7 操作符
支持的操作符有-*/一元 - %[]
4.2.8 参数带默认值的方法
和C#调用有默认值参数的函数一样如果所给的实参少于形参则会用默认值补上。
4.2.9 可变参数方法
对于C#的如下方法
void VariableParamsFunc(int a, params string[] strs)可以在lua里头这样调用
testobj:VariableParamsFunc(5, hello, john)4.2.10 使用Extension methods
在C#里定义了lua里就能直接使用。
4.2.11 泛化模版方法
不直接支持可以通过Extension methods功能进行封装后调用。
4.2.12 枚举类型
枚举值就像枚举类型下的静态属性一样。
testobj:EnumTestFunc(CS.Tutorial.TestEnum.E1)
上面的EnumTestFunc函数参数是Tutorial.TestEnum类型的
另外如果枚举类加入到生成代码的话枚举类将支持__CastFrom方法可以实现从一个整数或者字符串到枚举值的转换例如
CS.Tutorial.TestEnum.__CastFrom(1)
CS.Tutorial.TestEnum.__CastFrom(‘E1’)
4.2.13 delegate使用调用-
C#的delegate调用和调用普通lua函数一样
操作符对应C#的操作符把两个调用串成一个调用链右操作数可以是同类型的
C#delegate或者是lua函数。
-操作符和相反把一个delegate从调用链中移除。
Psdelegate属性可以用一个luafunction来赋值。
4.2.14 event
比如testobj里头有个事件定义是这样public event Action TestEvent;
增加事件回调
testobj:TestEvent(‘’, lua_event_callback)
移除事件回调
testobj:TestEvent(‘-’, lua_event_callback)
4.2.15 C#复杂类型和table的自动转换
对于一个有无参构造函数的C#复杂类型在lua侧可以直接用一个table来代替该table对应复杂类型的public字段有相应字段即可支持函数参数传递属性赋值等例如
C#下B结构体class也支持定义如下
public struct A{ public int a;}
public struct B{ public A b; public double c;}
某个类有成员函数如下void Foo(B b)
在lua可以这么调用
obj:Foo({b {a 100}, c 200})
获取类型相当于C#的typeof
比如要获取UnityEngine.ParticleSystem类的Type信息可以这样
typeof(CS.UnityEngine.ParticleSystem)。
4.2.16 “强”转
lua没类型所以不会有强类型语言的“强转”但有个有点像的东西告诉xlua要用指定的生成代码去调用一个对象这在什么情况下能用到呢有的时候第三方库对外暴露的是一个interface或者抽象类实现类是隐藏的这样我们无法对实现类进行代码生成。该实现类将会被xlua识别为未生成代码而用反射来访问如果这个调用是很频繁的话还是很影响性能的这时我们就可以把这个interface或者抽象类加到生成代码然后指定用该生成代码来访问
cast(calc, typeof(CS.Tutorial.Calc))
上面就是指定用CS.Tutorial.Calc的生成代码来访问calc对象。
4.2.17 C#与Lua交互原理
1、C#调用LuaC#先调用Lua的dll文件C语言写的库dll文件执行Lua代码
2、Lua调用C#
1、Wrap方式非反射机制需要为源文件生成相应的wrap文件当启动
Lua虚拟机时Wrap文件将会被自动注册到Lua虚拟机中之后Lua文件
将能够识别和调用C#源文件。
总结Lua调用Wrap文件 Wrap文件调用C#源文件
2、反射机制
五、xLua热更新
5.1 使用方式 添加HOTFIX_ENABLE宏打开该特性在Unity3D的File-Build Setting-Scripting Define Symbols下添加。编辑器、各手机平台这个宏要分别设置如果是自动化打包要注意在代码里头用API设置的宏是不生效的需要在编辑器设置。建议平时开发业务代码不打开HOTFIX_ENABLE只在build手机版本或者要在编译器下开发补丁时打开HOTFIX_ENABLE 执行XLua/Generate Code菜单。 注入构建手机包这个步骤会在构建时自动进行编辑器下开发补丁需要手动执行XLua/Hotfix Inject In Editor菜单。注入成功会打印“hotfix inject finish!”或者“had injected!”。
5.2 约束
不支持静态构造函数目前只支持Assets下代码的热补丁不支持引擎c#系统库的热补丁。
5.3 API
xlua.hotfix(class, [method_name], fix) 描述 注入lua补丁
class C#类两种表示方法CS.Namespace.TypeName或者字符串方式Namespace.TypeName字符串格式和C#的Type.GetType要求一致如果是内嵌类型Nested Type是非Public类型的话只能用字符串方式表示Namespace.TypeNameNestedTypeNamemethod_name 方法名可选fix 如果传了method_namefix将会是一个function否则通过table提供一组函数。table的组织按key是method_namevalue是function的方式。
base(csobj) 描述 子类override函数通过base调用父类实现。 csobj 对象 返回值 新对象可以通过该对象base上的方法
util.hotfix_ex(class, method_name, fix) 描述 xlua.hotfix的增强版本可以在fix函数里头执行原来的函数缺点是fix的执行会略慢。
method_name 方法名fix 用来替换C#方法的lua function。
5.4 Hotfix Flag
Hotfix标签可以设置一些标志位对生成代码及插桩定制化。
Stateless、Stateful 遗留设置Stateful方式在新版本已经删除因为这种方式可以用xlua.util.hotfix_state接口达到类似的效果。
ValueTypeBoxing 值类型的适配delegate会收敛到object好处是代码量更少不好的是值类型会产生boxing及gc适用于对text段敏感的业务。
IgnoreProperty 不对属性注入及生成适配代码一般而言大多数属性的实现都很简单出错几率比较小建议不注入。
IgnoreNotPublic 不对非public的方法注入及生成适配代码。除了像MonoBehaviour那种会被反射调用的私有方法必须得注入其它仅被本类调用的非public方法可以不注入只不过修复时会工作量稍大所有引用到这个函数的public方法都要重写。
Inline 不生成适配delegate直接在函数体注入处理代码。
IntKey 不生成静态字段而是把所有注入点放到一个数组集中管理。
5.5 使用建议
对所有较大可能变动的类型加上Hotfix标识 建议用反射找出所有函数参数、字段、属性、事件涉及的delegate类型标注CSharpCallLua 业务代码、引擎API、系统API需要在Lua补丁里头高性能访问的类型加上 LuaCallCSharp
引擎API、系统API可能被代码剪裁调C#无引用的地方都会被剪裁如果觉得可能会 新增C#代码之外的API调用这些API所在的类型要么加LuaCallCSharp要么ReflectionUse
六、热更新大致流程
游戏一开始启动时检查远程服务器上是否有新的myHotfix.lua文件例如md5比对自己加解密有的话下载下来放在指定目录没有的话读取本地已有的myHotfix.lua文件若文件不存在则说明不需要热修复一般这种情况是在项目刚发布的早期没有进行打补丁项目发布版本前需要在CustomGenConfig.cs中 加入需要添加[Hotfix]标签的类型想要更灵活使用的话可以自己写个配置表读取配置中的类型自动添加***只有加入[Hotfix]标签的类型才可以调用xlua.hotfix()。