原文1:[Unity3D]Unity3D游戏开发之Lua与游戏的不解之缘(上)
各位朋友,大家好,我是秦元培,欢迎大家关注我的博客,我地博客地址是blog.csdn.net/qinyuanpei。如果提到游戏开发,大家一定会想到C/C++、DirectX、OpenGL等这些东西,可是众所周知,游戏行业是一个需求变化极快地行业,如果我们采用编译型的语言,那么我们可能很难跟上这个时代的步伐,因为编译型的语言每经历一次重大地更新,整个项目都需要重新编译,这样无疑会影响我们的开发效率。那么,有没有一种更为高效的游戏开发模式呢?或许答案大家已经看到了。现在在游戏界普遍采用的方式是将游戏的底层逻辑交给C/C++这样的底层语言,而将游戏的上层逻辑交给脚本语言。因为底层逻辑更看重效率而上层逻辑更注重灵活、便捷地使用。例如我们熟知的Unreal引擎是采用UnrealScripts,这是一种类似于Java/C语法地语言;Unity3D引擎是采用的C#/javaScript/Boo这三种脚本语言;cocos2d-x采用地是Lua/javaScript这两种脚本语言,未来可能会支持更多的语言。大家可能想问一个问题:什么是脚本语言?所谓脚本语言是一种用来控制软件应用程序且只在被调用时进行解释或编译的编程语言,这种语言通常以文本的形式来存储脚本代码。换句话说,脚本语言类似于一种指令,它缩短了传统应用程序的编写-编译-链接-运行(edit-compile-link-run)这个过程,是一种解释执行的程序。或许人们发明脚本语言的那一刻起,从未想过要将脚本语言和游戏开发联系在一起,不过脚本语言注定会因为游戏开发而开拓出更为广阔的世界。本文将以目前游戏开发领域较为流行的Lua语言为线索,深度解密游戏开发领域与脚本语言之间千丝万缕的联系。
一、什么是Lua?
Lua 是一个小巧的脚本语言,巴西里约热内卢天主教大学里的一个研究小组于1993年开发,其设计目的是为了嵌入应用程序中,从而为应用程序提供灵活的扩展和定制功能。Lua由标准C编写而成,几乎在所有操作系统和平台上都可以编译,运行。一个完整的Lua解释器不过200k,在目前所有脚本引擎中,Lua的速度是最快的。这一切都决定了Lua是作为嵌入式脚本的最佳选择。相比Python和Per的内核,Lua的内核小于120KB,而Python的内核大约860KB,Perl的内核大约1.1MB。Lua语言支持面向对象编程和函数式编程,它提供了一个通用类型的表table,可以实现数组、哈希表、集合、对象的功能。Lua支持协同进程机制。作为一门可扩展的语言,Lua提供简单而稳定的交互接口,如Lua和C程序可通过一个堆栈交换数据,这使得Lua语言可以快速地和其它语言实现整合。总体来说,Lua语言具备以下优点:(1)语言优美、轻巧 (2)性能优良、速度快 (3)可扩展性强。正因为Lua语言具备了这样的特点,使得它能和游戏开发领域的需求完美地结合起来,因为我们需要这样的一门语言,它能够和C/C++进行完美地交互,因为我们需要它对底层进行封装。它需要足够地简单,因为我们需要简单、灵活、快速地编写代码。那么显然Lua就是我们一直在寻找地这种语言。
二、Lua可以做什么?
尽管博主已经告诉了大家太多的关于Lua语言的优秀特性,相信大家仍然会对Lua语言的能力存在怀疑。大家或许会想,Lua到底可以做什么呢?在《Lua游戏开发》一书中作者已经告诉了我们答案:
1、编辑游戏的用户界面
2、定义、存储和管理基础游戏数据
3、管理实时游戏事件
4、创建和维护开发者友好的游戏存储和载入系统
5、编写游戏的人工智能系统
6、创建功能原型,可以之后用高性能语言移植
这时候我们似乎觉得Lua语言在某种程度上就是专门为游戏开发而诞生的,因为它将大量的优秀特性全部指向了游戏开发领域,因此Lua语言走进走进游戏开发领域变得顺利成章,那么,让我们接着往下看吧,Lua在游戏开发领域有那些成熟的案例吧。
三、哪些游戏使用了Lua?
1、魔兽世界
如果提到Lua在游戏领域中第一次崭露头角,我们就不能不说《魔兽世界》这款游戏,由于《魔兽世界》在其客户端中使用了Lua,使得Lua在游戏领域的作用第一次被展示出来,Lua语言因此在游戏开发领域成名。Lua语言的虚拟机很轻巧,可以很容易地嵌入到客户端程序中。如果需要更新客户端,只需要更新脚本程序即可,无需重新编译整个客户端。这样地优点使得Lua在游戏开发领域一战成名,可以说是《魔兽世界》为游戏开发领域带来了这样激动人心的伟大语言,作为Lua在游戏领域攻城略地的尝试,《魔兽世界》功不可没。
2、大话西游2
如果说《魔兽世界》开辟Lua在国外游戏领域地战场,那么网易的《大话西游2》无疑是开启了国内游戏制作公司使用Lua的先河。2002年网易开发《大话西游2》时,决定在客户端内嵌入新的脚本语言,因为当时使用的微软JScript存在较多Bug、维护不便、兼容性差。当时该项目技术负责人云风吸取了《大话西游1》时外挂泛滥的教训,决定选择一个新的语言,这样既能摆脱对JScript的依赖,又能有效地打击外挂制作者,权衡再三,最终选择了Lua 4.0。后来《大话西游2》在市场上取得了成功,国内游戏开发行业纷纷受此影响采用Lua,可以说是网易Lua走进了国内开发者的视野,不过到今天为止,Lua在国内仍然是一门较为小众的语言,从《大话西游2》引领国内开发者将视角转向Lua到今天将近10余年地时间,此中缘由,只有大家自己去想个清楚啦。
3、古剑奇谭
《古剑奇谭》系列游戏是由上海烛龙信息科技有限公司研发的大型3DRPG单机游戏。游戏设定源自于《山海经》,故事则以武侠和仙侠为创作题材,以中国神话时代为背景,讲述了中国古代侠骨柔情的仙侠文化。《古剑奇谭》系列游戏初代作品与二代作品采用的是不同的游戏引擎和不同的战斗模式,尽管如此,我们依然能从中找到一个共同点,那就是在初代作品和二代作品中都毫无例外的使Lua作为游戏地脚本语言。例如下面是《古剑奇谭》红叶湖迷宫场景的Lua脚本节选:
- require("Necessary")
- require("StoryUtility")
- require("BigMap")
- require("Script_DLC4")
- --------------以下为初始化函数-------------
- function OnEnterLevel()
- if GetStoryVersion() == 2 then
- OnDLCEnterLevelM01()
- else
- if GetMainStory() == 10100 then
- callTaskFunction("story10100")
- elseif GetMainStory() == 161900 then
- callTaskFunction("story161900")
- end
- if gValue.MK == 1 then
- showNPC("NPC 06", false)
- showNPC("NPC 07", false)
- enableTrigger("Tri_MK",false)
- elseif gValue.MK >1 then
- showNPC("NPC 04", false)
- showNPC("NPC 05", false)
- showNPC("NPC 06", false)
- showNPC("NPC 07", false)
- enableTrigger("Tri_MK",false)
- enableTrigger("Tri_MK 02",false)
- end
4、仙剑奇侠传
既然提到了古剑奇谭,怎么能不提仙剑奇侠传呢?虽然和古剑奇谭初代作品发布时间仅仅相差一年的《仙剑奇侠传五》市场反响并没有像游戏制作方所预料地那样成功,不过这部作品值得称赞地地方还是蛮多的,因为进步总是要比缺点多的嘛,毕竟时代在进步,我们不能总是拿仙剑初代作品的高度去要求后续作品,因为我们已经不再是那个年龄的人,而仙剑依然要不断地突破自身、大胆创新和进取。好了,我们暂时先感慨到这里,仙剑四、仙剑五以及仙剑五前传都使用了RenderWare引擎,可能唯一的不同就是仙剑五和仙剑五前传都使用了Lua吧,下面同样是一段从游戏中提取的脚本:
- function baoxiang(id,npcID)
- player.Control(0)
- pid=player.GetMainPlayer()
- player.SetAnim(pid,203)
- global.Print(id)
- global.Wait(1)
- y=flag.GetValue(15093)
- ---------江洋大盗称号获得-------------
- jyd=flag.GetValue(15255)
- jyd=jyd+1
- flag.SetValue(15255,jyd)
- global.Print(jyd)
- global.AddTimer(0.5,13279)
- -----------------------------------------
- if id~=17711 then
- npc.SetAnim(npcID,501)
- global.Wait(1)
- end
5、金庸群侠传Lua复刻版
四、带你走进Lua的世界
- --while-do示例代码
- myValue=10
- while(myValue <= 20) do
- print(myValue)
- myValue=myValue+1
- end
- --sample table && for-do示例代码
- myTables={"Item0","Item1","Item2","Item3"}
- for i=1,table.maxn(myTables) do
- print(myTables[i])
- end
- --complex table示例代码
- myTables={}
- myTables["A"]="ItemA"
- myTables["B"]="ItemA"
- myTables["C"]="ItemA"
- myTables["D"]="ItemA"
- print(myTables["A"])--"ItemA"
- --function示例代码
- function fib(n)
- if(n<2) then
- return n
- else
- return fib(n-1)+fib(n-2)
- end
- end
- --math示例代码
- maxValue=math.max(12,23,56,18,10)--56
- minValue=math.min(25,34,12,75,8)--8
- print(maxValue-minValue)--48
- --字符串演示
- myString="Hello this is the cool program language called Lua";
- print(string.find(myString,"Lua"))--48,50
- --io演示
- io.write("Hello I get a powerful program language called Lua \n")
- io.write(string.format("This Lua is %s and now is %s \n",_VERSION,os.date()))
一、Lua堆栈
如果我们想要理解Lua语言与其它语言交互的实质,我们首先就要理解Lua堆栈。简单来说,Lua语言之所以能和C/C++进行交互,主要是因为存在这样一个无处不在的虚拟栈。栈的特点是先进后出,在Lua语言中,Lua堆栈是一种索引可以是正数或者负数的结构,并规定正数1永远表示栈底,负数-1永远表示栈顶。换句话说呢,在不知道栈大小的情况下,我们可以通过索引-1取得栈底元素、通过索引1取得栈顶元素。下面呢,我们通过一个实例来加深我们对于这段话的理解:
- #include <iostream>
- extern "C" {
- #include "lua.h"
- #include "lualib.h"
- #include "lauxlib.h"
- }
- using namespace std;
- int main()
- {
- //创建Lua环境
- lua_State* L=lua_open();
- //打开Lua标准库,常用的标准库有luaopen_base、luaopen_package、luaopen_table、luaopen_io、
- //luaopen_os、luaopen_string、luaopen_math、luaopen_debug
- luaL_openlibs(L);
- //压入一个数字20
- lua_pushnumber(L,20);
- //压入一个数字15
- lua_pushnumber(L,15);
- //压入一个字符串Lua
- lua_pushstring(L,"Lua");
- //压入一个字符串C
- lua_pushstring(L,"C");
- //获取栈元素个数
- int n=lua_gettop(L);
- //遍历栈中每个元素
- for(int i=1;i<=n;i++)
- {
- cout << lua_tostring(L ,i) << endl;
- }
- return 0;
- }
在上面的这段代码中,我们可以可以看到我们首先创建了一个lua_State类型的变量L,我们可以将它理解成一个Lua运行环境的上下文(Context),这里我们在Lua堆栈中压入了四个元素:20、15、"Lua"、"C"然后将其输出,如果大家理解了Lua堆栈中的索引,那么最终输出的结果应该是:20、15、"Lua"、"C",因为索引1始终指向栈底,最先入栈的元素会处于栈底。因此当我们按照递增的索引顺序来输出栈中的元素的话,实际上是自下而上输出,这样我们就能得到这样的结果了。
好了,如果这段代码没有什么问题的话,接下来我们来讲解Lua为C/C++提供的接口,它们均被定义在lua.h文件中。Lua提供的C/C++接口大部分与栈操作有关,因此深入理解Lua堆栈是学习Lua语言的重点和难点。通过数据结构的知识,我们可以知道栈有出栈和入栈两种基本操作,Lua提供的C API中入栈可以通过push系列的方法来实现,如下图所示:
而出栈或者说查询的方法则可以通过to系列的方法来实现,如下图:
这两部分是学习Lua语言一定要去了解的内容,因为以后如果需要我们将Lua整合到其它项目中这些内容,这些东西可以说是原理性、核心性的东西。好了,下面我们利用这里的API对一个示例代码进行改造,这里加入了对栈中元素类型的判断:
- #include <iostream>
- extern "C" {
- #include "lua.h"
- #include "lualib.h"
- #include "lauxlib.h"
- }
- using namespace std;
- int main()
- {
- //创建Lua环境
- lua_State* L=lua_open();
- //打开Lua标准库,常用的标准库有luaopen_base、luaopen_package、luaopen_table、luaopen_io、
- //luaopen_os、luaopen_string、luaopen_math、luaopen_debug
- luaL_openlibs(L);
- //压入一个数字20
- lua_pushnumber(L,20);
- //压入一个字符串15
- lua_pushnumber(L,15);
- //压入一个字符串Lua
- lua_pushstring(L,"Lua");
- //压入一个字符串C
- lua_pushstring(L,"C");
- //获取栈中元素个数
- int n=lua_gettop(L);
- //遍历栈中每个元素
- for(int i=1;i<=n;i++)
- {
- //类型判断
- switch(lua_type(L,i))
- {
- case LUA_TSTRING:
- cout << "This value\'s type is string" << endl;
- break;
- case LUA_TNUMBER:
- cout << "This value\'s type is number" << endl;
- break;
- }
- //输出值
- cout << lua_tostring(L ,i) << endl;
- }
- //释放Lua
- lua_close(L);
- }
二、Lua与C++交互
Lua与C++的交互从宿主语言的选择划分上可以分为C++调用Lua和Lua调用C++两中类型:
1、C++调用Lua
使用C++调用Lua时我们可以直接利用C++中的Lua环境来直接Lua脚本,例如我们在外部定义了一个lua脚本文件,我们现在需要使用C++来访问这个脚本该怎么做呢?在这里我们可以使用luaL_loadfile()、luaL_dofile()这两个方法个方法来实现,其区别是前者仅加载脚本文件而后者会在加载的同时调用脚本文件。我们一起来看下面的代码:
- #include <iostream>
- using namespace std;
- #include <iostream>
- extern "C" {
- #include "lua.h"
- #include "lualib.h"
- #include "lauxlib.h"
- }
- using namespace std;
- int main()
- {
- //创建Lua环境
- lua_State* L=luaL_newstate();
- //打开Lua标准库,常用的标准库有luaopen_base、luaopen_package、luaopen_table、luaopen_io、
- //luaopen_os、luaopen_string、luaopen_math、luaopen_debug
- luaL_openlibs(L);
- //下面的代码可以用luaL_dofile()来代替
- //加载Lua脚本
- luaL_loadfile(L,"script.lua");
- //运行Lua脚本
- lua_pcall(L,0,0,0);
- //将变量arg1压入栈顶
- lua_getglobal(L,"arg1");
- //将变量arg2压入栈顶
- lua_getglobal(L,"arg2");
- //读取arg1、arg2的值
- int arg1=lua_tonumber(L,-1);
- int arg2=lua_tonumber(L,-2);
- //输出Lua脚本中的两个变量
- cout <<"arg1="<<arg1<<endl;
- cout <<"arg2="<<arg2<<endl;
- //将函数printf压入栈顶
- lua_getglobal(L,"printf");
- //调用printf()方法
- lua_pcall(L,0,0,0);
- //将函数sum压入栈顶
- lua_getglobal(L,"sum");
- //传入参数
- lua_pushinteger(L,15);
- lua_pushinteger(L,25);
- //调用sum()方法
- lua_pcall(L,2,1,0);//这里有2个参数、1个返回值
- //输出求和结果
- cout <<"sum="<<lua_tonumber(L,-1)<<endl;
- //将表table压入栈顶
- lua_getglobal(L,"table");
- //获取表内a的值
- lua_getfield(L,-1,"a");
- //输出表中元素
- cout <<"table.a="<<lua_tostring(L,-1)<<endl;
- }
- --在Lua中定义两个变量
- arg1=15
- arg2=20
- --在Lua中定义一个表
- table=
- {
- a=25,
- b=30
- }
- --在Lua中定义一个求和的方法
- function sum(a,b)
- return a+b
- end
- --在Lua中定义一个输出的方法
- function printf()
- print("This is a function declared in Lua")
- end
2、Lua调用C++
首先我们在C++中定义一个方法,该方法必须以Lua_State作为参数,返回值类型为int,表示要返回的值的数目。
- static int AverageAndSum(lua_State *L)
- {
- //返回栈中元素的个数
- int n = lua_gettop(L);
- //存储各元素之和
- double sum = 0;
- for (int i = 1; i <= n; i++)
- {
- //参数类型处理
- if (!lua_isnumber(L, i))
- {
- //传入错误信息
- lua_pushstring(L, "Incorrect argument to \'average\'");
- lua_error(L);
- }
- sum += lua_tonumber(L, i);
- }
- //传入平均值
- lua_pushnumber(L, sum / n);
- //传入和
- lua_pushnumber(L, sum);
- //返回值的个数,这里为2
- return 2;
- }
- lua_register(L, "AverageAndSum", AverageAndSum);
- --在Lua中调用C++中定义并且注册的方法
- average,sum=AverageAndSum(20,52,75,14)
- print("Average=".average)
- print("Sum=".sum)
LuaPlus、LuaBind。这样相信大家对于C++中的方法如何在Lua中绑定会有更好的认识吧!
三、Lua与C#交互
首先看下不同版本Lua介绍:
luainterface、nlua、ulua、unilua、cstolua、slua
luainterface:LuaInterface是开源的C#的lua桥接库,配合开源库luanet,能轻松实现Lua,C#相互调用和参数事件传递。但作者仅完成了windows程序的功能实现,跨平台并没有完成,作者于2013年4月30日停止更新luainterface,并推荐大家关注luainterface的一个分支Nlua。Nlua是实现跨平台的Luainterface的升级版,uLua和NLua都是基于此库升级编写
nlua:是LuaInterface的一个分支,继承了Luainterface的所有优点,并将Luanet库功能集成到代码中,实现跨平台(Windows, Linux, Mac, iOS , Android, Windows Phone 7 and 8),对ios平台做了特殊处理,如支持了委托的桥接。
配合NLua有2种Lua实现,第一种是KeraLua,基于原生Lua,将C API 进行简单的包装,使C# 可以方便使用 Lua C API,第二种是KopiLua,C#实现的Lua vm(对,和UniLua一样也是纯C#实现的Lua vm)。以下为关于两种方案的比较。
使用KeraLua,必须将lua 编译成 Unity3D Plugin,并将编译好的文件放到Plugins文件夹下相应的平台文件夹中。并定义#define USE_KERALUA
使用KopiLua,定义#define USE_KERALUA即可
ulua:基于luainterface升级版,uLua = Lua + LuaJIT + LuaInterface,全平台支持。在原生C的基础上使用LuaJit进行加速,如果uLua效率高,LuaJit有很大功劳,作者仅仅提供了uLua插件包,并未提供整套插件源码。此外,作者重写了loadfile、print等api,使用非常简单,导入package,就可以开始编写代码了。
unilua:是云风团队阿南的作品,是lua5.2的C#版,
纯C#的Lua 5.2实现,是不是感觉似曾相识,对的,KopiLua也是纯C#实现的Lua vm,虽然Unilua出名,但是没有KopiLua的配套库好用,其自身同的Ffi库,是实验性质的库,不完善,作者不推荐使用,虽然作者在其商业项目中使用,但是这只是其中一部分代码,Unilua和C#中间层的代码作者并没有开源。UniLua仅仅提供了Lua原生的接口,如果要在Lua代码中调用C#,使用就需要把Luanet 移植到Unilua代码中,总的来说很蛋疼,据推测Unilua方法都是使用Lua标准的命名方式,所以将luanet源码中所有C接口全部手动改写成Unilua 的接口,就可以使用,这个工作量,等闲的时候把玩比较好。
cstolua:cstolua是作者对ulua的扩展,提高了效率
slua:也是从ulua扩展而来,官方说效率比cstolua还高,不过也有很多人质疑过 http://www.ulua.org/cstolua.html http://www.slua.net/ http://www.sineysoft.com/post/164
效率
cstolua > ulua > nlua > luainterface > unilua
既然我们已经知道了C++是怎样和Lua完成交互的,理论上我们可以通过编写dll的方式将前面完成的工作继续在C#中运行,可是这样做我们需要花费大量时间在三种语言之间纠结,因为这样会增加调试的难度。之前有个做coco2dx的朋友抱怨要在C++、Javascript、Lua之间来回跑,我当时没觉得有什么,因为我最困难的时候就是C#和Java项目混合的情形,如今我算是深有体会了啊,这算是报应吗?哈哈,好了,不说这个了,好在C#与Lua的交互目方面前已经有了较好的解决方案,在开源社区我们可以找到很多的支持在C#中调用Lua的工具库,博主这里向大家推荐的是LuaInterface这个开源项目,这个开源项目我找到了两个地址:
1、https://github.com/Jakosa/LuaInterface
2、http://code.google.com/p/luainterface
博主个人感觉这应该是同一个项目,因为两个项目的源代码是一样的,不过从Github上下载的项目在使用的时候会报错,估计是我电脑里的Lua版本和它项目里所用的Lua的版本不一致造成的吧。
LuaInterface中的核心就是C#通过Pinvoke对Lua C库调用的封装,所以,在Unity中,LuaInterface就是C#与Lua进行交互的接口。
Lua是一种很好的扩展性语言,Lua解释器被设计成一个很容易嵌入到宿主程序的库。LuaInterface则用于实现Lua和CLR的混合编程。
LuaInterface.Lua类是CLR访问Lua解释器的主要接口,一个LuaInterface.Lua类对象就代表了一个Lua解释器(或Lua执行环境),Lua解释器可以同时存在多个,并且它们之间是完全相互独立的。
下面的这个项目是可以使用的,博主这里写了一个简单的示例:
- //------------------------------------------------------------------------------
- // <summary>
- // 这是一个用以演示LuaInterface的简单程序,通过LuaInterface我们可以实现在C#与Lua的
- // 的相互通信。Lua是一个轻巧而高效的语言,它可以和任何语言混合使用。Lua语言最初并不是
- // 为游戏开发而诞生,却是因为游戏开发而成名。目前,在世界上有大量的游戏使用了Lua作为它
- // 的脚本语言。如图Unity使用了C#作为它的语言,Lua在游戏开发领域发挥着不可忽视的重要作
- // 用。使用LuaInterface的方法如下:
- // 1.C#
- // 注册Lua中可调用方法:
- // mLua.RegisterFunction(Lua调用方法名, 类, 类.GetMethod(C#方法名));
- // 注:C#不要使用方法级泛型,即 void Fun<T>(string str);,如果使用,系统自动判定T为第一个参数的类型。
- // 加载Lua代码
- // mLua.DoString(Lua代码);
- // mLua.DoFile(Lua文件绝对路径);
- // 调用Lua方法
- // mLua.GetFunction(Lua方法).Call(参数); 注:此处参数不要传递dynamic类型的类,否则Lua中无法获取属性值
- // 2.Lua
- // 调用C#方法时需要先注册注册后按照Lua方法处理
- // </summary>
- //------------------------------------------------------------------------------
- using System;
- using LuaInterface;
- namespace LuaExample
- {
- public class LuaScript
- {
- //定义LuaFile属性以便于从外部调用一个Lua脚本
- private string mLuaFile;
- public string LuaFile {
- get {
- return mLuaFile;
- }
- set {
- mLuaFile = value;
- }
- }
- //Lua虚拟机
- private Lua mLua;
- //构造函数
- public LuaScript ()
- {
- //初始化Lua虚拟机
- mLua=new Lua();
- //注册Printf方法
- mLua.RegisterFunction("Printf",this,this.GetType().GetMethod("Printf"));
- }
- //定义一个C#方法供Lua使用
- public void Printf(string str)
- {
- Console.WriteLine("This Method is Invoked by Lua:" + str);
- }
- //在C#中调用Lua方法
- public void DoFile()
- {
- if(mLuaFile!="")
- //执行Lua脚本中的代码
- mLua.DoFile(mLuaFile);
- }
- //在C#中调用Lau方法
- public void DoString()
- {
- //以字符串形式定义的Lua脚本
- string mFuncString="function Add(a,b) io.write(a+b) end";
- //在Lua中定义该方法
- mLua.DoString(mFuncString);
- //调用该方法
- mLua.GetFunction("Add").Call(4,8);
- }
- //在Lua中调用C#脚本
- public void Invoke()
- {
- //调用注册的Printf方法
- mLua.GetFunction("Printf").Call("Hello Lua");
- }
- }
- }
- using System;
- using LuaInterface;
- namespace LuaExample
- {
- class MainClass
- {
- public static void Main (string[] args)
- {
- //实例化LuaSxript
- LuaScript mLua=new LuaScript();
- //设置LuaFile
- mLua.LuaFile="D:\\test.lua";
- //调用字
请发表评论