菜鸟学习 - Unity中的热更新 - 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的project文件。其是在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的保留keyword,脚本仍然能使用它们。通过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的资源包。导入project中。依照说明,将当中的Plugins目录中的内容总体复制到project的Plugins目录中;
(这一步可能会出现找不到dll的错误,我这里是重新启动Unity解决的)
导入后project的根目录有以下这些东西:
2】在.txt文件里编写一些Lua脚本。比方:
(注意:此处.txt脚本的后缀名是".txt")
3】将这个存有Lua脚本的.txt文件载入到游戏中,资源类型是"TextAsset":
方法任选,比方:
①放到Resources目录中。Resources.Load()来载入;
②放到project中随意地方。使用Resoueces.LoadAtPath()来载入。
③打成AssetBundle,放到文件server上。使用WWW来异步载入。
(假设使用③的话,推荐一个名字叫做"HFS"的虚拟文件server的小工具非常方便。仅仅需点一下,就可以模拟一个文件server出来:
。非常好用)
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。用之前,须要转一下类型
非常easy。综上,Lua与Unity3d中的C#的联系。互相传递方法和參数。就OK了。
你会看到,真的有外部的逻辑在正在运行的游戏中起作用了。
菜鸟学习 - Unity中的热更新 - LuaInterface用户指南的更多相关文章
- Unity实现c#热更新方案探究(一)
转载请标明出处:http://www.cnblogs.com/zblade/ 最近研究了一下如何在unity中实现c#的热更新,对于整个DLL热更新的过程和方案有一个初步的了解,这儿就写下来,便于后续 ...
- Unity官方发布热更新方案性能对照
孙广东 2016.3.11 Unity应用的iOS热更新 作者:丁治宇 Unity TechnologiesChina Agenda • 什么是热更新 • 为何要热更新 • 怎样在iOS 上对 ...
- React Native超简单完整示例-tabs、页面导航、热更新、用户行为分析
初学React Native,如果没有人指引,会发现好多东西无从下手,但当有人指引后,会发现其实很简单.这也是本人写这篇博客的主要原因,希望能帮到初学者. 本文不会介绍如何搭建开发环境,如果你还没有搭 ...
- Unity实现c#热更新方案探究(三)
转载请标明出处:http://www.cnblogs.com/zblade/ 前面两篇文章从头到尾讲解了C#热更新的一些方案,从程序域来加载和卸载DLL,到使用ILRuntime来实现安卓和IOS平台 ...
- Unity实现c#热更新方案探究(二)
转载请标明出处:http://www.cnblogs.com/zblade/ 一.IOS对DLL热更新的禁止 紧接上文,继续对C#热更新的研究.上文中,已经说了如何基于appDomain来实现对DLL ...
- 用CodePush在React Native App中做热更新
最近在学React Native,学到了CodePush热更新. 老师讲了两种实现的方法,现将其记录一下. 相比较原生开发,使用React Native开发App不仅能节约开发成本,还能做原生开发不能 ...
- 在React Native中集成热更新
最近,在项目DYTT集成了热更新,简单来说,就是不用重新下载安装包即可达到更新应用的目的,也不算教程吧,这里记录一下. 1.热更新方案 目前网上大概有两个比较广泛的方式,分别是 react-nativ ...
- 另类Unity热更新大法:代码注入式补丁热更新
对老项目进行热更新 项目用纯C#开发的? 眼看Unity引擎热火朝天,无数程序猿加入到了Unity开发的大本营. 一些老项目,在当时ulua/slua还不如今天那样的成熟,因此他们选择了全c#开发:也 ...
- Unity 安卓下DLL热更新一(核心思想)
大家都知道一谈起热更新的话首选是Ulua这个插件, 其实Unity可以使用dll热更新的,如果你实在不想用Lua来编写逻辑,0.0请下看Dll+AssetBundle如何实现热更新的.让你看完这个文章 ...
随机推荐
- jQuery ajax - serialize() 方法
http://www.jb51.net/article/60849.htm http://www.w3school.com.cn/jquery/ajax_serialize.asp
- 程序里面的system.out.println()输出到其他位置,不输出到tomcat控制台。
设置startup.bat: call "%EXECUTABLE%" run %CMD_LINE_ARGS% >> ..\logs\kongzitai.txt 将sys ...
- Warning once only: Detected a case where constraints ambiguously suggest a height of zero for a tableview cell's content view...
Warning once only: Detected a case where constraints ambiguously suggest a height of zero for a tabl ...
- [转]Delphi 中 image 控件加载bmp、JPG、GIF、PNG等图片的办法
procedure TForm1.Button1Click(Sender: TObject); var jpg: TJPEGImage; // 要use Jpeg单元 begin // 显示jpg大图 ...
- js监听回车事件
标题通俗的说,也就是绑定当用户按下回车键要执行的事件. 下面,入正题. 第一步,先编写简单的页面代码,这里我们只需要一个按钮就足够了.当然,还有按钮事件. <html> <head& ...
- sqlserver 进行MD5加密
官方定义函数: HashBytes ( '<algorithm>', { @input | 'input' } ) <algorithm>::= MD2 | MD4 | MD ...
- TableView_编辑 实例代码
@interface MJViewController () <UITableViewDataSource, UITableViewDelegate> { NSMutableArray * ...
- temp gbk2utf8
__author__ = 'root' # -*- coding: utf-8 -*- ps = '/data/poitestdata/行政地名.csv' pt = '/data/poitestdat ...
- 主流屏幕对比:IPS/LTPS/CGS/IGZO/AMOLED
IPS.LTPS.CGS.IGZO.AMOLED都是什么屏幕又有什么区别?目前的手机屏幕技术实在太多,本文旨在介绍各种面板以及屏幕技术,便于大家更好地进行区分. 近年来手机屏幕技术层出不穷,早在几年前 ...
- Keil C51中变量的使用
引言 8051内核单片机是一种通用单片机,在国内占有较大的市场份额.在将C语言用于51内核单片机的研究方面,Keil公司做得最为成功.由于51内核单片机的存储结构的特殊性,Keil C51中变量的使用 ...