如何通过JS_CallFunctionValue函数完成JS层回调函数的调用。
这篇文章将为大家详细讲解有关cocos2d-x中回调函数的按键回调是怎样的,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。
目前创新互联公司已为上千余家的企业提供了网站建设、域名、雅安服务器托管、成都网站托管、企业网站设计、西青网站维护等服务,公司将坚持客户导向、应用为本的策略,正道将秉承"和谐、参与、激情"的文化,与客户和合作伙伴齐心协力一起成长,共同发展。
回调函数是界面交互和接入各种第三方SDK的关键所在,因为回调函数的C++代码是不能自动生成的,一切的一切,都需要手写完成。
比较不错的是,Cocos2d-x引擎对于回调函数提供了完整的包装机制。我们所需要做的就是了解这个机制,并使用他。学习引擎自己的代码例子,可以比较快速准确的上手这一机制。
首先,我们在Cocos2d-x 3.0 beta版中,使用他自带的工程创建工具,新建一个跨平台的JS项目。按照惯例,这是一个helloworld项目。在XCode运行时,我们可以看到右下角的回调按钮。我们来看看他是怎么实现的。分成两个过程来做:
一、绑定回调函数过程
首先,我们要去找回调函数JS的绑定代码,在myApp.js中,init函数里面,可以看到如下代码:
1 2 3 4 5 6 7 8 9 10 11 12 | // var closeItem "res/CloseNormal.png" , "res/CloseSelected.png" , function () cc.log( "close ); }, this ); closeItem.setAnchorPoint(cc.p(0.5, var menu menu.setPosition(cc.p(0, this .addChild(menu, closeItem.setPosition(cc.p(size.width |
cc.MenuItemImage.create函数的第三个参数,绑定了匿名回调函数。第四个参数,传入的是回调函数调用时的this(如果不理解JS的this机制,请先阅读一些JS的资料)。这些都是意图和作用很明显的JS代码,不用细说。
然后,我们去看底层对应执行的C++代码。在cocos2d_specifics.cpp文件中,找到js_cocos2dx_CCMenuItemImage_create函数。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 | // // JSBool { if (argc jsval JSStringWrapper JSStringWrapper JSStringWrapper bool thirdArgIsString true ; jsval jsval int last if (argc thirdArgIsString if (thirdArgIsString) arg2.set(argv[2], last } } cocos2d::MenuItemImage* if (argc if (!thirdArgIsString) //cc.MenuItemImage.create( jsCallback if (argc jsThis } } else { //cc.MenuItemImage.create( if (argc jsCallback if (argc jsThis } } } } JSObject JS_SET_RVAL(cx, return JS_TRUE; } JS_ReportError(cx, "Invalid ); return JS_FALSE; } |
因为在C++层,这是一个重载过的函数,所以他的实现里面有很多参数个数的判断(关于重载问题请参考之前的章节)。过滤掉很多代码,我们直接看关键部分:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | if (argc if (!thirdArgIsString) //cc.MenuItemImage.create( jsCallback if (argc jsThis } } else { //cc.MenuItemImage.create( if (argc jsCallback if (argc jsThis } } } } |
在这里我们从参数中取出回调函数和this,分别赋值给jsCallback和jsThis。
1 | JSObject |
由这句模板函数来实现回调的绑定,四个参数依次是,JS上下文,cc.MenuItemImage对应的C++对象,回调函数,和回调函数调用时的this。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | template < class T> JSObject* js_proxy_t if (p) addCallBackAndThis(p->obj, return p->obj; } else { js_type_class_t assert (classType); JSObject // js_proxy_t JS_AddNamedObjectRoot(cx, typeid (*nativeObj).name()); addCallBackAndThis(tmp, return tmp; } } |
继续看bind_menu_item的实现。简单说一下,因为绑定的是一个JS函数,所以实际上,需要在SpiderMonkey里面做这个绑定操作。传进来的是一个C++对象(CCMenuItemImage类型),首先找到和这个C++对象对应的JS对象。如果找不到,就新建立一个。然后通过函数addCallBackAndThis执行绑定。
1 2 3 4 5 6 7 8 9 | static { if (callback ScriptingCore::getInstance()->setReservedSpot(0, } if (thisObj ScriptingCore::getInstance()->setReservedSpot(1, } } |
1 2 3 4 | JSBool JS_SetReservedSlot(obj, return JS_TRUE; } |
最终我们看到,存储回调函数的方法是通过SpiderMonkey的ReservedSlot机制。0位存放的是回调函数,1位存放的是回调函数对应的this。
好,到此为止,回调函数的绑定全部结束。
二、调用回调函数过程
现在我们看从C++层启动JS回调的过程。我们省略掉事件派发机制,直接看按键事件发生时的调用代码。在按键事件发生时,会调用MenuItemImage的父类MenuItem中的activate函数。该函数在CCMenuItem.cpp中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | void MenuItem::activate() { if (_enabled) { if ( { _callback( this ); } if (kScriptTypeNone { BasicScriptData this ); ScriptEvent ScriptEngineManager::getInstance()->getScriptEngine()->sendEvent(&scriptEvent); } } } |
非常简单,首先判断按键是否可用。然后如果有C++层回调就调用。如果有脚本层(JS或lua)回调,就包装一个kMenuClickedEvent事件,然后向对应的脚本引擎发送该事件。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | int { if (NULL return 0; JSAutoCompartment switch (evt->type) { case kNodeEvent: { return handleNodeEvent(evt->data); } break ; case kMenuClickedEvent: { return handleMenuClickedEvent(evt->data); } break ; case kTouchEvent: { return handleTouchEvent(evt->data); } break ; case kTouchesEvent: { return handleTouchesEvent(evt->data); } break ; case kKeypadEvent: { return handleKeypadEvent(evt->data); } break ; case kAccelerometerEvent: { return handleAccelerometerEvent(evt->data); } break ; default : break ; } return 0; } |
JS通过ScriptingCore::sendEvent进行事件分发。kMenuClickedEvent事件派发给handleMenuClickedEvent函数来处理。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | int { if (NULL return 0; BasicScriptData* if (NULL return 0; MenuItem* js_proxy_t if (!p) return 0; jsval jsval js_proxy_t dataVal executeJSFunctionFromReservedSpot( this ->_cx, return 1; } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | static void executeJSFunctionFromReservedSpot(JSContext jsval jsval if (func return ; jsval JSAutoCompartment if (thisObj JS_CallFunctionValue(cx, } else { assert (!JSVAL_IS_PRIMITIVE(thisObj)); JS_CallFunctionValue(cx, } } |
再次通过SpiderMonkey的ReservedSlot机制,取回相应的参数,最后通过JS_CallFunctionValue函数完成JS层回调函数的调用。
关于cocos2d-x中回调函数的按键回调是怎样的就分享到这里了,希望以上内容可以对大家有一定的帮助,可以学到更多知识。如果觉得文章不错,可以把它分享出去让更多的人看到。
新闻名称:如何通过JS_CallFunctionValue函数完成JS层回调函数的调用。
当前地址:http://hbruida.cn/article/jeehop.html