【cocos2d-x从c++到js】12:回调函数1——按键回调

【cocos2d-x从c++到js】12:回调函数1——按键回调,第1张

概述2014-01-25 15:17:37 标签: cocos2d-x  js  jsb 原创作品,允许转载,转载时请务必以超链接形式标明文章  原始出处 、作者信息和本声明。否则将追究法律责任。 http://www.voidcn.com/article/p-ficsibso-vm.html 回调函数是界面交互和接入各种第三方SDK的关键所在,因为回调函数的C++代码是不能自动生成的,一切的一切,都 2014-01-25 15:17:37 标签: cocos2d-x js jsb 原创作品,允许转载,转载时请务必以超链接形式标明文章 原始出处、作者信息和本声明。否则将追究法律责任。 http://www.jb51.cc/article/p-ficsibso-vm.html

回调函数是界面交互和接入各种第三方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 // add a "close" icon to exit the progress. it's an autorelease object var closeItem = cc.MenuItemImage.create( "res/Closenormal.png" , "res/CloseSelected.png" function () { cc.log( "close button was clicked." ); }, this ); closeItem.setAnchorPoint(cc.p(0.5,0.5)); menu = cc.Menu.create(closeItem); menu.setposition(cc.p(0,0)); .addChild(menu,1); closeItem.setposition(cc.p(size.wIDth - 20,20));

cc.MenuItemImage.create函数的第三个参数,绑定了匿名回调函数。第四个参数,传入的是回调函数调用时的this(如果不理解Js的this机制,请先阅读一些Js的资料)。这些都是意图和作用很明显的Js代码,不用细说。


然后,我们去看底层对应执行的C++代码。在cocos2d_specifics.cpp文件中,找到Js_cocos2dx_CcmenuItemImage_create函数。


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 // "create" in Js // cc.MenuItemImage.create( normalimage,selectedImage,[DisabledImage],callback_fn,[this] JsBool Js_cocos2dx_CcmenuItemImage_create(jscontext *cx,uint32_t argc,Jsval *vp) { if (argc >= 2 && argc <= 5) { Jsval *argv = Js_ARGV(cx,vp); JsstringWrapper arg0(argv[0]); JsstringWrapper arg1(argv[1]); JsstringWrapper arg2; bool thirdArgIsstring = true ; Jsval JsCallback = JsVAL_VOID; Jsval JsThis = JsVAL_VOID; int last = 2; (argc >= 3) { thirdArgIsstring = argv[2].isstring(); (thirdArgIsstring) { arg2.set(argv[2],cx); last = 3; } } cocos2d::MenuItemImage* ret = cocos2d::MenuItemImage::create(arg0.get(),arg1.get(),std::string(arg2.get())); (argc >= 3) { (!thirdArgIsstring) { //cc.MenuItemImage.create( normalimage,[this] ) JsCallback = argv[last++]; (argc == 4) { JsThis = argv[last]; } } else { (argc >= 4) { @H_841_404@ JsCallback = argv[last++]; (argc == 5) { JsThis = argv[last]; } } } } JsObject *obj = bind_menu_item<cocos2d::MenuItemImage>(cx,ret,JsCallback,JsThis); Js_SET_RVAL(cx,vp,OBJECT_TO_JsVAL(obj)); return Js_TRUE; } Js_ReportError(cx, "InvalID number of arguments. Expecting: 2 <= args <= 5" ); Js_FALSE; }

因为在C++层,这是一个重载过的函数,所以他的实现里面有很多参数个数的判断(关于重载问题请参考之前的章节)。过滤掉很多代码,我们直接看关键部分:


18 (argc >= 3) { (!thirdArgIsstring) { JsCallback = argv[last++]; (argc == 4) { JsThis = argv[last]; } } { (argc >= 4) { JsCallback = argv[last++]; (argc == 5) { JsThis = argv[last]; } } } }

在这里我们从参数中取出回调函数和this,分别赋值给JsCallback和JsThis。


1 由这句模板函数来实现回调的绑定,四个参数依次是,Js上下文,cc.MenuItemImage对应的C++对象,回调函数,和回调函数调用时的this。


17 template
< class T> JsObject* bind_menu_item(jscontext *cx,T* nativeObj,Jsval callback,Jsval thisObj) { Js_proxy_t *p = Jsb_get_native_proxy(nativeObj); (p) { addCallBackAndThis(p->obj,callback,thisObj); p->obj; } { Js_type_class_t *classtype = Js_get_type_from_native<T>(nativeObj); assert (classtype); JsObject *tmp = Js_NewObject(cx,classtype->Jsclass,classtype->proto,classtype->parentProto); // bind nativeObj <-> JsObject Js_proxy_t *proxy = Jsb_new_proxy(nativeObj,tmp); Js_AddnamedobjectRoot(cx,&proxy->obj, typeID (*nativeObj).name()); addCallBackAndThis(tmp,thisObj); tmp; } }

继续看bind_menu_item的实现。简单说一下,因为绑定的是一个Js函数,所以实际上,需要在SpIDerMonkey里面做这个绑定 *** 作。传进来的是一个C++对象(CcmenuItemImage类型),首先找到和这个C++对象对应的Js对象。如果找不到,就新建立一个。然后通过函数addCallBackAndThis执行绑定。


9 static voID addCallBackAndThis(JsObject *obj,Jsval &thisObj) { if (callback != JsVAL_VOID) { Scriptingcore::getInstance()->setReservedSpot(0,obj,callback); } (thisObj != JsVAL_VOID) { Scriptingcore::getInstance()->setReservedSpot(1,thisObj); } }


4 JsBool Scriptingcore::setReservedSpot(uint32_t i,JsObject *obj,Jsval value) { Js_SetReservedSlot(obj,i,value); Js_TRUE; }


最终我们看到,存储回调函数的方法是通过SpIDerMonkey的ReservedSlot机制。0位存放的是回调函数,1位存放的是回调函数对应的this。


好,到此为止,回调函数的绑定全部结束。


二、调用回调函数过程


现在我们看从C++层启动Js回调的过程。我们省略掉事件派发机制,直接看按键事件发生时的调用代码。在按键事件发生时,会调用MenuItemImage的父类MenuItem中的activate函数。该函数在CcmenuItem.cpp中。


17 voID MenuItem::activate() { (_enabled) ( _callback ) { _callback( this ); (kScriptTypeNone != _scriptType) { BasicScriptData data( ); ScriptEvent scriptEvent(kMenuClickedEvent,&data); ScriptEngineManager::getInstance()->getScriptEngine()->sendEvent(&scriptEvent); }


非常简单,首先判断按键是否可用。然后如果有C++层回调就调用。如果有脚本层(Js或lua)回调,就包装一个kMenuClickedEvent事件,然后向对应的脚本引擎发送该事件。


45 int Scriptingcore::sendEvent(ScriptEvent* evt) (NulL == evt) return 0; JsautoCompartment ac(_cx,_global); switch (evt->type) { case kNodeEvent: { handleNodeEvent(evt->data); } break ; kMenuClickedEvent: { handleMenuClickedEvent(evt->data); } ; ktouchEvent: { handletouchEvent(evt->data); } ; ktouchesEvent: { handletouchesEvent(evt->data); } ; kKeypadEvent: { handleKeypadEvent(evt->data); @H_841_404@ } ; kAccelerometerEvent: { handleAccelerometerEvent(evt->data); } ; default : ; } 0; }

Js通过Scriptingcore::sendEvent进行事件分发。kMenuClickedEvent事件派发给handleMenuClickedEvent函数来处理。


20 int Scriptingcore::handleMenuClickedEvent(voID* data) (NulL == data) BasicScriptData* basicScriptData = static_cast<BasicScriptData*>(data); (NulL == basicScriptData->nativeObject) 0; MenuItem* menuItem = static_cast<MenuItem*>(basicScriptData->nativeObject); Js_proxy_t * p = Jsb_get_native_proxy(menuItem); (!p) 0; Jsval retval; Jsval dataVal; Js_proxy_t *proxy = Jsb_get_native_proxy(menuItem); dataVal = (proxy ? OBJECT_TO_JsVAL(proxy->obj) : JsVAL_NulL); executeJsFunctionFromreservedSpot( ->_cx,p->obj,dataVal,retval); 1; }


14 static executeJsFunctionFromreservedSpot(jscontext *cx, Jsval &dataVal,Jsval &retval) { Jsval func = Js_GetReservedSlot(obj,0); (func == JsVAL_VOID) { ; } Jsval thisObj = Js_GetReservedSlot(obj,1); JsautoCompartment ac(cx,obj); (thisObj == JsVAL_VOID) { Js_CallFunctionValue(cx,func,1,&dataVal,&retval); { (!JsVAL_IS_PRIMITIVE(thisObj)); } }


再次通过SpIDerMonkey的ReservedSlot机制,取回相应的参数,最后通过Js_CallFunctionValue函数完成Js层回调函数的调用。

下篇继续

本文出自 “老G的小屋” 博客,请务必保留此出处http://www.jb51.cc/article/p-ficsibso-vm.html

总结

以上是内存溢出为你收集整理的【cocos2d-x从c++到js】12:回调函数1——按键回调全部内容,希望文章能够帮你解决【cocos2d-x从c++到js】12:回调函数1——按键回调所遇到的程序开发问题。

如果觉得内存溢出网站内容还不错,欢迎将内存溢出网站推荐给程序员好友。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/web/1020091.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-23
下一篇 2022-05-23

发表评论

登录后才能评论

评论列表(0条)

保存