Unity LuaFramework_UGUI tolua# examples学习整理(一)

说明

上次想要学习使用ulua,结果误打误撞下了个基于tolua#的LuaFramework,还当成ulua在用,哈哈哈哈哈——详见(Unity 初次使用ulua,熟悉C#和Lua交互
既然用的是tolua#,那这次再整理一下tolua#,将官方的几个examples都尝试一下

下载&导入

从http://www.ulua.org/index.html下载,因为我学的Unity版本比较新,使用的是UGUI,所以选择LuaFramework_UGUI版本
在这里插入图片描述从GitHub上Clone下来后,有这些内容:
在这里插入图片描述新建一个Unity项目,新建完成后,打开项目所在目录,将LuaFramework_UGUI中的Assets和LuaEncoder文件夹拷贝至项目目录下:
在这里插入图片描述打开Unity项目,等待加载完成后,会发现菜单栏中多了一些东西
在这里插入图片描述算是导入成功了

LuaFramework Examples

导入成功后
项目Assets>LuaFramework>ToLua>Examples文件路径下,有一些例子,接下来一个个执行看看:
在这里插入图片描述

1 HelloWorld

先从第一个HelloWorld开始
打开Examples>01_HelloWorld下的那个场景文件,切换到该场景
运行:
在这里插入图片描述HelloWorld.cs中的代码:

using UnityEngine;
using LuaInterface;
using System;

public class HelloWorld : MonoBehaviour
{
    void Awake()
    {
        LuaState lua = new LuaState();
        lua.Start();
        string hello =
            @"                
                print('hello tolua#')                                  
            ";
        
        lua.DoString(hello, "HelloWorld.cs");
        lua.CheckTop();
        lua.Dispose();
        lua = null;
    }
}

这个代码逻辑很简单,直接将string类型的lua代码放在lua虚拟机中执行,通过lua打印显示hello,然后析构虚拟机并置空,结束。

2 ScriptsFromFile

打开02_ScriptsFromFile下的场景文件,运行
在这里插入图片描述点击DoFile和Reqire按钮都可以执行文件中的lua代码
在这里插入图片描述ScriptsFromFile.cs代码如下:

using UnityEngine;
using System.Collections;
using LuaInterface;
using System;
using System.IO;

//展示searchpath 使用,require 与 dofile 区别
public class ScriptsFromFile : MonoBehaviour 
{
    LuaState lua = null;
    private string strLog = "";    

	void Start () 
    {
#if UNITY_5 || UNITY_2017 || UNITY_2018		
        Application.logMessageReceived += Log;
#else
        Application.RegisterLogCallback(Log);
#endif         
        lua = new LuaState();                
        lua.Start();        
        //如果移动了ToLua目录,自己手动修复吧,只是例子就不做配置了
        string fullPath = Application.dataPath + "\\LuaFramework/ToLua/Examples/02_ScriptsFromFile";
        lua.AddSearchPath(fullPath);        
    }

    void Log(string msg, string stackTrace, LogType type)
    {
        strLog += msg;
        strLog += "\r\n";
    }

    void OnGUI()
    {
        GUI.Label(new Rect(100, Screen.height / 2 - 100, 600, 400), strLog);

        if (GUI.Button(new Rect(50, 50, 120, 45), "DoFile"))
        {
            strLog = "";
            lua.DoFile("ScriptsFromFile.lua");                        
        }
        else if (GUI.Button(new Rect(50, 150, 120, 45), "Require"))
        {
            strLog = "";            
            lua.Require("ScriptsFromFile");            
        }

        lua.Collect();
        lua.CheckTop();
    }

    void OnApplicationQuit()
    {
        lua.Dispose();
        lua = null;
#if UNITY_5 || UNITY_2017 || UNITY_2018	
        Application.logMessageReceived -= Log;
#else
        Application.RegisterLogCallback(null);
#endif 
    }
}

ScriptsFromFile.lua中的代码如下:

print("This is a script from a utf8 file")
print("tolua: 你好! こんにちは! 안녕하세요!")

上面的C#代码中展现了两种加载文件的方式,DoFile和Require
在lua中也有这两种加载lua文件的方式doFile和require(注意!lua中是小驼峰!)
两者的区别如下:
require:在加载一个.lua文件时,require会先在package.loaded中查找此模块是否存在,如果存在,直接返回模块;如果不存在,则加载模块文件。在加载时不需要写文件的后缀名,函数会通过特定的搜索路径去查找符合规则的模块,例如上面C#中的lua.Require(“ScriptsFromFile”);
doFile:读入代码文件并编译执行。每调用dofile一次,都会重新编译执行一次。在使用该函数时需要写上文件后缀,例如上面C#中的lua.DoFile(“ScriptsFromFile.lua”);
所以相对来说,Require只会加载文件一次,更加高效,推荐使用Require
顺带一提,在lua中还有一种加载文件的方式:loadFile
loadFile是编译代码,将这个模块当做一个函数返回,但是不执行,例如:

a = loadfile("test1.lua")

此时的a是一个包含test1.lua所有内容的函数,但是test1.lua并没有被执行
doFile相当于在loadFile外再进行了一次包装,直接执行

在C#中调用lua文件的方式除了上面两种之外,还可以读取lua文件转化为字符串进行执行

TextAsset scriptFile;
// scriptFile读取文件
luaState.DoString(scriptFile.text);

3 CallLuaFunction

打开第三个example,运行:
在这里插入图片描述example中展示了四种调用lua函数的方式,C#代码如下:

using UnityEngine;
using System.Collections;
using LuaInterface;
using System;

public class CallLuaFunction : MonoBehaviour 
{
    private string script =
        @"  function luaFunc(num)                        
                return num + 1
            end

            test = {}
            test.luaFunc = luaFunc
        ";

    LuaFunction luaFunc = null;
    LuaState lua = null;
    string tips = null;
	
	void Start () 
    {
#if UNITY_5 || UNITY_2017 || UNITY_2018
        Application.logMessageReceived += ShowTips;
#else
        Application.RegisterLogCallback(ShowTips);
#endif
        new LuaResLoader();
        lua = new LuaState();
        lua.Start();
        DelegateFactory.Init();        
        lua.DoString(script);

        //Get the function object
        luaFunc = lua.GetFunction("test.luaFunc");

        if (luaFunc != null)
        {
            int num = luaFunc.Invoke<int, int>(123456);
            Debugger.Log("generic call return: {0}", num);

            num = CallFunc();
            Debugger.Log("expansion call return: {0}", num);

            Func<int, int> Func = luaFunc.ToDelegate<Func<int, int>>();
            num = Func(123456);
            Debugger.Log("Delegate call return: {0}", num);
            
            num = lua.Invoke<int, int>("test.luaFunc", 123456, true);
            Debugger.Log("luastate call return: {0}", num);
        }

        lua.CheckTop();
	}

    void ShowTips(string msg, string stackTrace, LogType type)
    {
        tips += msg;
        tips += "\r\n";
    }

#if !TEST_GC
    void OnGUI()
    {
        GUI.Label(new Rect(Screen.width / 2 - 200, Screen.height / 2 - 150, 400, 300), tips);
    }
#endif

    void OnDestroy()
    {
        if (luaFunc != null)
        {
            luaFunc.Dispose();
            luaFunc = null;
        }

        lua.Dispose();
        lua = null;

#if UNITY_5 || UNITY_2017 || UNITY_2018
        Application.logMessageReceived -= ShowTips;
#else
        Application.RegisterLogCallback(null);
#endif
    }

    int CallFunc()
    {        
        luaFunc.BeginPCall();                
        luaFunc.Push(123456);
        luaFunc.PCall();        
        int num = (int)luaFunc.CheckNumber();
        luaFunc.EndPCall();
        return num;                
    }
}

generic call

这里使用的是Invoke<>()函数来调用函数
<>内写的是参数和返回值,例如上面例子中的luaFunc.Invoke<int, int>(123456);
表示参数为int,返回值为int
Invoke<T1, T2, T3, …, R1>
Invoke<>内可以放多个输入参数的类型,但只能有一个返回类型且放在最后
因为Lua支持多参数返回,如果使用上面的Invoke调用函数的话,只会返回Lua函数中的第一个返回值
例如:

function luaFunc(a, b)
    return a+b, a-b, 'helloworld'
end

在C#中只能得到第一个返回值a+b
如果需要得到lua返回的所有数据,可以将lua的返回值用数组封装起来,然后在C#中利用object[]读取,例如:

--lua
function luaFunc(a, b)
	return {a+b, a-b, 'helloworld'}
end

//C#
luaFunction = luaState.GetFunction("luaFunc");
object[] res = luaFunction.Invoke<int, int, object[]>(1, 2);
Debugger.Log("generic call return: {0}, {1}, {2}", res[0].ToString(), res[1].ToString(), res[2].ToString());

这样可以得到所有的返回数据

expansion call

这种方式调用函数相对复杂一些,利用了lua的堆栈
我尝试了一下lua函数的多参数输入和返回,代码如下:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using LuaInterface;

public class Example03_CallLuaFunction : MonoBehaviour {

    private static string luaScript = 
@"
    function luaFunc(a, b)
        return a+b, a-b, 'helloworld'
    end
";
    private LuaState luaState;
    private LuaFunction luaFunction;

	// Use this for initialization
	void Start () {
        luaState = new LuaState();
        luaState.Start();
        luaState.DoString(luaScript);

        luaFunction = luaState.GetFunction("luaFunc");

        if (luaFunction != null)
        {
            luaFunction.BeginPCall();
            luaFunction.Push(1);
            luaFunction.Push(2);
            luaFunction.PCall();
            int res1 = (int)luaFunction.CheckNumber();
            int res2 = (int)luaFunction.CheckNumber();
            string res3 = luaFunction.CheckString();
            Debugger.Log("expansion call return: {0}, {1}, {2}", res1, res2, res3);
            luaFunction.EndPCall();
        }
	}
}

这种方式利用Push压入参数,PCall开始调用,然后是用CheckXXX()函数取出对应类型的参数,可以支持lua的多参数返回

delegate call

这是将lua的function转化为C#的委托(delegate)

Func<int, int> Func = luaFunc.ToDelegate<Func<int, int>>();
num = Func(123456);
Debugger.Log("Delegate call return: {0}", num);

example中的Func(123456)实际是调用了delegate
Func作为一个delegate,还是能够通过+操作添加其他同类型函数,-操作删除函数委托的

luastate call

这个第一种调用方式一样,只是这种方式调用不需要先获取lua的function,而是直接通过luastate调用
LuaState.Invoke<>内同样可以指定多个输入参数,和一个返回参数(放在末尾),同LuaFunction.Invoke
LuaState.Invoke<T1, T2,…, R1>(“lua function name”, T1, T2, …, bool beLogMiss)
调用时输入参数为lua的函数名(用string表示),函数的输入参数,以及一个bool类型的beLogMiss(这个beLogMiss只是指定在找不到lua函数的时候是否打印日志)

if (beLogMiss)
{
    Debugger.Log("Lua function {0} not exists", name);
}

猜你喜欢

转载自blog.csdn.net/ACwawayu/article/details/89419915