014-热更新之xlua语言一
Posted jake-caiee
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了014-热更新之xlua语言一相关的知识,希望对你有一定的参考价值。
终于我们把lua语言的部分结束了,今天我们正式开始xlua的学习。首先我们先看看热更新是什么?热:就是刚出炉。简单来说就是当游戏某个功能出现bug,或者修改了某个功能的时候,或者增加了某个功能的时候,我们不需要重新下载安装安装包,就可以更新游戏内容。热更新的好处“不用浪费流量重新下载,不用通过商店审核更加快速,不用重新安装玩家可以更快体验到更新的内容。目前比较首欢迎的热更新方案:ulua tolua xlua。好了,因为我们在前面已经学习过lua语言了,所以我们的前言就是这些了,就不说太多的废话了。
我们首先创建一个unity工程,并将xlua的asset导入其中。说明一下,我们是根据作者提供的教程来学习的。
一、lua文件加载
1.执行字符串 最基本是直接用LuaEnv.DoStrig执行一个字符串。我们在空的物体上添加一个c#,并写如下的代码:
private LuaEnv luaenv; void Start () { luaenv = new LuaEnv(); //方式一 luaenv.DoString("print(‘Hello World!‘)"); //方式二 luaenv.DoString("CS.UnityEngine.Debug.Log(‘Hello World!‘)"); luaenv.Dispose(); }
就会有如下图的效果:
其实这个算法也是没简单的,只要将lua虚拟机创建好就行了,但是作者他不推荐这个方式。
2.加载lua文件
用lua的require函数就行了。这个require我们还是比较熟悉的,因为我们在lua语言中的模块一节中,我们已经学过了,形式是这样的:require(‘文件名‘),所以我们在xlua中也是可以这样写的。require实际上是调用一个个的loader去加载,有一个成功就不再往下尝试,全失败则包文件找不到。目前xlua除了原生的loader外,还添加了从Resource加载的loader,需要注意的是因为Resource只支持有限的后缀,放Resources下的lua文件的加上txt后缀。我们需要在Resources文件下创建一个txt文件,名字叫helloworld.lua.txt,在其中写print(‘HelloWorld‘),就能完成与上面一样的操作了。代码如下:
void Start () { TextAsset ta = Resources.Load<TextAsset>("helloworld.lua"); LuaEnv luaenv = new LuaEnv(); luaenv.DoString(ta.ToString()); luaenv.Dispose(); }
3.自定义loader
在xlua加自定义loader是很简单的,只涉及到一个接口:public delegate byte[] CustomLoader(ref string filepath);public void LuaEnv.AddLoader(CustomLoader loader)。通过AddLoader可以注册多个回调,该回调参数是字符串,lua代码里头调用require时,参数将会透传给回调,回调中就可以根据这个参数支持调试,需要把filepath修改为真实路径传出。该回调返回值是一个byte数组,如果为空表示该loader找不到,否则则为lua文件的内容。
我们首先在unity创建一个StreamingAssets文件,在其中创建一个txt文件,名叫text007.lua.txt。print(‘I am 007‘),接着我们在空游戏物体上的c#脚本上添加如下的脚本。
using UnityEngine; using XLua; using System.IO; public class DefineLoader : MonoBehaviour { // Use this for initialization private LuaEnv luaenv; void Start () { luaenv = new LuaEnv(); luaenv.AddLoader(MyLoader); luaenv.DoString(" require ‘text007‘"); luaenv.Dispose(); } private byte[] MyLoader(ref string filPath) { string absPath = Application.streamingAssetsPath + "/" + filPath + ".lua.txt"; return System.Text.Encoding.UTF8.GetBytes(File.ReadAllText(absPath)); } }
我们在这个myloader中得到我们要创建的文件的目录,可以通过以上的方法来得到。通过addloader这个方法加入回到函数,上面已经说过addloader可以注册回调,回调是字符串,该回调是一个byte数组。因为是byte数组,所以我们在传递之前就要将字符串改成字节数组才行,这样addloader才能编译。
二、C#访问Lua
1.访问一个全局的基本数据类型
这里指的是C#主动发起对Lua数据结构的访问。我们在将虚拟状态机创建好,可以通过luaenv.Global.Get<T>()这个方法来得到,所以我们可以写下面的代码,完成在C#中访问lua中的变量。在这里创建lua.txt就不再多说了,步骤是一样的。
void Start () { LuaEnv luaenv = new LuaEnv(); //打开指定的文件夹 luaenv.DoString("require ‘CSharpCallLua‘"); int a = luaenv.Global.Get<int>("a"); string str = luaenv.Global.Get<string>("str"); bool isFly = luaenv.Global.Get<bool>("isFly"); print(a + " " + str + " " + isFly); luaenv.Dispose(); }
2.访问一个全局的table
在c#中一共有4种方式访问lua中的table,在这里就不说哪几个,我们一个一个来说明。
2.1映射到普通的class或struct
定义一个class,有对应与table的字段的public属性,而且有无参数构造函数即可,比如定义一个包含public int f2;的class。这种方式下xlua会帮助你new一个实例,并把对应的字段赋值过去。table的属性可以多于或者少于class的属性。可以镶嵌其他复杂类型。要注意的是,这个过程比较复杂代价会比较大。数值也不会同步。
// Use this for initialization void Start () { LuaEnv luaenv = new LuaEnv(); //打开指定的文件夹 luaenv.DoString("require ‘CSharpCallLua‘"); //int a = luaenv.Global.Get<int>("a"); //string str = luaenv.Global.Get<string>("str"); //bool isFly = luaenv.Global.Get<bool>("isFly"); //print(a + " " + str + " " + isFly); Person p = luaenv.Global.Get<Person>("person"); print(p.name + " " + p.age); p.name = "dfdfdf"; luaenv.DoString("print(person.name)"); luaenv.Dispose(); } class Person { public string name;public int age; }
得到的效果如下:
我们从图中可以看见,我们在c#中改变person.name的值,输出时并不会发生改变,就像前面所说的一样,这个方式就是将lua中的值赋值下来,并不会改变lua中的值。这个方式我么首先应该在c#中创建一个与之对应的类Person,在这个类中有对应的值,然后通过方法,将lua中的变量映射到Person类中,这样就行了。关于映射,我们在高一学习函数的时候就学过了,函数是一对一,映射就是多对多的关系。其实我们也是没有必要这么在意映射的概念,可以理解成将lua中的变量给Person中对应的变量赋值。
2.2映射到一个interface
这种方式依赖与生成代码,代码生成器会生成这个interface的实例,如果get一个属性,生成代码会get对应的table字段,如果set属性也会设置对应的字段。甚至可以通过interface的方法访问lua的函数。
void Start () { LuaEnv luaenv = new LuaEnv(); //打开指定的文件夹 luaenv.DoString("require ‘CSharpCallLua‘"); //int a = luaenv.Global.Get<int>("a"); //string str = luaenv.Global.Get<string>("str"); //bool isFly = luaenv.Global.Get<bool>("isFly"); //print(a + " " + str + " " + isFly); //第一种方式访问 //Person p = luaenv.Global.Get<Person>("person"); //print(p.name + " " + p.age); IPerson p = luaenv.Global.Get<IPerson>("person"); print(p.name + " " + p.age); p.name = "dfdfdf"; luaenv.DoString("print(person.name)"); p.eat(3,4); luaenv.Dispose(); } //class Person { public string name;public int age; } [CSharpCallLua] interface IPerson { string name { get; set; } int age { get; set; }void eat(int a,int b); }
效果图如下:
从上面我们可以看见,我们通过这种方式改变lua中的值是可以改变成功的,因为这种方式实际上是一种引用的方式,在我们创建接口的时候一定要声明接口的类型,如上面的代码一样,否则就会报错。下面是lua的代码:
看到上面两种函数的区别的吗?没有:的话,就会需要加一个变量,其实这个变量你可以把它理解成从c#中的this,就是构造自己,在调用的时候。如果有:的话,就不用写这个变量了,因为lua会帮你构造的。好了,本节的内容就到这里了,拜拜。
以上是关于014-热更新之xlua语言一的主要内容,如果未能解决你的问题,请参考以下文章