LuaInterface用户指南

【因为学习,所以翻译!】

1.介绍

LuaInterface 是 Lua 语言和 Microsoft.NET 平台公共语言运行时 (CLR) 之间的集成库。很多语言已经有面向 CLR 编译器和 CLR 实现,已经存在为微软windows、 BSD 操作系统和 Linux 操作系统。

Lua是一个为扩展应用程序而设计的编程语言,解释执行,很容易嵌入的库。详细的信息可以参考Lua‘sreference manual。

下面的部分介绍怎样编译和安装LuaInterface。第3部分包括了在CLR应用程序中使用它,第4部分介绍Lua脚本中的使用。

2.安装LuaInterface

LuaInterface需要Lua解释器来工作。Lua5.0的一个解释器已经包含在LuaInterface发布包中,包含Mircrosoft Windows下的LuaInterface二进制文件和.NETCLR(LuaInterface.dll和luanet.dll)。你必须拷贝lua50.exe和lua50.dll到你的PATH目录下,拷贝LuaInterface.dll到你的全局Assembly缓存。LuaInterface使用Compat-5.1,因此拷贝luanet.dll到你的package.cpath下。

从源码编译LuaInterface不困难。发布包包含一个用来编译luanet的工程文件,其是在Visual Studio .Net 2003下的,用来编译的编译脚本是在Visual Studio 6下的。你可以简单的编译所有src/LuaInte得到LuaInterface.dll。

3.在CLR下使用Lua

CLR应用程序通过LuaInterface.Lua类来使用Lua解释器。实例化这个类,创建一个新的Lua解释器,不同实例直接完全独立。 Lua类索引创建,读取和修改全部变量,由变量的名字索引,如在下面的示例 (索

引器总是返回一个对象,必须强制转换为正确的类型):

代码:

// Start a Lua interpreter
Lua lua = new Lua();
// Create global variables "num" and "str"
lua["num"] = 2;
lua["str"] = "a string";
// Create an empty table
lua.NewTable("tab");
// Read global variables "num" and "str"
double num = (double)lua["num"];
string str = (string)lua["str"];

DoString和DoFile方法执行Lua脚本。当脚本返回值时,这个方法返回一个数组,如

代码:

// Execute a Lua script file
lua.DoFile("script.lua");
// Execute chunks of Lua code
lua.DoString("num=2");
lua.DoString("str=’a string’");
// Lua code returning values
object[] retVals = lua.DoString("return num,str");

LuaInterface自动转换Lua的nil到CLR的null,strings到System.String,numbers到System.Double,booleans到System.Boolean,tables到LuaInterface.LuaTable,functions到LuaInterface.LuaTable,反之亦然。Userdata是一种特殊情况:CLRobjects没有匹配的Lua类型,userdata转换回原类型当传递给CLR时。LuaInterface转换其它userdata到LuaInterface.LuaUserData.

LuaTable和LuaUserData对象有索引来读取和修改字段,使用字符串或数字进行索引。LuaFunction和LuaUserData对象包含一个Call方法来执行函数,包含参数个数,

返回值在一个数组中。

最后,Lua类有一个RegisterFunction函数来注册CLR函数作为一个全局Lua函数。它的参数包含函数名字、目标函数和表示方法的MethodInfo,如lua.RegisterFunction("foo",obj,obj.GetType().GetMethod("Foo"))注册了object obj的Foo方法作为foo函数。

4.lua下使用CLR

这个部分包含在Lua脚本初始化和使用CLR对象,或通过Lua解释器执行,或在CLR应用程序中执行。下面的所有例子都是Lua语言。

4.1加载CLR类型和实例化对象

为了实例化对象,脚本需要类型引用。使用静态字段 static fields、调用静态方法static methods同样也需要类型引用。为了获得类型引用,脚本文件首先需要加

载一个assembly包含指定类型,通过load_assembly函数。然后使用import_type函数来获得引用。下面的例子显示了怎样使用这两个函数:

代码:

require"luanet"
-- Loads the System.Windows.Forms and System.Drawing assemblies
luanet.load_assembly("System.Windows.Forms")
luanet.load_assembly("System.Drawing")
Form = luanet.import_type("System.Windows.Forms.Form")
Point = luanet.import_type("System.Drawing.Point") -- structure
-- Loading an enumeration
StartPosition = luanet.import_type("System.Windows.Forms.FormStartPosition")

调用实例化对象的类型引用。LuaInterface使用第一个满足参数个数和类型的构造函数,由于有重载构造函数存在,匹配过程会转换数字字符串到数字,数字到字符

串,如果必要,Lua中的数字参数同样转换到对应CLR数字类型。get constructor bysig返回一个给定的类型和构造函数的 signa-ture (为构造函数的形参类型的类型引用) 的构造函数。调用返回构造函数实例化的对象。下面的示例演示实例化一个 CLR 对象的不同方法:

代码:

-- SomeType is a reference to a type with the following constructors
-- 1. SomeType(string)
-- 2. SomeType(int)
-- 3. SomeType(int,int)
obj1 = SomeType(2,3) -- instantiates SomeType with constructor 3
obj2 = SomeType("x") -- instantiates SomeType with constructor 1
obj3 = SomeType(3) -- instantiates SomeType with constructor 1
Int32 = import_type("System.Int32")
-- Gets the SomeType constructor with signature (Int32)
SomeType_cons2 = get_constructor_bysig(SomeType,Int32)
obj3 = SomeType_cons2(3) -- instantiates SomeType with constructor 2

4.2使用字段和方法

脚本中可以使用CLR对象的字段,语法和从table中索引数据一样。写入字段的数据被转换为了对应字段的类型,赋给Int32字段的数字转换为了Int32.没有被索引的属

性也像字段一样使用。

LuaInterface有一个简单的索引一维数组的方式,如arr[3].多维数组需要使用Array类的方法。

脚本可以调用对象的方法,语法和调用table的方法一样,传递对象的第一个参数,使用‘:’操作符.可以使用Get和Set方法来使用索引属性(usually get PropertyName and set PropertyName).。

代码:

-- button1, button2 and form1 are CLR objects
button1.Text = "OK";
button2.Text = "Cancel";
form1.Controls:Add(button1);
form1.Controls:Add(button2);
form1:ShowDialog();

Lua只有按值参数的函数调用,因此当脚本调用一个使用了out或ref参数的方法时,LuaInterface返回这些参数的输出值,和方法的返回值一起。在方法调用中out 参

数应被省略,如下例所示:

代码:

-- calling int obj::OutMethod1(int,out int,out int)
retVal,out1,out2 = obj:OutMethod1(inVal)
-- calling void obj::OutMethod2(int,out int)
retVal,out1 = obj:OutMethod2(inVal) -- retVal será nil
-- calling int obj::RefMethod(int,ref int)
retVal,ref1 = obj:RefMethod(inVal,ref1)

如果一个方法被重载,第一个匹配参数数目、类型的版本会被调用,忽略out参数。下面的例子展示了一个脚本怎么调用不同版本的SomeType的重载SomeMethod方法。

代码:

-- Versions of SomeType.SomeMethod:
-- 1. void SomeMethod(int)
-- 2. void SomeMethod(string)
-- 3. void SomeMethod(OtherType)
-- 4. void SomeMethod(string,OtherType)
-- 5. void SomeMethod(int,OtherType)
-- 6. void SomeMethod(int,OtherTypeSubtype)
-- obj1 is instance of SomeType
-- obj2 is instance of OtherType
-- obj3 is instance of OtherTypeSubtype
obj1:SomeMethod(2) -- version 1
obj1:SomeMethod(2.5) -- version 1, round down
obj1:SomeMethod("2") -- version 1, converts to int
obj1:SomeMethod("x") -- version 2
obj1:SomeMethod(obj2) -- version 3
obj1:SomeMethod("x",obj2) -- version 4
obj1:SomeMethod(2,obj2) -- version 4
obj1:SomeMethod(2.5,obj2) -- version 4, round down
obj1:SomeMethod(2,obj3) -- version 4, cast
-- versions 5 and 6 never get called

还有函数 get_method_bysig 的情况下一种方法有永远不会调用的版本。给定一个对象或类型引用和方法签名signature(方法的名称和类型) 该函数将返回与该签名,如以下示例所示的方法:

代码:

-- Versions of SomeType.SomeMethod:
-- 5. void SomeMethod(int,OtherType)
-- obj1 is instance of SomeType
-- obj2 is instance of OtherType
Int32 = luanet.import_type(’System.Int32’)
SomeMethod_sig5 = luanet.get_method_bysig(obj1,’SomeMethod’,
Int32,obj2:GetType())
SomeMethod_sig5(obj1,2,obj2) -- calls version 5

如果一个方法或字段名称是Lua的保留关键字,脚本仍然能使用它们,通过obj["name"]语法。如果一个对象有2个方法使用相同的签名但是属于不同接口,如IFoo.method()和IBar.method(),那么标记obj["IFoo.method"](obj)调用第一个版本。

LuaInterface在执行方法发生错误时会抛出异常,以带错误信息的异常对象。如果脚本想捕获错误,必须用pcall来调用所有方法。

4.3处理事件

LuaInterface中的事件有个一个Add和一个Remove方法,分别用来注册和取消注册事件处理。Add以Lua方法为参数,转换它到CLR对应delegate托管方法并返回。Remove以事件处理delegate托管为参数,移除处理器,如下:

代码:

function handle_mouseup(sender,args)
print(sender:ToString() .. ’ MouseUp!’)
button.MouseUp:Remove(handler)
end
handler = button.MouseUp:Add(handle_mouseup)

脚本同样可以使用事件对象的add和remove方法来注册事件处理(usually add EventName and remove EventName),,但是add不返回delegate托管对象,因此函数以这种方式注册后不能取消注册。

4.4托管Delegates和子类化subtyping

LuaInterface提供3中动态创建类型的方法来扩展CLR。

第一种已经谈到在事件处理程序的上下文中: 通过 Lua 函数委托预计在哪里,LuaInterface 创建一个新的委托类型并将其传递给 CLR。

第二种方法是传递一个Lua table,其中实现了接口。LuaInterface创建一个新的接口实现的类型,这个类型的对象方法托管到table,如下:

代码:

-- interface ISample { int DoTask(int x, int y); }
-- SomeType.SomeMethod signature: int SomeMethod(ISample i)
-- obj is instance of SomeType
sum = {}
function sum:DoTask(x,y)
return x+y
end
-- sum is converted to instance of ISample
res = obj:SomeMethod(sum)

如果接口中有重载函数,所有版本函数都会托管到一个Lua函数,这个函数根据参数类型确定哪个版本的被调用。LuaInterface不能传递out参数给函数,但是函数必须一起返回这些值和ref参数的输出值,如下

代码:

-- interface ISample2 { void DoTask1(ref int x, out int y);
-- int DoTask2(int x, out int y); }
-- SomeType.SomeMethod signature: int SomeMethod(ISample i)
-- obj is instance of SomeType
inc = {}
function inc:DoTask1(x)
return x+1,x
end
function inc:DoTask2(x)
return x+1,x
end
res = obj:SomeMethod(sum)

最后一种创建新CLR类型的方式是子类化已经存在的类,用Lua table的函数来重写一些或所有它的virtual方法(如果 Lua table不会重写的方法 LuaInterface 使用的原始版本)。table函数调用父类的函数通过一个名字为base的字段。

为了将一个table变成一个子类的实例,脚本必须调用make_object函数,table 并将作为参数创建子类的类类型引用。下面的示例演示如何创建子类:如下:

代码:

-- class SomeObject {
-- public virtual int SomeMethod(int x, int y) { return x+y; } }
-- SomeType.SomeMethod signature: int SomeMethod(SomeObject o)
-- obj is instance of SomeType
some_obj = { const = 4 }
function some_obj:SomeMethod(x,y)
local z = self.base:SomeMethod(x,y)
return z * self.const
end
SomeObject = luanet.import_type(’SomeObject’)
luanet.make_object(some_obj,SomeObject)
res = some_obj:SomeMethod(2,3) -- returns 20
res = some_obj:ToString() -- calls base method
res = obj:SomeMethod(some_obj) -- passing as argument

在实现接口应用在子类化过程中,关于重载和out/ref参数存在同样的问题。最后,free_object函数以前面make_object调用的table参数,服务table和CLR子类实例的连接。脚本必须在丢弃table的引用前使用这个方法,不然会造成内存泄露。

【完】

应用:

【USAGE】

导入资源包然后向‘uLua/Plugins/‘路径下的Plugins 文件夹拷贝到 Assets下将 LuaInterface 命名空间添加到您的脚本查看examples 中的一些基本用法。main代码是非常具有可读性 (Lua.cs) 和 LuaInterface 手册。

【Lua ‘require‘ or ‘dofile‘】

为了导入文件要求 and/or dofile 文件,您必须拥有text asset放在您的项目根目录‘Assets/Resources‘ 内,它必须将命名为 *.lua.txt‘。

例如:

‘Assets/Resources/MyDir/myscript.lua.txt‘

然后在你的lua代码中可以要求:

‘require("MyDir/myscript.lua")‘

1】下载到ulua的资源包,导入工程中,按照说明,将其中的Plugins文件夹中的内容整体复制到工程的Plugins文件夹中;

(这一步可能会出现找不到dll的错误,我这里是重启Unity解决的)

导入后工程的根目录有以下这些东西:

2】在.txt文件中编写一些Lua脚本,比如:

(注意:此处.txt脚本的后缀名是".txt")

3】将这个存有Lua脚本的.txt文件加载到游戏中,资源类型是"TextAsset":

方法任选,比如:

①放到Resources文件夹中,Resources.Load()来加载;

②放到工程中任意地方,使用Resoueces.LoadAtPath()来加载;

③打成AssetBundle,放到文件服务器上,使用WWW来异步加载。

如果使用③的话,推荐一个名字叫做"HFS"的虚拟文件服务器的小工具很方便,只需点一下,即可模拟一个文件服务器出来:

,非常好用)

4】解析Lua脚本:

1 LuaState ls = new LuaState();
2 ls.DoString(luaString);              //luaString是加载进来的文档中的字符,类型是string
3 LuaFunction lf = ls.GetFunction("luaFunc");  //luaFunc是Lua脚本中编写的方法名
4 object[] r = lf.Call("2");            //括号中的"2"是要传递到Lua中的方法中的参与整型运算的值
5 return r[0].ToString();              //r[0]即是运算后返回的值,类型是object,用之前,需要转一下类型

非常简单,综上,Lua与Unity3d中的C#的联系,互相传递方法和参数,就OK了。你会看到,真的有外部的逻辑在正在运行的游戏中起作用了。

时间: 2024-11-10 07:56:13

LuaInterface用户指南的相关文章

菜鸟学习 - Unity中的热更新 - LuaInterface用户指南

[由于学习,所以翻译!] 1.介绍 LuaInterface 是 Lua 语言和 Microsoft.NET 平台公共语言运行时 (CLR) 之间的集成库. 非常多语言已经有面向 CLR 编译器和 CLR 实现,已经存在为微软windows. BSD 操作系统和 Linux 操作系统. Lua是一个为扩展应用程序而设计的编程语言,解释运行,非常容易嵌入的库.具体的信息能够參考Lua'sreference manual. 以下的部分介绍如何编译和安装LuaInterface.第3部分包括了在CLR

Android官方技术文档翻译——Gradle 插件用户指南(4)

近期赶项目,白天基本没时间,仅仅有晚上在家的时候才干看一看.昨天晚上仅仅翻译完了第四章,今天就仅仅发第四章吧. 本文译自Android官方技术文档<Gradle Plugin User Guide>,原文地址:http://tools.android.com/tech-docs/new-build-system/user-guide. 翻译不易.转载请注明CSDN博客上的出处: http://blog.csdn.net/maosidiaoxian/article/details/4195580

Gradle用户指南(章10:Web应用程序快速入门)

Gradle用户指南(章10:Web应用程序快速入门) 本章正在完善中..... 本章介绍gradle对web应用程序的支持.Gradle为web应用程序开发提供了两个插件:War插件和Jetty插件.War插件扩展了java插件并且能为你的项目构建一个war包.Jetty插件扩展了War插件并且提供了发布你的web项目到Jetty容器中的功能. 构建一个War文件 构建War文件,你需要在项目中添加War插件. 注意:你可以在gradle发布库和源文件的samples/webApplicati

【Flume NG用户指南】(2)构造

作者:周邦涛(Timen) Email:[email protected] 转载请注明出处:  http://blog.csdn.net/zhoubangtao/article/details/28277575 上一篇请參考[Flume NG用户指南](1)设置 3. 配置 前边的文章已经介绍过了,Flume Agent配置是从一个具有分层属性的Java属性文件格式的文件里读取的. 3.1 定义数据流 要在一个Flume Agent中定义数据流,你须要通过一个Channel将Source和Sin

CecOS v1.4 中文企业云操作系统 管理员及用户指南发布

CecOS 项目团队为了使用者更好的安装.使用,我们整理历史版本资料整理成PDF文档,欢迎大家下载阅览及指正. CecOS v1.4 -管理员指南:下载 CecOS v1.4 -用户指南:下载 OPENFANS 官方邮件:[email protected]   [旨 OPENFANS 官方对外合作等通道] CecOS 官方邮件:[email protected]    [旨 CecOS 项目相关交流通道]

tween.js 用户指南 - 与 Three.js 配合使用的补间动画库

太阳火神的美丽人生 (http://blog.csdn.net/opengl_es) 本文遵循"署名-非商业用途-保持一致"创作公用协议 转载请保留此句:太阳火神的美丽人生 -  本博客专注于 敏捷开发及移动和物联设备研究:iOS.Android.Html5.Arduino.pcDuino,否则,出自本博客的文章拒绝转载或再转载,谢谢合作. Learning Three.js - Tween.js for Smooth Animation tween.js 用户指南tween.js u

【Flume NG用户指南】(2)配置

作者:周邦涛(Timen) Email:[email protected] 转载请注明出处:  http://blog.csdn.net/zhoubangtao/article/details/28277575 上一篇请参考[Flume NG用户指南](1)设置 3. 配置 前边的文章已经介绍过了,Flume Agent配置是从一个具有分层属性的Java属性文件格式的文件中读取的. 3.1 定义数据流 要在一个Flume Agent中定义数据流,你需要通过一个Channel将Source和Sin

Gradle用户指南(章9:Groovy快速入门)

Gradle用户指南(章9:Groovy快速入门) 你可以使用groovy插件来构建groovy项目.这个插件继承了java插件的功能,且扩展了groovy编译.你的项目可以包含groovy代码.java代码或者两者混合.在其他方面,groovy项目与java项目是一致的,就像我们在章7:java快速入门中所看到的那样. 一个简单的groovy项目 让我们来看一个示例.使用groovy插件,需要添加下面的代码得到你的构建文件中. 注意:示例代码在gradle发布的源文件包的路径samples/g

阿里云 EDAS-HSF 用户指南

阿里云 EDAS-HSF 用户指南 针对 EDAS v2.3.0©Alibaba EDAS 项目组2015/8/19 1 前言本文档旨在描述阿里云 EDAS 产品中应用服务化模块的基本概念,以及如何使用. 2 产品背景 HSF(High Speed Framework)是一个高可用.高性能.分布式的服务框架.HSF 可以被看作是人体的血管,帮助应用轻松实现服务化解耦,是阿里内部各个系统通信的基础软件. 3 专业术语?  Agent安装于用于 ECS,负责 EDAS 控制台与用户 ECS 之间的通