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

前端之家收集整理的这篇文章主要介绍了【cocos2d-x从c++到js】12:回调函数1——按键回调前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
2014-01-25 15:17:37
@H_404_4@ 标签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." );
@H_301_117@ }, 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
@H_502_226@ 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);
@H_301_117@ 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_44_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++层,这是一个重载过的函数,所以他的实现里面有很@R_502_428@个数的判断(关于重载问题请参考之前的章节)。过滤掉很多代码,我们直接看关键部分:


18
(argc >= 3) {
(!thirdArgIsString) {
jsCallback = argv[last++];
(argc == 4) {
jsThis = argv[last];
@H_301_117@ }
}
{
(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;
@H_301_117@ } {
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) {
@H_301_117@ 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 )
{
@H_301_117@ _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);
@H_301_117@
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_44_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);
@H_301_117@ (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);
@H_301_117@
(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

原文链接:https://www.f2er.com/cocos2dx/345662.html

猜你在找的Cocos2d-x相关文章