[Cocos2d-x from c ++ to js] 12: callback function 1 -- button callback

Source: Internet
Author: User

The callback function is the key to interface interaction and access to various third-party sdks, because the C ++ code of the callback function cannot be automatically generated, and everything needs to be done by hand.


The good thing is that the Cocos2d-x engine provides a complete packaging mechanism for callback functions. All we need to do is to understand this mechanism and use it. The learning engine's own code example can help you get started with this mechanism quickly and accurately.


First, we create a cross-platform JS project in Cocos2d-x 3.0 beta using its built-in project creation tool. By convention, this is a helloworld project. When running XCode, we can see:

650) this. width = 650; "src =" http://www.bkjia.com/uploads/allimg/140207/222G54a3-0.jpg "title =" iOS simulator screen snapshot "January 25, 2014 201712.36.492.16.png" alt = "wKiom1LjP67wo2etAAGX7-JVxuY628.jpg"/>


You can see the callback button in the lower right corner. Let's see how it is implemented. It is divided into two processes:



I. Process of binding callback Functions


First, we need to retrieve the binding code of the calling function JS. In myApp. js, The init function shows the following code:

// add a "close" icon to exit the progress. it's an autorelease objectvar 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));var menu = cc.Menu.create(closeItem);menu.setPosition(cc.p(0, 0));this.addChild(menu, 1);closeItem.setPosition(cc.p(size.width - 20, 20));

The third parameter of the cc. MenuItemImage. create Function is bound to the anonymous callback function. The fourth parameter is the callback function call. If you do not understand the JS this mechanism, read some JS materials first ). These are JS Code with obvious intentions and functions.


Then, let's look at the C ++ code executed at the underlying layer. In the cocos2d_specifics.cpp file, find the js_cocos2dx_CCMenuItemImage_create function.


// "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;        if (argc >= 3) {            thirdArgIsString = argv[2].isString();            if (thirdArgIsString) {                arg2.set(argv[2], cx);                last = 3;            }        }        cocos2d::MenuItemImage* ret = cocos2d::MenuItemImage::create(arg0.get(), arg1.get(), std::string(arg2.get()));        if (argc >= 3) {            if (!thirdArgIsString) {                //cc.MenuItemImage.create( normalImage, selectedImage, callback_fn, [this] )                jsCallback = argv[last++];                if (argc == 4) {                    jsThis = argv[last];                }            }            else {                //cc.MenuItemImage.create( normalImage, selectedImage, disabledImage, callback_fn, [this] )                if (argc >= 4) {                    jsCallback = argv[last++];                    if (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");    return JS_FALSE;}

In the C ++ layer, this is a function that has been overloaded. Therefore, there are many parameters in its implementation to determine the number of parameters. For more information, see the previous chapter ). To filter out a lot of code, let's look at the key part:


if (argc >= 3) {            if (!thirdArgIsString) {                //cc.MenuItemImage.create( normalImage, selectedImage, callback_fn, [this] )                jsCallback = argv[last++];                if (argc == 4) {                    jsThis = argv[last];                }            }            else {                //cc.MenuItemImage.create( normalImage, selectedImage, disabledImage, callback_fn, [this] )                if (argc >= 4) {                    jsCallback = argv[last++];                    if (argc == 5) {                        jsThis = argv[last];                    }                }            }        }

Here we take the callback function and this from the parameter and assign them to jsCallback and jsThis respectively.


JSObject *obj = bind_menu_item<cocos2d::MenuItemImage>(cx, ret, jsCallback, jsThis);

This template function is used to bind callback. The four parameters are JS context, C ++ object corresponding to cc. MenuItemImage, callback function, and this when the callback function is called.


template<class T>JSObject* bind_menu_item(JSContext *cx, T* nativeObj, jsval callback, jsval thisObj) {       js_proxy_t *p = jsb_get_native_proxy(nativeObj);    if (p) {        addCallBackAndThis(p->obj, callback, thisObj);        return p->obj;    } else {        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, callback, thisObj);        return tmp;    }}

Continue with the implementation of bind_menu_item. To put it simply, because a JS function is bound, you actually need to perform this binding operation in SpiderMonkey. Is a C ++ object CCMenuItemImage type). First, find the JS object corresponding to the C ++ object. If not, create a new one. Then, use the addCallBackAndThis function to perform binding.


static void addCallBackAndThis(JSObject *obj, jsval callback, jsval &thisObj){    if(callback != JSVAL_VOID) {        ScriptingCore::getInstance()->setReservedSpot(0, obj, callback);    }    if(thisObj != JSVAL_VOID) {        ScriptingCore::getInstance()->setReservedSpot(1, obj, thisObj);    }}


JSBool ScriptingCore::setReservedSpot(uint32_t i, JSObject *obj, jsval value) {    JS_SetReservedSlot(obj, i, value);    return JS_TRUE;}


Finally, we can see that the method for storing callback functions is through the ReservedSlot mechanism of SpiderMonkey. 0-bit stores the callback function, and 1-bit stores this corresponding to the callback function.


Okay. So far, the binding of all callback functions has ended.


Ii. callback function call Process


Now let's look at the process of starting the JS callback from the C ++ layer. We omit the event dispatch mechanism to directly view the call code when a key event occurs. When a key event occurs, the activate function of the MenuItemImage parent class is called. This function is in CCMenuItem. cpp.


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


It is very simple. First, determine whether the buttons are available. Then, if there is a C ++ layer callback, it will be called. If there is a script layer JS or lua) callback, it encapsulates a kMenuClickedEvent event and sends the event to the corresponding script engine.


int ScriptingCore::sendEvent(ScriptEvent* evt){    if (NULL == evt)        return 0;                                                                    JSAutoCompartment ac(_cx, _global);                                                                       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 uses ScriptingCore: sendEvent to distribute events. The kMenuClickedEvent event is distributed to the handleMenuClickedEvent function for processing.


int ScriptingCore::handleMenuClickedEvent(void* data){    if (NULL == data)        return 0;                                                              BasicScriptData* basicScriptData = static_cast<BasicScriptData*>(data);    if (NULL == basicScriptData->nativeObject)        return 0;                                                              MenuItem* menuItem = static_cast<MenuItem*>(basicScriptData->nativeObject);                                                              js_proxy_t * p = jsb_get_native_proxy(menuItem);    if (!p) return 0;    jsval retval;    jsval dataVal;    js_proxy_t *proxy = jsb_get_native_proxy(menuItem);    dataVal = (proxy ? OBJECT_TO_JSVAL(proxy->obj) : JSVAL_NULL);    executeJSFunctionFromReservedSpot(this->_cx, p->obj, dataVal, retval);    return 1;}


static void executeJSFunctionFromReservedSpot(JSContext *cx, JSObject *obj,                                              jsval &dataVal, jsval &retval) {    jsval func = JS_GetReservedSlot(obj, 0);    if (func == JSVAL_VOID) { return; }    jsval thisObj = JS_GetReservedSlot(obj, 1);    JSAutoCompartment ac(cx, obj);                          if (thisObj == JSVAL_VOID) {        JS_CallFunctionValue(cx, obj, func, 1, &dataVal, &retval);    } else {        assert(!JSVAL_IS_PRIMITIVE(thisObj));        JS_CallFunctionValue(cx, JSVAL_TO_OBJECT(thisObj), func, 1, &dataVal, &retval);    }}


Use the ReservedSlot mechanism of SpiderMonkey again to retrieve the corresponding parameters. Finally, use the JS_CallFunctionValue function to call the callback function at the JS layer.




Next

This article is from the "Old G hut" blog, please be sure to keep this source http://4137613.blog.51cto.com/4127613/1354756

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.