在目前游戏中不能使用如何来使用LUA

&nbsp>&nbsp
&nbsp>&nbsp
&nbsp>&nbsp
在游戏中如何来使用LUA
摘要:在游戏中如何来使用LUA是本文要介绍的内容,主要是来学习游戏中lua的使用方法,具体内容的实现来看本文详解。首先,让我来简单的解释一下Lua解释器的工作机制,Lua解释器自身维护一个运行时栈,通过这个运行时栈,Lua解释器向主机程序传递参数,所以我们可以这样来得到一个脚本变量的值:获取脚本的变量的值lua_pushstring(L,&var&);//将变量的名字放入栈lua_gettatbl(L,LUA_GLOBALSINDEX);变量的值现在栈顶假设你在
在游戏中如何来使用LUA是本文要介绍的内容,主要是来学习游戏中lua的使用方法,具体内容的实现来看本文详解。首先,让我来简单的解释一下Lua解释器的工作机制,Lua解释器自身维护一个运行时栈,通过这个运行时栈,Lua解释器向主机程序传递参数,所以我们可以这样来得到一个脚本变量的值:
获取脚本的变量的值lua_pushstring(L,&var&);//将变量的名字放入栈 lua_gettatbl(L,LUA_GLOBALSINDEX);变量的值现在栈顶
假设你在脚本中有一个变量 var = 100
你可以这样来得到这个变量值:intvar=lua_tonumber(L,-1);
怎么样,是不是很简单?
Lua定义了一个宏让你简单的取得一个变量的值:lua_getglobal(L,name)
我们可以这样来取得一个变量的值:lua_getglobal(L,&var&);//变量的值现在栈顶 intvar=lua_tonumber(L,-1);
完整的测试代码如下:#include&lua.h& #inculde&lauxlib.h& #include&lualib.h& intmain(intargc,char*argv[]) { lua_State*L=lua_open(); luaopen_base(L); luaopen_io(L); constchar*buf=&var=100&; lua_dostring(L,buf); lua_getglobal(L,&var&); intvar=lua_tonumber(L,-1); assert(var==100); lua_close(L); return0; }
关于全局变量:
如上面我们所看到的,lua_getglobal()将Lua的一个全局变量放至栈顶,假如我们的脚本包含一个全局变量z,下面这段代码将获取z的值,代码:lua_getglobal(L,&z&); z=(int)lua_tonumber(L,-1); lua_pop(L,1);
与之对应的lua_setglobal()用来设置Lua的一个全局变量的值,下面的这段代码将全局变量z的值设置为10,代码:lua_pushnumber(L,10); lua_setglobal(L,&z&);
注意,不需要在你的Lua脚本中显式的全局变量,如果全局变量不存在,lua_setglobal()将创建一个新的全局变量。
在程序中调用脚本的函数
在你的游戏中应用Lua(1):调用函数
假设你在脚本中定义了一个函数:functionmain(number) numbernumber=number+1 returnnumber end
在你的游戏代码中,你希望在某个时刻调用这个函数取得它的返回值。
在Lua中,函数等同于变量,所以你可以这样来取得这个函数:lua_getglobal(L,&main&);//函数现在栈顶
现在,我们可以调用这个函数,并传递给它正确的参数:lua_pushnumber(L,100);//将参数压栈 lua_pcall(L,1,1,0);//调用函数,有一个参数,一个返回值 //返回值现在栈顶 intresult=lua_tonumber(L,-1);
result 就是函数的返回值
完整的测试代码如下:#include&lua.h& #include&lauxlib.h& #include&lualib.h& intmain(intargc,char*argv[]) { lua_State*L=lua_open(); luaopen_base(L); constchar*buf=&functionmain(number)number=number+1returnnumberend&; lua_dostring(buf); lua_getglobal(L,&main&); lua_pushnumber(L,100); lua_pcall(L,1,1,0); intresult=lua_tonumber(L,-1); assert(result==101); lua_close(L); return0;
在你的游戏中应用Lua(2):扩展Lua
Lua本身定位在一种轻量级的,灵活的,可扩充的脚本语言,这意味着你可以自由的扩充Lua,为你自己的游戏量身定做一个脚本语言。
你可以在主机程序中向脚本提供你自定的api,供脚本调用。
Lua定义了一种类型:lua_CFunction,这是一个函数指针,它的原型是:typedefint(*lua_CFunction)(lua_State*L);
这意味着只有这种类型的函数才能向Lua注册。
首先,我们定义一个函数intfoo(lua_State*L) { //首先取出脚本执行这个函数时压入栈的参数 //假设这个函数提供一个参数,有两个返回值 //getthefirstparameter constchar*par=lua_tostring(L,-1); printf(&%s/n&,par); //pushthefirstresult lua_pushnumber(L,100); //pushthesecondresult lua_pushnumber(L,200); //return2result return2; }
我们可以在脚本中这样调用这个函数r1,r2=foo(&hello&) print(r1..r2)
完整的测试代码如下:#include&lua.h& #include&lauxlib.h& #include&lualib.h& intfoo(lua_State*L) { //首先取出脚本执行这个函数时压入栈的参数 //假设这个函数提供一个参数,有两个返回值 //getthefirstparameter constchar*par=lua_tostring(L,-1); printf(&%s/n&,par); //pushthefirstresult lua_pushnumber(L,100); //pushthesecondresult lua_pushnumber(L,200); //return2result return2; } intmain(intargc,char*argv[]) { lua_State*L=lua_open(); luaopen_base(L); luaopen_io(L); constchar*buf=&r1,r2=foo(&hello&)print(r1..r2)&; lua_dostring(L,buf); lua_close(L); return0; }
程序输出:hello 100200
在你的游戏中应用Lua(3):usingluaincpp
lua和主机程序交换参数是通过一个运行时栈来进行的,运行时栈的信息放在一个lua_State的结构中,lua提供的api都需要一个lua_State*的指针,除了一个:lua_open();
这个函数将返回一个lua_State*型的指针,在你的游戏代码中,你可以仅仅拥有一个这样的指针,也可以有多个这样的指针。
最后,你需要释放这个指针,通过函数:lua_close(L);
注意这个事实,在你的主机程序中,open()与close()永远是成对出现的,在c++中,如果有一些事情是成对出现的,这通常意味着你需要一个构造函数和一个析构函数,所以,我们首先对lua_State做一下封装:#ifndefLUA_EXTRALIBS #defineLUA_EXTRALIBS/*empty*/ #endif staticconstluaL_reglualibs[]= { {&base&,luaopen_base}, {&table&,luaopen_table}, {&io&,luaopen_io}, {&string&,luaopen_string}, {&math&,luaopen_math}, {&debug&,luaopen_debug}, {&loadlib&,luaopen_loadlib}, /*addyourlibrarieshere*/ LUA_EXTRALIBS {NULL,NULL} };
这是lua提供给用户的一些辅助的lib,在使用lua_State的时候,你可以选择打开或者关闭它。
完整的类实现如下://lua_State classstate { public: state(boolbOpenStdLib=false) : err_fn(0) { L=lua_open(); assert(L); if(bOpenStdLib) { open_stdlib(); } } ~state() { lua_setgcthreshold(L,0); lua_close(L); } voidopen_stdlib() { assert(L); constluaL_reg*lib= for(;lib-&lib++) { lib-&func(L);/*openlibrary*/ lua_settop(L,0);/*discardanyresults*/ } } lua_State*get_handle() { returnL; } interror_fn() { returnerr_ } private: lua_State*L; interr_ };
通常我们仅仅在游戏代码中使用一个lua_State*的指针,所以我们为它实现一个单件,默认打开所有lua提供的lib://returntheglobalinstance state*lua_state() { staticstateL(true); return&;L; }
在你的游戏中应用Lua(3):using lua in cpp(封装栈操作)
前面提到了lua与主机程序是通过一个运行时栈来交换信息的,所以我们把对栈的访问做一下简单的封装。
我们利用从c++的函数重载机制对这些操作做封装,重载提供给我们一种以统一的方式来处理操作的机制。
向lua传递信息是通过压栈的操作来完成的,所以我们定义一些Push()函数:inlinevoidPush(lua_State*L,intvalue); inlinevoidPush(lua_State*L,boolvalue);
对应简单的c++内建类型,我们实现出相同的Push函数,至于函数内部的实现是非常的简单,只要利用lua提供的api来实现即可,例如:inlinevoidPush(lua_State*L,intvalue) { lua_pushnumber(L,value); }
这种方式带来的好处是,在我们的代码中我们可以以一种统一的方式来处理压栈操作,如果有一种类型没有定义相关的压栈操作,将产生一个编译期错误。
后面我会提到,如何将一个用户自定义类型的指针传递到lua中,在那种情况下,我们的基本代码无须改变,只要添加一个相应的Push()函数即可。
记住close-open原则吧,它的意思是对修改是封闭的,对扩充是开放的,好的类库设计允许你扩充它,而无须修改它的实现,甚至无须重新编译。
《c++泛型设计新思维》一书提到了一种技术叫type2type,它的本质是很简单:template&typenameT&structtype2type { typedefTU; };
正如你看到的,它并没有任何数据成员,它的存在只是为了携带类型信息。
类型到类型的映射在应用于重载函数时是非常有用的,应用type2type,可以实现编译期的分派。
下面看看我们如何在从栈中取得lua信息时应用type2type:
测试类型:由于lua的类型系统与c++是不相同的,所以,我们要对栈中的信息做一下类型检测。inlineboolMatch(type2type&bool&,lua_State*L,intidx) { returnlua_type(L,idx)==LUA_TBOOLEAN; }
类似的,我们要为cpp的内建类型提供相应的Match函数:inlineboolMatch(type2type&int&,lua_State*L,intidx); inlineboolMatch(type2type&constchar*&,lua_State*L,intidx);
可以看出,type2type的存在只是为了在调用Match时决议到正确的函数上,由于它没有任何成员,所以不存在运行时的成本。
同样,我们为cpp内建类型提供Get()函数:inlineboolGet(type2type&bool&,lua_State*L,intidx) { returnlua_toboolean(L,idx); } inlineintGet(type2type&int&,lua_State*L,intidx) { returnstatic_cast&int&(lua_tonumber(L,idx)); }
我想你可能注意到了,在int Get(type2type&int&)中有一个转型的动作,由于lua的类型系统与cpp的类型不同,所以转型动作必须的。
除此之外,在Get重载函数(s)中还有一个小小的细节,每个Get的函数的返回值是不相同的,因为重载机制是依靠参数的不同来识别的,而不是返回值。
前面说的都是一些基础的封装,下来我们将介绍如何向lua注册一个多参数的c函数。还记得吗?利用lua的api只能注册int (*ua_CFunction)(lua_State *)型的c函数,别忘记了,lua是用c写的。
在你的游戏中应用Lua(3):using lua in cpp(注册不同类型的c函数)之一
前面说到,我们可以利用lua提供的api,向脚本提供我们自己的函数,在lua中,只有lua_CFunction类型的函数才能直接向lua注册,lua_CFunction实际上是一个函数指针:typedefint(*lua_CFunction)(lua_State*L);
而在实际的应用中,我们可能需要向lua注册各种参数和返回值类型的函数,例如,提供一个add脚本函数,返回两个值的和:intadd(intx,inty);
为了实现这个目的,首先,我们定义个lua_CFunction类型的函数:intadd_proxy(lua_State*L) { //取得参数 if(!Match(TypeWrapper&int&(),L,-1)) return0; if(!Match(TypeWrapper&int&(),L,-2)) return0; intx=Get(TypeWrapper&int&(),L,-1); inty=Get(TypeWrapper&int&(),L,-1); //调用真正的函数 intresult=add(x,y); //返回结果 Push(result); return1; }
现在,我们可以向lua注册这个函数:lua_pushstring(L,“add”); lua_pushcclosure(L,add_proxy,0); lua_settable(L,LUA_GLOBALINDEX);
在脚本中可以这样调用这个函数:print(add(100,200))
从上面的步骤可以看出,如果需要向lua注册一个非lua_CFunction类型的函数,需要:
1、为该函数实现一个封装调用。
2、在封装调用函数中从lua栈中取得提供的参数。
3、使用参数调用该函数。
4、向lua传递其结果。
注意,我们目前只是针对全局c函数,类的成员函数暂时不涉及,在cpp中,类的静态成员函数与c函数类似。
假设我们有多个非lua_CFunction类型的函数向lua注册,我们需要为每一个函数重复上面的步骤,产生一个封装调用,可以看出,这些步骤大多是机械的,因此,我们需要一种方式自动的实现上面的步骤。
首先看步骤1,在cpp中,产生这样一个封装调用的函数的最佳的方式是使用template,我们需要提供一个lua_CFunction类型的模板函数,在这个函数中调用真正的向脚本注册的函数,类似于这样:template&typenameFunc&inlineintregister_proxy(lua_State*L)
现在的问题在于:我们要在这个函数中调用真正的函数,那么我们必须要在这个函数中取得一个函数指针,然而,lua_CFunction类型的函数又不允许你在增加别的参数来提供这个函数指针,现在该怎么让regisger_proxy函数知道我们真正要注册的函数呢?
在oop中,似乎可以使用类来解决这个问题:template&Func&structregister_helper { explicitregister_helper(Funcfn):m_func(fn) {} intregister_proxy(lua_State*L); protected: Funcm_ };
可是不要忘记,lua_CFunction类型指向的是一个c函数,而不是一个成员函数,他们的调用方式是不一样的,如果将上面的int register_proxy()设置为静态成员函数也不行,因为我们需要访问类的成员变量m_
让我们再观察一下lua_CFunction类型的函数:intregister_proxy(lua_State*L);
我们看到,这里面有一个lua_State*型的指针,我们能不能将真正的函数指针放到这里面存储,到真正调用的时候,再从里面取出来呢?
Lua提供了一个api可以存储用户数据:Lua_newuserdata(L,size)
在适当的时刻,我们可以再取出这个数据:lua_touserdata(L,idx)
ok,现在传递函数指针的问题我们已经解决了,后面再看第二步:取得参数。
在你的游戏中应用Lua(3):using lua in cpp(注册不同类型的c函数)之二
在解决了传递函数指针的问题之后,让我们来看看调用函数时会有一些什么样的问题。
首先,当我们通过函数指针调用这个函数的时候,由于我们面对的是未知类型的函数,也就是说,我们并不知道参数的个数,参数的类型,还有返回值的类型,所以我们不能直接从lua栈中取得参数,当然,我们可以通过运行时测试栈中的信息来得到lua传递进来的参数的个数和类型,这意味着我们在稍后通过函数指针调用函数时也需要动态的根据参数的个数和类型来决议到正确的函数,这样,除了运行时的成本,cpp提供给我们的强类型检查机制的好处也剩不了多少了,我们需要的是一种静态的编译时的“多态”。
在cpp中,至少有两种方法可以实现这点。最直接简单的是使用函数重载,还有一种是利用模板特化机制。
简单的介绍一下模板特化:
在cpp中,可以针对一个模板函数或者模板类写出一些特化版本,编译器在匹配模板参数时会寻找最合适的一个版本。类似于这样:templat&typenameT&Tfoo() { Ttmp(); } //提供特化版本 template&&intfoo() { return100; }
在main()函数中,我们可以显示指定使用哪个版本的foo:intmain(intargc,char**argv) { cout&&foo&int&()&& return0; }
程序将输出100,而不是0,以上代码在 g++中编译通过,由于vc6对于模板的支持不是很好,所以有一些模板的技术在vc6中可能不能编译通过。
所以最好使用重载来解决这个问题,在封装函数调用中,我们首先取得这个函数指针,然后,我们要提供一个Call函数来真正调用这个函数,类似于这样://伪代码 intCall(pfn,lua_State*L,intidx)
可是我们并不知道这个函数指针的类型,现在该怎么写呢?别忘记了,我们的register_proxy()是一个模板函数,它有一个参数表示了这个指针的类型:template&typenameFunc&intregister_proxy(lua_State*L) { //伪代码,通过L参数取得这个指针 unsignedchar*buffer=get_pointer(L); //对这个指针做强制类型转化,调用Call函数 returnCall(*(Func*)buffer,L,1); }
由重载函数Call调用真正的函数,这样,我们可以使用lua api注册相关的函数,下来我们提供一个注册的函数:template&typenameFunc&voidlua_pushdirectclosure(Funcfn,lua_State*L,intnUpvalue) { //伪代码,向L存储函数指针 save_pointer(L); //向lua提供我们的register_proxy函数 lua_pushcclosure(L,register_proxy&Func&,nUpvalue+1); }
再定义相关的注册宏:#definelua_register_directclosure(L,func)/ lua_pushstring(L,#func); lua_pushdirectclosure(func,L,1); lua_settable(L,LUA_GLOBALINDEX)
现在,假设我们有一个int add(int x, int y)这样的函数,我们可以直接向lua注册:lua_register_directclosure(L,add);
看,最后使用起来很方便吧,我们再也不用手写那么多的封装调用的代码啦,不过问题还没有完,后面我们还得解决Call函数的问题。
在你的游戏中应用Lua(3):using lua in cpp(注册不同类型的c函数)之三
下面,让我们集中精力来解决Call重载函数的问题吧。
前面已经说过来,Call重载函数接受一个函数指针,然后从lua栈中根据函数指针的类型,取得相关的参数,并调用这个函数,然后将返回值压入lua栈,类似于这样://伪代码 intCall(pfn,lua_State*L,intidx)
现在的问题是pfn该如何声明?我们知道这是一个函数指针,然而其参数,以及返回值都是未知的类型,如果我们知道返回值和参数的类型,我们可以用一个typedef来声明它:typedefvoid(*pfn)(); intCall(pfnfn,lua_State*L,intidx);
我们知道的返回值以及参数的类型只是一个模板参数T,在cpp中,我们不能这样写:template&typenameT&typedefT(*Func)();
一种解决办法是使用类模板:template&typenameT&structCallHelper { typedefT(*Func)(); };
然后在Call中引用它:template&typenameT&intCall(typenameCallHelper::Funcfn,lua_State*L,intidx)
注意typename关键字,如果没有这个关键字,在g++中会产生一个编译警告,它的意思是告诉编译器,CallHelper::Func是一个类型,而不是变量。
如果我们这样来解决,就需要在CallHelper中为每种情况大量定义各种类型的函数指针,还有一种方法,写法比较古怪,考虑一个函数中参数的声明:void(intn);
首先是类型,然后是变量,而应用于函数指针上:typedefvoid(*pfn)(); void(pfnfn);
事实上,可以将typedef直接在参数表中写出来:void(void(*pfn)());
这样,我们的Call函数可以直接这样写://针对没有参数的Call函数 template&typenameRT&intCall(RT(*Func)(),lua_State*L,intidx); { //调用Func RTret=(*Func)(); //将返回值交给lua Push(L,ret); //告诉lua有多少个返回值 return1; } //针对有一个参数的Call template&typenameT,typenameP1&intCall(RT(*Func)(),lua_State*L,intidx) { //从lua中取得参数 if(!Match(TypeWrapper&P1&(),L,-1) return0; RTret=(*Func)(Get(TypeWrapper&P1&(),L,-1)); Push(L,ret); return1; }
按照上面的写法,我们可以提供任意参数个数的Call函数,现在回到最初的时候,我们的函数指针要通过lua_State *L来存储,这只要利用lua提供的api就可以了,还记得我们的lua_pushdirectclosure函数吗:template&typenameFunc&voidlua_pushdirectclosure(Funcfn,lua_State*L,intnUpvalue) { //伪代码,向L存储函数指针 save_pointer(L); //向lua提供我们的register_proxy函数 lua_pushcclosure(L,register_proxy&Func&,nUpvalue+1); }
其中,save_pointer(L)可以这样实现:voidsave_pointer(lua_State*L) { unsignedchar*buffer=(unsignedchar*)lua_newuserdata(L,sizeof(func)); memcpy(buffer,&;func,sizeof(func)); }
而在register_proxy函数中:template&typenameFunc&intregister_proxy(lua_State*L) { //伪代码,通过L参数取得这个指针 unsignedchar*buffer=get_pointer(L); //对这个指针做强制类型转化,调用Call函数 returnCall(*(Func*)buffer,L,1); } get_pointer函数可以这样实现: unsignedchar*get_pointer(lua_State*L) { return(unsignedchar*)lua_touserdata(L,lua_upvalueindex(1)); }
这一点能够有效运作主要依赖于这样一个事实:
我们在lua栈中保存这个指针之后,在没有对栈做任何操作的情况下,又把它从栈中取了出来,所以不会弄乱lua栈中的信息,记住,lua栈中的数据是由用户保证来清空的。
到现在,我们已经可以向lua注册任意个参数的c函数了,只需简单的一行代码:lua_register_directclosure(L,func)就可以啦
在你的游戏中应用Lua(3):Using Lua in cpp(基本数据类型、指针和引用)之一UsingLuaincpp(基本数据类型、指针和引用)
前面介绍的都是针对cpp中的内建基本数据类型,然而,即使是这样,在面对指针和引用的时候,情况也会变得复杂起来。
使用前面我们已经完成的宏lua_register_directclosure只能注册by value形式的参数的函数,当参数中存在指针和引用的时候(再强调一次,目前只针对基本数据类型):
1、如果是一个指针,通常实现函数的意图是以这个指针传递出一个结果来。
2、如果是一个引用,同上。
3、如果是一个const指针,通常只有面对char*的时候才使用const,实现函数的意图是,不会改变这个参数的内容。其它情况一般都避免出现使用const指针。
4、如果是一个const引用,对于基本数据类型来说,一般都避免出现这种情况。
Lua和cpp都允许函数用某种方式返回多个值,对于cpp来说,多个返回值是通过上述的第1和第2种情况返回的,对于lua来说,多个返回值可以直接返回:--inLua functionswap(x,y) tmp=xx=yy=tmpreturnx,y end x=100y=200x,y=swap(x,y) print(x..y) 程序输出:200100
同样的,在主机程序中,我们也可以向Lua返回多个值:intswap(lua_State*L) { //取得两个参数 intx=Get(TypeWrapper&int&(),L,-1); inty=Get(TypeWrapper&int&(),L,-2); //交换值 inttmp=x; x=y; y= //向Lua返回值 Push(L,x); Push(L,y); //告诉Lua我们返回了多少个值 return2; }
现在我们可以在Lua中这样调用这个函数:x=100y=200x,y=swap(x,y)
在我们的register_proxy函数中只能对基本数据类型的by value方式有效,根据我们上面的分析,如果我们能够在编译期知道,对于一个模板参数T:
1、这是一个基本的数据类型,还是一个用户自定义的数据类型?
2、这是一个普通的指针,还是一个iterator?
3、这是一个引用吗?
4、这是一个const 普通指针吗?
5、这是一个const 引用吗?
如果我们能知道这些,那么,根据我们上面的分析,我们希望:(只针对基本数据类型)
1、 如果这是一个指针,我们希望把指针所指的内容返回给Lua。
2、 如果这是一个引用,我们希望把引用的指返回给Lua。
3、 如果这是const指针,我们希望将从Lua栈中取得的参数传递给调用函数。 4、 如果这是一个const引用,我们也希望把从Lua栈中取得的参数传递给调用函数。
小结:在游戏中如何来使用LUA的内容介绍完了,希望通过本文的学习能对你有所帮助!
以上是的内容,更多
的内容,请您使用右上方搜索功能获取相关信息。
若你要投稿、删除文章请联系邮箱:zixun-group@service.aliyun.com,工作人员会在五个工作日内给你回复。
云服务器 ECS
可弹性伸缩、安全稳定、简单易用
&40.8元/月起
预测未发生的攻击
&24元/月起
为您提供0门槛上云实践机会
你可能还喜欢
你可能感兴趣
阿里云教程中心为您免费提供
在游戏中如何来使用LUA相关信息,包括
的信息,所有在游戏中如何来使用LUA相关内容均不代表阿里云的意见!投稿删除文章请联系邮箱:zixun-group@service.aliyun.com,工作人员会在五个工作日内答复
售前咨询热线
支持与服务
资源和社区
关注阿里云
International51CTO旗下网站
漫话Lua:在游戏中崛起之后
这个热门语言何去何从?
我其实是非常不想讨论编程语言的好坏的,一个是因为这本身无法定论,就好像非要争论到底是中文好还是英文好一样;另一个是一旦有人谈论程序语言,必然各种声音四起,导致没完没了的“战争”。把以前关于语言的争论打印出来,恐怕可以盖一座摩天大楼了。以下内容只是我个人观点,存在偏见和误解还请原谅,如果有不同观点,可以讨论,我保留态度。
作者:蔡毅恒来源:51cto.com| 09:22
我其实是非常不想讨论编程语言的好坏的,一个是因为这本身无法定论,就好像非要争论到底是中文好还是英文好一样;另一个是一旦有人谈论程序语言,必然各种声音四起,导致没完没了的&战争&。把以前关于语言的争论打印出来,恐怕可以盖一座摩天大楼了。以下内容只是我个人观点,存在偏见和误解还请原谅,如果有不同观点,可以讨论,我保留态度。
既然要聊Lua,那么首先需要介绍一下它,Lua是一门设计优雅,轻量、易扩展的可嵌入式脚本语言。提起它,但凡使用过的朋友都会联想到这么几个关键词:轻量、快速、可嵌入等等。
一门语言想要流行,很大程度上并不取决于语言本身,而是由行业决定的。近年来Lua的流行,不得不承认,很大程度上是因为魔兽世界使用它所带来的影响,但是这也只是达成了大家使用它的前提,如果不是Lua自身的一些特点让大家觉得值得用它,Lua也不会有现在这么火爆。
我觉得Lua在游戏领域以及嵌入式设备上能够获得那么多人的支持,最主要的原因有3点:
首先是Lua足够的小。有人说小也能够成为用它的理由吗?在别的领域可能很难让人信服,但是在编写程序上,这绝对有说服力。Lua官网发布的版本,比如Lua5.1,实现的内容包括整个Lua的核心加上几个基本的库,整个实现也就只有2万多行代码,代码量如此的精简,让人不得不佩服Lua作者在追求语言的简练性上所做出的努力。Lua源码采用C语言实现,能够非常容易地嵌入到C\C++的程序中,因为Lua的小巧,你可以根据自己需要来调整Lua的源码,让它满足自己程序的要求,在阅读源码的时候,可以非常清楚得弄明白,语言的内部到底在做些什么,而不必去担心因为引入它而出现一些意想不到的bug。
第二个使用Lua的原因是它极佳的可移植性。因为Lua使用ANSI C编写而成,这使得它天生就具备极佳的可移植性,我们能够在各种设备的开发上使用它,比如目前最火的手机软件的开发,国内流行的手游开发模式cocos2dx + Lua也证明了这一点。相信在不久的将来,我们能够在更多的设备开发中发现Lua的身影。
另一个重要的原因我认为是Lua从5.0版本后使用了MIT协议进行发布,这使得几乎所有人都可以把它放进自己的产品中,而不用去担心版权的问题,至少对于商业软件来说,这一点属于必须考虑的问题之一。使用它会不会有法律纠纷,修改它有没有那么自由,这些也是一个有责任的程序员所必须面对的问题。
Lua语言受到这么多开发者的拥戴,在我看来也是十分正常的。一门程序语言能不能得到使用者的喜爱,最重要的一个标准就是能否拿它实现自己想要的目标。Lua的小巧,代码的精炼,使得它相对于其他庞大的脚本语言来说有着极大的优势,这门语言是否剔除了不必要的冗余结构,是否干净、整洁、KISS,这都是非常重要的,它内部实现的各个模块是否逻辑正交,是否已经达到最简。作为一名程序员,我当然希望我所使用的语言如同数学公理系统一样完美,满足相容、独立、完备的性质。当然,我不是说Lua达到了这样的标准,在我使用过的所有语言当中,我也找不到满足这样标准的语言,但是Lua精简的源码,却是让人眼前一亮。
不得不说目前Lua用的最火的地方,还是在游戏开发上(当然,像Adobe Photoshop Lightroom这样大量使用Lua的软件也不在少数)。我们看到除了《魔兽世界》、《孤岛危机》这样的PC端大作使用它以外,像《愤怒的小鸟》以及网上流行的开源版本的《Flappy Bird》也使用Lua作为脚本来处理从逻辑到UI的各种工作。在我自己的项目当中,也大量的使用到了Lua,无论是作为服务端的逻辑,还是客户端的UI处理,Lua的优势都显而易见,它的各种语言特性让人处处惊喜。Lua的语法虽然谈不上极为简洁,但是写起来是十分舒服的,而且让看代码的人也不会很痛苦,心理负担相对较小;Lua中最重要的数据类型table类型,也让人在使用的时候有一种发现宝藏的感觉,table的实现方式采用数组和散列表的组合,无论是查询效率还是插入效率,都让人满意,至少在处理一般逻辑问题上,table的描述能力和性能是十分强大的;作为一门动态类型语言,Lua的gc处理以及弱引用机制也让人印象深刻;在面向对象方面,Lua自身所具备的机制(比如元表)提供了实现面向对象编程的多种途径;还有Lua的协程机制,对于编写并行逻辑是非常有用的,它让我们可以用同步的方式写出异步回调的逻辑,减少学习的时间,降低使用的成本。Lua的性能也是它的一大亮点,基于寄存器的虚拟机本来是Lua作者的一次尝试,但是结果证明,这是成功的。Lua还有各种各样的特点,比如优秀的C API等等,所以我觉得,在未来,Lua将会继续在各个设备和领域得到广泛的运用。
因为最近Lua的火爆,有人拿它和Javascript做对比,既然编辑也问到这个问题,我也说一下我的看法,Javascript的火爆是有目共睹的,在github上,js的代码项目无疑是最多,自从存在Web应用以来,js就一直被人们所关注,并在不断的发展壮大当中,现在越来越多的非Web应用也采用了js来编写,它无疑是一种被大家认可且喜爱的语言,它已经被证明拥有构建大规模复杂程序的能力。它和Lua有许多相似之处,也有大量的不同,这源于js的设计目的本来就和Lua是有所差异的,Lua的作者也曾说过,Lua并非是为了设计成为主流的编程语言,但在嵌入C\C++程序,或者是作为API的封装以及作为宿主程序和逻辑层之间的粘合剂,Lua有着天然的优势。到底是使用js还是选择Lua则要根据它们自身的特点以及自己所面对的应用场景来定。
前面谈的,其实在各种资料和业内新闻以及博客中都能够看到,为了表示我没有敷衍了事,下面说点我对Lua未来的想法。
Lua目前被大家广泛使用,有一部分原因是因为它强大的性能,我们可以在网上看到各种语言和Lua比速度、比性能的报告,但是在未来,随着计算机运算速度的提升,我相信我们考虑性能问题会越来越弱化,这并不是说性能问题不重要,在任何时候,追求性能的卓越都是值得鼓励的,而且在某些方面,性能是越快越好,比如一些数值运算或者是图形的渲染处理等等。但是作为程序员,更多的去关注程序的逻辑,把性能问题交给编译器才是我理想中的情况。在这种弱化性能问题的情况下,Lua能否继续被广泛使用呢?这是我的第一个考虑。
其次,在未来,我相信大多数语言的核心都会被设计的精简、强壮,而各种各样的库才是我们大家关注的焦点,当程序库成为比语言核心更为重要的东西的时候,一个很明显的例子就是python语言,python有着各种各样丰富多彩的程序库,我身边的朋友使用python的时候,从来就不操心有功能没办法实现,因为已经有大量可用的程序库可以选择,而相比下来,Lua的程序库就要少了许多,Lua的能力更多的是依靠它的宿主语言赋予的,那么将来Lua能否拥有一些可供选择,不需要重新造轮子,完善的程序库供我们使用呢?这是我的第二个考虑。
第三个考虑是,在Lua广泛使用之前,使用者的人数比较少,Lua的作者可以对语言进行大刀阔斧的修改,而不会引起大规模的恐慌或不满,每个用户都可以向作者提出自己的意见和方案,就好像拍美剧一样,每一集都由不同的编剧来写,但是最终由总编剧来把握整个剧情的发展,Lua的一切修改,最终都由Lua的作者来决定并实现,用户数少的时候这没有问题,但是当用户数量增多之后,这种发布方式能否跟得上现代开源软件的发展趋势呢?比如Lua5.2和LuaJIT的分裂就让我们痛心疾首,而Lua各个版本之间的不兼容也让我们寝食难安,我怀着良好的心态相信这最终会得到解决。
最后我希望Lua的社区能够更加活跃,更加团结一些,由于Lua的核心非常精简,而且提供了强大的可扩展性,目前很难统一或者是规划Lua的方方面面,举个例子,比如用Lua实现面向对象的方法,就有好多个不同的版本,这些风格分裂的代码根本无法统一起来,这让Lua的初学者比较苦恼,当然这也会激发各种各样的灵感,毕竟语言定要处在发展当中才有生命力。
在新的一年中,我相信Lua的使用度会越来越高,而且随着可穿戴式设备的火热,我们将会在这些领域也能看到Lua的身影。当然,在游戏行业,Lua天生可扩展和性能良好的语言特性,让它成为C\C++编写的游戏程序,去选择脚本语言的首要考虑,今年这个趋势应该不会改变,而且随着移动端游戏的火热开发,使用Lua的人数将会越来越多,我想Lua的作者恐怕是想象不到,有一天,这门语言会受到如此大范围的关注。
【编辑推荐】
【责任编辑: TEL:(010)】
大家都在看猜你喜欢
热点头条关注头条头条
24H热文一周话题本月最赞
讲师:427108人学习过
讲师:207523人学习过
讲师:12981人学习过
精选博文论坛热帖下载排行
Linux主要用于架设网络服务器。如今关于服务器和网站被黑客攻击的报告几乎每天都可以见到,而且随着网络应用的丰富多样,攻击的形式和方法...
订阅51CTO邮刊

我要回帖

更多关于 目前游戏内不能使用 的文章

 

随机推荐