关于如何在ToLua中使用C#中的类

2021-06-08 21:31发布

对于游戏开发人员来说,游戏热更新再熟悉不过了,只要是网络游戏,要使游戏长时间生存下去,都要对游戏进行更新,最常用到的热更新方式就是使用Lua脚本。Lua语言不用经过编译,在代码中读取Lua代码后即可运行,因此常用到的一种热更新方案就是:把要更新的代码逻辑写到一个Lua脚本里面,然后上传到服务器,客户端开始游戏后,先到服务器上比对与客户端已经下载的Lua脚本版本是否一样,如果不一样,就从服务器上下载Lua脚本到客户端本地文件夹中,然后在客户端读取下载后的Lua脚本运行里面的代码即可。

常用的Lua有ToLua、ULua和XLua,其中以ToLua用的居多。当我们在Lua脚本中写热更新的代码逻辑时,会调用引擎中开发人员创建的C#脚本,本文就简单的介绍一下如何ToLua与C#之间的交互。

大家可以到https://github.com/topics/ToLua下载ToLua的框架。把下载好的ToLua框架放到Unity引擎的Assets目录下,在菜单栏会生成新的菜单,如下图:

Assets目录如下图所示(版本不一样,目录结构稍有不同,但主要文件夹结构不会改变):

     

在Editor/Custom文件夹下有CustomSettings.cs脚本,本脚本的主要作用是注册新的类,以方便在Lua脚本中调用。先在引擎中创建一个C#脚本CSVParse.cs,该脚本的功能是解析csv类型的数据,CSVParse.cs脚本中里面包含了两个静态方法,代码如下:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using System;
using System.IO;
public class CSVParse
{
    public static  List<QuestionModel> CSVParse_QuestionModel(string csvName)
    {
        List<string[]> dataArray = new List<string[]>();
        List<QuestionModel> questionModels = new List<QuestionModel>();
        StreamReader sr = null;
        try
        {
            string file_url = Application.dataPath + "/Resources/Configs/"  + csvName;
            sr = File.OpenText(file_url);
        }
        catch
        {
            Debug.Log("File is not find");
        }
        string line;
        while ((line = sr.ReadLine()) != null)
        {
            dataArray.Add(line.Split(','));
        }
        
        for (int i = 1;i < dataArray.Count;i++)
        {
            //ID
            string _id = dataArray[i][0];
            
            //问题类型
            QuestionType _questionType = (QuestionType)Enum.Parse(typeof(QuestionType), dataArray[i][1], true);
            //背景图片
            string _bgName = dataArray[i][2];
            //声音片断
            string _soundName = dataArray[i][3];
            //标题文字
            string _title = dataArray[i][4];
            //所有选项的图片名称
            List<string> _imageNames = new List<string>();
            string str = dataArray[i][5];
 
            string[] names = str.Split('|');
            for (int j = 0;j < names.Length;j++)
            {
                _imageNames.Add(names[j]);
            }
            //回答类型
            AnswerType _answerType = (AnswerType)Enum.Parse(typeof(AnswerType), dataArray[i][6], true);
            //问题选项
            List<string> _answerNames = new List<string>();
            str = dataArray[i][7];
            string[] strs = str.Split('|');
            for (int j = 0;j < strs.Length;j++)
            {
                _answerNames.Add(strs[j]);
            }
            //结果
            string _result = dataArray[i][8];
            QuestionModel model = new QuestionModel(_id, _questionType, _bgName, _soundName, _title, _imageNames, _answerType, _answerNames, _result);
            questionModels.Add(model);
        }
 
        sr.Close();
        sr.Dispose();
        return questionModels;
    }
    
    public static Scene_Model CSVParse_SceneModel(string csvName)
    {
        List<string[]> dataArray = new List<string[]>();
        StreamReader sr = null;
        try
        {
            string file_url = Application.dataPath + "/Resources/Configs" + "//" + csvName;
            sr = File.OpenText(file_url);
 
        }
        catch
        {
            Debug.Log("File is not find");
        }
        string line;
        while ((line = sr.ReadLine()) != null)
        {
            dataArray.Add(line.Split(','));
        }
        //视频名字
        string _videoName = null;
        if (dataArray[0][0].Equals("V"))
        {
            _videoName= dataArray[0][1];
        }
        //问题组
        List<QuestionGroup> _questionGroups = new List<QuestionGroup>();
        //Flag组
        List<FlagGroup> _flagGroups = new List<FlagGroup>();
        for (int i = 1;i < dataArray.Count;i++)
        {
            if (dataArray[i][0].Equals("Q"))
            {
                string  time = dataArray[i][1];
                List<string> _questionIDs = new List<string>();
                for (int j = 2;j < dataArray[i].Length;j++)
                {
                    _questionIDs.Add(dataArray[i][j]);
                }
                QuestionGroup _questionGroup = new QuestionGroup(time, _questionIDs);
 
                _questionGroups.Add(_questionGroup);
            }else if (dataArray[i][0].Equals("F"))
            {
                string _time = dataArray[i][1];
                FlagType _flagType = (FlagType)Enum.Parse(typeof(FlagType), dataArray[i][2], true);
                FlagGroup _flagGroup = new FlagGroup(_time, _flagType);
                _flagGroups.Add(_flagGroup);
            }
        }
        
        Scene_Model _sm = new Scene_Model(_videoName, _questionGroups, _flagGroups);
        sr.Close();
        sr.Dispose();
        return _sm;
    }
}

(以上代码内容请忽略,主要是看如何在Lua中调用上面的方法)

ToLua需要把C#脚本的类名注册到ToLua框架中,然后在Lua脚本中才能够使用该类以及类里面的成员。打开CustomSettings.cs脚本,在public static BindType[] customTypeList ={}中添加如下代码:

_GT(typeof(CSVParse))

保存脚本后点击Lua/Clear wrap files菜单,清除已存在的所有文件。

然后点击上图中的Generate All,在Source/Generaet目录下,就会生成Lua语言中能识别的类库,该文件是以Wrap为结尾的C#文件。

以下流程是在C#脚本中调用上面的CSVParse.lua脚本,在层级视图中创建空物体Manager,给其添加LuaManager脚本组件,然后再添加Test.cs脚本,脚本内容如下:

void Start()
    {
       List<QuestionModel> list = CSVParse.CSVParse_QuestionModel("Q_Config.csv");
        Debug.Log(list.Count);
}

运行程序后显示如下:

以下流程是在C#中读取Lua脚本,在Assets/Lua文件夹中创建CSVParse.lua脚本,脚本内容如下:

Test = {}
local this = Test
require('TestMusic')
local ui
local manager
local audio
local 
function this.Awake(object)
    manager = GameObject.Find('Manager')
    manager:AddComponent(typeof(AudioSource))
    coroutine.start(Voice.PlayRightVoice)
end

Test.cs脚本中改为如下代码:

void Start()
    {
        LuaManager.Instance.LuaClient.LuaState.DoFile("Test.lua");
        LuaManager.Instance.LuaClient.CallFunc("Test.Awake", gameObject);
}

运行程序以后,就可以播放音乐了,以上就是本文的大致内容,针对文章中的技术大家可以私聊我,让我一起共同进步吧。