cocos2d-x 3.x中lua调用原理1--从基本的c++与lua互相调用开始

刚开始弄cocos2d-x 3.x的lua框架。看了很多网上的文章和例子。到最后自己手动写 的时候也遇到很多困难。现在就把这个学习过程记录下来。

 首先我看完了 lua脚本在c++的舞步。这篇文章很好,很详细的讲述了c++与lua是如何互相调用的。之后我也看了笨木头的5个例子,简单的实现了一下印证了一些想法。

 1)Lua指针的初始化

    lua_State* pL =lua_open();

    luaL_openlibs(pL);

  Lua指针的释放  

lua_close(pL);

而在cocos2d-x中 lua_State*被封装在LuaStack对象中,它的构造和析构也执行了相同的调用。不同的地方是它的初始化中所执行的初始化语句更多,包装了更多的绑定相关的内容。

2)lua本身是脚本语言,加载lua本身的时候才会编译。所以,推荐大家在加载文件的时候尽量放在程序的初始化中,因为当你执行luaL_dofile()函数的时候,Lua会启用语法分析器,去分析你的脚本语法是否符合Lua规则,如果你胡乱的传一个文件过去,Lua就会告诉你文件语法错误,无法加载。如果你的Lua脚本很大,函数很多,语法分析器会比较耗时,所以,加载的时候,尽量放在合适的地方。

int nRet =luaL_dofile(_state, fullPath.c_str());

if (nRet !=0)

{

    CCLOG("[LUA ERROR] %s",lua_tostring(_state, -1));

   lua_pop(_state,1);

   return nRet;

}

这一段指明执行完lua文件后返回若非0则出错,lua_tostring从栈中获得错误信息描述,并输出。

说到这里简单借用一下lua的栈描述栈底为1,往栈顶顺序增加。栈顶为-1,往栈底顺序减少。执行文件出错,lua会将出错信息放入栈顶,因此为-1。

3)对lua进行栈操作的常用函数介绍

压入元素到栈里

void lua_pushnil (lua_State *L);    
void lua_pushboolean (lua_State *L, int bool);
void lua_pushnumber (lua_State *L, double n);
void lua_pushlstring (lua_State *L, const char *s, size_t length);
void lua_pushstring (lua_State *L, const char *s);
void lua_pushcfunction (lua_State *L, lua_CFunction fn);


查询栈里的元素

lua_isnil (lua_State *L, int index);
lua_isboolean (lua_State *L, int index);
int lua_isnumber (lua_State *L, int index);
int lua_isstring (lua_State *L, int index);
int lua_isfunction (lua_State *L, int index);
int lua_istable (lua_State *L, int index);
int lua_isuserdata (lua_State *L, int index);
lua_islightuserdata (lua_State *L, int index);
lua_isthread (lua_State *L, int index);

 

转换栈里的元素

int                lua_toboolean (lua_State *L, int index);
double            lua_tonumber (lua_State *L, int index);
const char *    lua_tostring (lua_State *L, int index);
const char *    lua_tolstring (lua_State *L, int idx, size_t *len);
size_t            lua_strlen (lua_State *L, int index);
lua_CFunction   lua_tocfunction (lua_State *L, int idx);
void *          lua_touserdata (lua_State *L, int idx);
lua_State *     lua_tothread (lua_State *L, int idx);

 

Lua栈的维护

int  lua_gettop (lua_State *L);
    取得栈顶元素的索引,即栈中元素的个数
void lua_settop (lua_State *L, int index);
    设置栈顶索引,即设置栈中元素的个数,如果index<0,则从栈顶往下数,下同
void lua_pushvalue (lua_State *L, int index);
    把栈中指定索引的元素复制一份到栈顶
void lua_remove (lua_State *L, int index);
    删除指定索引的元素
void lua_insert (lua_State *L, int index);
    移动栈顶元素到指定索引的位置,栈中数目没有改变
void lua_replace (lua_State *L, int index);
    从栈顶弹出元素值并将其设置到指定索引位置,栈中的数目减一
int  lua_checkstack (lua_State *L, int extra);
    确保堆栈上至少有 extra 个空位。如果不能把堆栈扩展到相应的尺寸,函数返回 false 。这个函数永远不会缩小堆栈。
int  lua_pop(L,n)
    从栈顶弹出n个元素,它是一个lua_settop的包装:#define lua_pop(L,n)  lua_settop(L, -(n)-1)
void lua_newtable (lua_State *L);
    新建一个空的table并压入栈顶。
void lua_settable (lua_State *L, int idx);
    lua_settable以table在栈中的索引作为参数,并将栈顶的key和value出栈,用这两个值修改table。
void lua_gettable (lua_State *L, int idx);
    lua_gettable以table在栈中的索引作为参数,弹出栈顶的元素作为key,返回与key对应的value并压入栈顶。
最后,Lua告别针对table提供了存取函数
void lua_rawgeti (lua_State *L, int idx, int n)
    取得table[n]并放到栈顶,例如lua_pushnumber(L,i);lua_gettable(L,-2);可以用lua_rawgeti(L,-1,i)代替。
lua_getfield (lua_State *L, int idx, const char *k)
    取得table.k并放到栈顶,例如lua_pushstring(L,"u");lua_gettable(L,-2);可以替换lua_getfield(L,-1,"u")。
void lua_setfield (lua_State *L, int idx, const char *k)
    把栈顶的数据作为value放入table.k中,形如lua_pushstring(L, "key");lua_pushstring(L,value);lua_settable(L, -3);可以改成lua_pushstring(L, value);lua_setfield(L,-2,"key");的形式。
void lua_rawseti (lua_State *L, int idx, int n)
    把栈顶的数据作为value放入table[n]中,形如lua_pushnumber(L,value);lua_rawseti(L,-2,key);
raw操作针对table的key均为数字,类似数组.filed操作针对table的key为string,类似map. 

4) c++中获取lua中的全局变量 

lua_getglobal(L, "var"); //变量的值现在栈顶
int var = lua_tonumber(L, -1);

5) c++中设置lua中的全局变量

lua_pushnumber(L, 10);

lua_setglobal(L, "z");

操作结束后设置lua中的全局变量z = 10,即使z原本不在lua的全局变量表中也会创建。

6) c++中设置lua中表的值

lua_getglobal( L, "myTable" );   // 获取要设置值的table
lua_pushstring( L, "hp" );         // "hp"在栈上的位置为-1
lua_pushnumber( L, 211 );          // "hp"在栈上的位置变为-2,而211则是-1
lua_settable( L, -3 );               // 值被正确的设置到全局变量(表)的myTable


如果我是想把hp这个值设置到全局表中呢?一般通过调用lua_setglobal宏 

lua_pushnumber( L, 211 );
lua_setglobal( L, "hp" );

7)c++中调用lua函数.
上面讲了这么多,现在我们尝试在c++中调用lua函数(不带参数,带普通参数,带用户自定义对象参数)。
首先我们可以在cocos2d-x中的LuaStack中找到

virtual int executeGlobalFunction(const char* functionName);

方法,该方法会执行一个不带参数的全局lua函数,且返回一个整形值

lua中代码如下:


function testByWp()

print("-----wptest------")

return 1;

end


c++中代码如下:

auto engine = LuaEngine::getInstance();

int ret = engine->executeGlobalFunction("testByWp");


可以看到LuaEngine中封装了LuaStack中的executeGlobalFunction函数。

我们若要执行带参数的函数怎么办呢。那么就简单的改造一下LuaStack中的函数

int LuaStack::executeGlobalFunction(const char* functionName)

{

    lua_getglobal(_state, functionName);       /* query function by name, stack: function */

    if (!lua_isfunction(_state, -1))

    {

        CCLOG("[LUA ERROR] name '%s' does not represent a Lua function", functionName);

        lua_pop(_state, 1);

        return 0;

    }

    

    

    return executeFunction(0);

}

这个是原函数,我们要传入参数 改造如下

int LuaStack::executeGlobalFunction(const char* functionName,int value ,int numArgs)

{

    lua_getglobal(_state, functionName);       /* query function by name, stack: function */

    if (!lua_isfunction(_state, -1))

    {

        CCLOG("[LUA ERROR] name '%s' does not represent a Lua function", functionName);

        lua_pop(_state, 1);

        return 0;

    }

    pushInt(value);

    return executeFunction(numArgs);

}

其实这里numArgs可以不用传递,作为例子我们只是简单的为函数传入一个参数,因此numArgs的值为1.若要传入不同的参数,就得自己封装一下。

lua中代码如下:


function testByWp(int val)

print("-----wptest------"..val)

return 1;

end


c++中代码如下:

auto engine = LuaEngine::getInstance();

int ret = engine->executeGlobalFunction("testByWp",10,1);

若要传递用户自定义类型作为参数,那么首先要求该自定义类型继承自cocos::Ref ,然后通过genbindings绑定工具绑定到lua中以使得lua中能识别。然后我们再封装一个执行得函数

int LuaStack::executeGlobalFunction(const char* functionName,cocos2d::Ref *objectValue, const char *typeName)

{

    lua_getglobal(_state, functionName);       /* query function by name, stack: function */

    if (!lua_isfunction(_state, -1))

    {

        CCLOG("[LUA ERROR] name '%s' does not represent a Lua function", functionName);

        lua_pop(_state, 1);

        return 0;

    }

    pushObject(objectValue, typeName);

    return executeFunction(1);

}

这里更加简单一些,传入一个自定义对象 以及类型得名称。

lua中代码如下:


function testByWp(user_data)

print("-----wptest------")

if user_data then

        user_data = tolua.cast(user_data, "TestScene");

        print (user_data:GetFlag());

    end


return 100;

end


c++中代码如下:

    auto engine = LuaEngine::getInstance();

    TestScene *pScene = new TestScene;

    pScene->SetFlag(108);

    

    int ret = engine->executeGlobalFunction("testByWp",pScene,"TestScene");

    CC_SAFE_DELETE(pScene);

















你可能感兴趣的:(cocos2d-x 3.x中lua调用原理1--从基本的c++与lua互相调用开始)