其实自从打开GameScene.lua文件以来,我一直都很想搞清楚的一个问题就是cc.Sprite这些是如何识别的。其实第一反应肯定就是:它肯定是一个全局变量,要不然怎么调用create方法呢。先不考究cocos的C++类方法是如何绑定到lua的,我就是想知道这个全局变量是什么时候注册的?
知道一点lua知识的都知道 lua里面的全局变量会保存到一个全局表_G中去,问题就转换为了 什么时候向_G中注册的?在使用cc.Sprite的create之前,我好像没有没有在lua中定义cc.Sprite这样的变量,所以不可能是lua定义的这个全局变量,而是cocos在初始化lua引擎的时候对它进行注册的。
让我们来看看lua引擎初始化的时候都做了什么操作。果然找到了_G的踪迹。
luaL_register(_state,"_G",global_functions);
这是在向lua中注册全局函数。(关键是此时的栈顶是 全局表 啊!!!这点很重要)。
让我们继续看下去,我们会看到各种注册:
register_all_cocos2dx(_state); register_all_cocos2dx_extension(_state); register_cocos2dx_extension_CCBProxy(_state); tolua_opengl_open(_state); register_all_cocos2dx_ui(_state); register_all_cocos2dx_studio(_state); register_all_cocos2dx_manual(_state); register_all_cocos2dx_module_manual(_state); register_all_cocos2dx_extension_manual(_state); register_all_cocos2dx_coco_studio_manual(_state); register_all_cocos2dx_ui_manual(_state); register_all_cocos2dx_spine(_state); register_all_cocos2dx_spine_manual(_state); register_glnode_manual(_state);
每一个注册完成后,均会还原现场,即保持栈顶是_G表。让我们选一个来分析吧,就以register_all_cocos2dx 为例吧。
TOLUA_API int register_all_cocos2dx(lua_State* tolua_S) { tolua_open(tolua_S); tolua_module(tolua_S,"cc",0); tolua_beginmodule(tolua_S,"cc"); lua_register_cocos2dx_Ref(tolua_S); lua_register_cocos2dx_Console(tolua_S); ...省略很多代码... }
这里貌似看到一点苗头。cc和我们的cc.Sprite很近了。
基础函数介绍:
tolua_module:尝试注册以name为名的模块,每个模块都是在向父模块注册,但是cc没有父模块,所以它会认为_G是它该去注册的地方。
TOLUA_API void tolua_module (lua_State* L,const char* name,int hasvar) { if (name) { /* tolua module */ lua_pushstring(L,name); lua_rawget(L,-2);//拿到name在当前模块表中是否进行了注册 if (!lua_istable(L,-1)) /* check if module already exists */ { //没有注册过,new一个 lua_pop(L,1); lua_newtable(L); lua_pushstring(L,name); lua_pushvalue(L,-2); /* 下面这段测试表明该模块是加到(全局表)中 if(lua_istable(L,-4)){ <span style="white-space:pre"> </span>lua_getfield(L,-4,"print"); <span style="white-space:pre"> </span>if(lua_isnil(L,-1)){ <span style="white-space:pre"> </span>char a='a'; <span style="white-space:pre"> </span>}else{ <span style="white-space:pre"> </span>char b ='b'; //come here啦啦啦!!! <span style="white-space:pre"> </span>} <span style="white-space:pre"> </span>lua_pop(L,1); <span style="white-space:pre"> </span>}*/ //注册到当前模块表中去 lua_rawset(L,-4); /* assing module into module */ } } }从上面的测试代码就可以发现就是向_G表注册。所以我们的猜测是完全正确的。为什么是_G表呢?之前已经提示过了(注册全局函数的时候,会把_G放在栈顶)。
现在全局表中有这样的一个键值对:_GTable["cc"]=cc_table。
接下就看Sprite是如何注册进去的吧。在register_all_cocos2dx那堆注册的函数中,我们发现:
lua_register_cocos2dx_Sprite(tolua_S);
int lua_register_cocos2dx_Sprite(lua_State* tolua_S) { tolua_usertype(tolua_S,"cc.Sprite"); tolua_cclass(tolua_S,"Sprite","cc.Sprite","cc.Node",nullptr); tolua_beginmodule(tolua_S,"Sprite"); tolua_function(tolua_S,"setSpriteFrame",lua_cocos2dx_Sprite_setSpriteFrame); //...此处省略很多代码... }
基础函数介绍:
tolua_usertype:向注册表中注册该用户类型,创建该类型的表。(table和type在注册表中是双向映射)。
tolua_cclass:设置该类的元表啊,父类,tolua_super表注册,tolua_uBox表注册,。并且以"Sprite"为key加入到当前所在父模板表中。
我们知道当前父模板表是cc_table,当前模板表是:sprite_table。cc_table["Sprite"] = sprite_table。sprite_table["setSpriteFrame"] = lua_cocos2dx_Sprite_setSpriteFrame。
现在就可以通过全局表去访问 到Sprite了,并且可以访问到里面的方法。
所以我觉得cc.Sprite.setSpriteFrame(...)的执行流程是:向_G表中拿到cc对应的cc_table,再看在cc_table中是否有Sprite,如果有就在sprite_table中去寻找setSpriteFrame方法。
以上纯属个人看法。本人也是在学习中,欢迎和一起交流。