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语言一的主要内容,如果未能解决你的问题,请参考以下文章

xLua热更新实现热更新

Unity2020 几种常用热更新方案的优劣及XLua实战

XLua----热更新

Xlua热更新

游戏热更新实战案例(基于xLua)

[Unity XLua]热更新XLua入门-俄罗斯方块实例篇