cocos2d lua绑定感悟---像cc.Sprite,cc.Director这些是如何识别的

其实自从打开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方法。

以上纯属个人看法。本人也是在学习中,欢迎和一起交流。

相关文章

    本文实践自 RayWenderlich、Ali Hafizji 的文章《...
Cocos-code-ide使用入门学习地点:杭州滨江邮箱:appdevzw@1...
第一次開始用手游引擎挺激动!!!进入正题。下载资源1:从C...
    Cocos2d-x是一款强大的基于OpenGLES的跨平台游戏开发...
1.  来源 QuickV3sample项目中的2048样例游戏,以及最近《...
   Cocos2d-x3.x已经支持使用CMake来进行构建了,这里尝试...