在你的游戏中应用LUA.docx

上传人:b****5 文档编号:6740898 上传时间:2023-01-09 格式:DOCX 页数:25 大小:56.51KB
下载 相关 举报
在你的游戏中应用LUA.docx_第1页
第1页 / 共25页
在你的游戏中应用LUA.docx_第2页
第2页 / 共25页
在你的游戏中应用LUA.docx_第3页
第3页 / 共25页
在你的游戏中应用LUA.docx_第4页
第4页 / 共25页
在你的游戏中应用LUA.docx_第5页
第5页 / 共25页
点击查看更多>>
下载资源
资源描述

在你的游戏中应用LUA.docx

《在你的游戏中应用LUA.docx》由会员分享,可在线阅读,更多相关《在你的游戏中应用LUA.docx(25页珍藏版)》请在冰豆网上搜索。

在你的游戏中应用LUA.docx

在你的游戏中应用LUA

在你的游戏中应用LUA(ZT)

-

在你的游戏中应用Lua

(1):

在你的游戏代码中运行解释器

  通常,你希望在你的游戏开始的时候读取一些信息,以配置你的游戏,这些信息通常都是放到一个文本文件中,在你的游戏启动的时候,你需要打开这个文件,然后解析字符串,找到所需要的信息。

  是的,或许你认为这样就足够了,为什么还要使用Lua呢?

  应用于“配置”这个目的,Lua提供给你更为强大,也更为灵活的表达方式,在上一种方式中,你无法根据某些条件来配置你的游戏,Lua提供给你灵活的表达方式,你可以类似于这样来配置你的游戏:

ifplayer:

is_dead()then

do_something()

else

do_else()

end

更为重要的是,在你做了一些修改之后,完全不需要重新编译你的游戏代码。

通常,在游戏中你并不需要一个单独的解释器,你需要在游戏来运行解释器,下面,让我们来看看,如何在你的代码中运行解释器:

//这是lua所需的三个头文件

//当然,你需要链接到正确的lib

#include"lua.h"

#include"lauxlib.h"

#include"lualib.h"

intmain(intargc,char*argv[])

{

lua_State*L=lua_open();

luaopen_base(L);

luaopen_io(L);

constchar*buf="print('hello,world!

')";

lua_dostring(buf);//luaL_dostring(L,buf);原文有问题

lua_close(L);

return0;

}

程序输出:

hello,world!

有时你需要执行一段字符串,有时你可能需要执行一个文件,当你需要执行一个文件时,你可以这么做:

lua_dofile(L,"test.lua");

看,非常简单吧。

在你的游戏中应用Lua

(1):

GettingValue

在上一篇文章我们能够在我们的游戏代码中执行Lua解释器,下面让我们来看看如何从脚本中取得我们所需要的信息。

首先,让我来简单的解释一下Lua解释器的工作机制,Lua解释器自身维护一个运行时栈,通过这个运行时栈,Lua解释器向主机程序传递参数,所以我们可以这样来得到一个脚本变量的值:

lua_pushstring(L,"var");//将变量的名字放入栈

lua_gettatble(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);//luaL_dostring

lua_getglobal(L,"var");

intvar=lua_tonumber(L,-1);

assert(var==100);

lua_close(L);

return0;

}

在你的游戏中应用Lua

(1):

调用函数

假设你在脚本中定义了一个函数:

functionmain(number)

number=number+1

returnnumber

end

在你的游戏代码中,你希望在某个时刻调用这个函数取得它的返回值。

在Lua中,函数等同于变量,所以你可以这样来取得这个函数:

lua_getglobal(L,"main");//函数现在栈顶

#definelua_getglobal(L,s)lua_getfield(L,LUA_GLOBALSINDEX,(s))

#defineLUA_GLOBALSINDEX(-10002)

取表中的元素

voidlua_getfield(lua_State*L,intindex,constchar*k)

操作:

arr=Stack[index]//arr肯定是表

Stack.push(arr[k])

取表中键为k的元素,这里的表是由index指向的栈上的一个表

无返回值

栈高度+1,栈顶元素是(Stack[index])[k]注意,该操作将触发__index元方法

现在,我们可以调用这个函数,并传递给它正确的参数:

lua_pushnumber(L,100);//将参数压栈

lua_pcall(L,1,1,0);//调用函数,有一个参数,一个返回值

保护下调用一个lua函数

intlua_pcall(lua_State*L,intnargs,intnresults,interrfunc)

参数,行为和lua_call都一样,如果在调用中没有发生任何错误,lua_pcall==lua_call;但是如果有错误发生时,lua_pcall会捕获它

errfunc指出了Stack上的一个元素,这个元素应该是一个函数,当发生错误的时候

ef=Stack[errfunc]

value=ef(errmsg)

Stack.push(value)

也就是说,在错误的时候,errfunc指定的错误处理函数会被调用,该处理函数的返回值被压到栈上.

默认情况下,可以给errfunc传值0,实际的效果是指定了这样一个函数做出错处理functiondefaulterr(errmsg)returnerrmsgend.

本函数有返回值LUA_ERRRUN运行时错误LUA_ERRMEM内存分配错误[注意,这种错会导致lua调用不了错误处理函数]LUA_ERRERR运行错误处理函数时出错了,写程序的时候必须检查返回值:

强烈推荐该函数,不过事实上大家也都用的这个函数:

//返回值现在栈顶

intresult=lua_tonumber(L,-1);

为什么栈顶是-1?

Lua使用一个虚拟栈来和C传递值。

栈上的的每个元素都是一个Lua值(nil,数字,字符串,等等)。

无论何时Lua调用C,被调用的函数都得到一个新的栈,这个栈独立于C函数本身的堆栈,也独立于以前的栈。

(译注:

在C函数里,用LuaAPI不能访问到Lua状态机中本次调用之外的堆栈中的数据)它里面包含了Lua传递给C函数的所有参数,而C函数则把要返回的结果也放入堆栈以返回给调用者(参见lua_CFunction)。

方便起见,所有针对栈的API查询操作都不严格遵循栈的操作规则。

而是可以用一个索引来指向栈上的任何元素:

正的索引指的是栈上的绝对位置(从一开始);负的索引则指从栈顶开始的偏移量。

更详细的说明一下,如果堆栈有n个元素,那么索引1表示第一个元素(也就是最先被压入堆栈的元素)而索引n则指最后一个元素;索引-1也是指最后一个元素(即栈顶的元素),索引-n是指第一个元素。

如果索引在1到栈顶之间(也就是,1≤abs(index)≤top)我们就说这是个有效的索引。

-------------------------------------------------------------------------------------------

当你使用LuaAPI时,就有责任保证其坚固性。

特别需要注意的是,你有责任控制不要堆栈溢出。

你可以使用lua_checkstack这个函数来扩大可用堆栈的尺寸。

无论何时Lua调用C,它都只保证LUA_MINSTACK这么多的堆栈空间可以使用。

LUA_MINSTACK一般被定义为20 ,因此,只要你不是不断的把数据压栈,通常你不用关心堆栈大小。

所有的查询函数都可以接收一个索引,只要这个索引是任何栈提供的空间中的值。

栈能提供的最大空间是通过lua_checkstack来设置的。

这些索引被称作可接受的索引,通常我们把它定义为:

(index<0&&abs(index)<=top)||(index>0&&index<=stackspace)

注意,0永远都不是一个可接受的索引。

(译注:

下文中凡提到的索引,没有特别注明的话,都指可接受的索引。

result就是函数的返回值

完整的测试代码如下:

#include"lua.h"

#include"lauxlib.h"

#include"lualib.h"

intmain(intargc,char*argv[])

{

lua_State*L=lua_open();//创建一个lua_State

luaopen_base(L);

constchar*buf="functionmain(number)number=number+1returnnumberend";

lua_dostring(buf);//压栈,并且会生成表?

lua_getglobal(L,"main");//把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_CFunction

typedefint(*lua_CFunction)(lua_State*L);

TypeforCfunctions.

Inorderto(为了)communicateproperlywithLua(更好的与lua通讯),aCfunctionmustusethefollowingprotocol(C函数必须用以下协议),whichdefinesthewayparametersandresultsarepassed(它定义了传递参数和结果的方式):

aCfunctionreceivesitsargumentsfromLuainitsstackindirectorder(thefirstargumentispushedfirst)【一个C函数是顺序接收来自LUA的栈中的参数(即在LUA中调用C函数时,传入的参数)(第一个参数第一个压入)】.So,whenthefunctionstarts(所以,当一个函数在LUA中被调用),lua_gettop(L)returnsthenumberofargumentsreceivedbythefunction【lua_gettop(L)返回接收自这个LUA的栈中的所有参数(Lua调用C函数就会传入参数,指的就是这个东西)】.Thefirstargument(ifany若有的话)isatindex1【第一个参数为索引1】anditslastargumentisatindexlua_gettop(L)【并且最后一个参数也在这个索引中】.ToreturnvaluestoLua(执行C函数最终产生的值也会返回值给LUA),aCfunctionjustpushesthemontothestack(C函数只压入它们到LUA的栈),indirectorder(thefirstresultispushedfirst)(按顺序,第一个结果,第一个压入),andreturnsthenumberofresults(并且把返回的结果个数也压入LUA的栈).AnyothervalueinthestackbelowtheresultswillbeproperlydiscardedbyLua【除此以外的数据都被LUA丢弃】.LikeaLuafunction(像一个Lua函数),aCfunctioncalledbyLuacanalsoreturnmanyresults(LUA中调用一个C函数也能返回多个结果).{英语喜欢省略主语}

Asanexample,thefollowingfunctionreceivesavariablenumberofnumericalargumentsandreturnstheiraverageandsum:

(看一个例子,下面的函数接受一个数值参数,并返回他们的平均和可变数量的总和)

staticintfoo(lua_State*L){

intn=lua_gettop(L);/*numberofarguments参数个数*/

printf("共传入%d个参数\n",n);

lua_Numbersum=0;//doublue8字节

inti;

for(i=1;i<=n;i++){

if(!

lua_isnumber(L,i)){

lua_pushstring(L,"incorrectargumenttofunction'average'");

lua_error(L);

}

sum+=lua_tonumber(L,i);

}

lua_pushnumber(L,sum/n);/*firstresult第一个结果*/

lua_pushnumber(L,sum);/*secondresult第二个结果*/

return2;/*numberofresults结果个数*/

printf("在C函数里计算的结果是:

%d\n",sum);//sum为什么不可以输出?

除了压栈的全部清掉?

//这个值肯定是丢掉了。

因为要通过栈来通信。

}

lua_gettop

intlua_gettop(lua_State*L);

Returnstheindexofthetopelementinthestack.Becauseindicesstartat1,thisresultisequaltothenumberofelementsinthestack(andso0meansanemptystack).

返回索引中的栈顶元素,因为索引从1开始,这个结果等于栈中的所包含的元素(所以0表示一个空栈)。

问题:

为什么要有这个?

[我想表达的意思是,lua中的一句话,在capi实现起来就是n句,可能有人疑惑那为什么不直接用lua多好,capi这么麻烦,答案是有的事只能用capi才能实现]

//测试LUA调用C函数的代码:

intmain(void){

lua_State*L=lua_open();

luaL_openlibs(L);

lua_register(L,"a",faoo);

constchar*buf="r1,r2=a(50,100)print(r1..r2)";

luaL_dostring(L,buf);

lua_close(L);

return0;

}

这意味着只有这种类型的函数才能向Lua注册。

首先,我们定义一个函数

intfoo(lua_State*L)

{

//首先取出脚本执行这个函数时压入栈的参数(因为是在LUA中调C函数)

//假设这个函数提供一个参数,有两个返回值

//getthefirstparameter

constchar*par=lua_tostring(L,-1);//取出栈顶的参数,即调用函数时的参数。

printf("%s",par);

//pushthefirstresult

lua_pushnumber(L,100);//压入第一个结果

//pushthesecondresult

lua_pushnumber(L,200);//压入第二个结果

//return2result

return2;//返回2,表示有两个结果。

}

我们可以在脚本中这样调用这个函数

r1,r2=foo("hello")//两个返回值,r1,r2

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",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;

}//此测试代码有问题,1、没有把C函数注册到LUA,2、luaL_dostring(L,buf).

程序输出:

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=lualibs;

for(;lib->func;lib++)

{

lib->func(L);/*openlibrary*/

lua_settop(L,0);/*discardanyresults*/

}

}

lua_State*get_handle()

{

returnL;

}

interror_fn()

{

returnerr_fn;

}

private:

lua_State*L;

interr_fn;

};

通常我们仅仅在游戏代码中使用一个lua_State*的指针,所以我们为它实现一个单件,默认打开所有lua提供的lib:

//returntheglobalinstance

state*lua_state()

{

staticstateL(true);

return&L;

}

在你的游戏中应用Lua(3):

using

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 医药卫生 > 基础医学

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1