In the previous article, we talked about Button callback. This time we will talk about various logical callback functions.
There are a total of three types of callback functions in the Cocos2d-x, the first is the key callback CCMenu related, the second is the timer-related callback
Schedule. The third type is Action-related callback CallFunc. These callbacks exist from the original engine version until now.
I. Bind code
In the JSB solution, for the last two types of functions, the engine is encapsulated into JSCallbackWrapper and its subclass.
class JSCallbackWrapper: public cocos2d::Object {public: JSCallbackWrapper(); virtual ~JSCallbackWrapper(); void setJSCallbackFunc(jsval obj); void setJSCallbackThis(jsval thisObj); void setJSExtraData(jsval data); const jsval& getJSCallbackFunc() const; const jsval& getJSCallbackThis() const; const jsval& getJSExtraData() const;protected: jsval _jsCallback; jsval _jsThisObj; jsval _extraData;};
JSCallbackWrapper is the JS callback function package. The three interfaces are also clear at a glance. The callback function, this, and external data.
// cc.CallFunc.create( func, this, [data])// cc.CallFunc.create( func )static JSBool js_callFunc(JSContext *cx, uint32_t argc, jsval *vp){ if (argc >= 1 && argc <= 3) { jsval *argv = JS_ARGV(cx, vp); std::shared_ptr<JSCallbackWrapper> tmpCobj(new JSCallbackWrapper()); tmpCobj->setJSCallbackFunc(argv[0]); if(argc >= 2) { tmpCobj->setJSCallbackThis(argv[1]); } if(argc == 3) { tmpCobj->setJSExtraData(argv[2]); } CallFuncN *ret = CallFuncN::create([=](Node* sender){ const jsval& jsvalThis = tmpCobj->getJSCallbackThis(); const jsval& jsvalCallback = tmpCobj->getJSCallbackFunc(); const jsval& jsvalExtraData = tmpCobj->getJSExtraData(); bool hasExtraData = !JSVAL_IS_VOID(jsvalExtraData); JSObject* thisObj = JSVAL_IS_VOID(jsvalThis) ? nullptr : JSVAL_TO_OBJECT(jsvalThis); JSB_AUTOCOMPARTMENT_WITH_GLOBAL_OBJCET js_proxy_t *proxy = js_get_or_create_proxy<cocos2d::Node>(cx, sender); jsval retval; if(jsvalCallback != JSVAL_VOID) { if (hasExtraData) { jsval valArr[2]; valArr[0] = OBJECT_TO_JSVAL(proxy->obj); valArr[1] = jsvalExtraData; JS_AddValueRoot(cx, valArr); JS_CallFunctionValue(cx, thisObj, jsvalCallback, 2, valArr, &retval); JS_RemoveValueRoot(cx, valArr); } else { jsval senderVal = OBJECT_TO_JSVAL(proxy->obj); JS_AddValueRoot(cx, &senderVal); JS_CallFunctionValue(cx, thisObj, jsvalCallback, 1, &senderVal, &retval); JS_RemoveValueRoot(cx, &senderVal); } } // I think the JSCallFuncWrapper isn't needed. // Since an action will be run by a cc.Node, it will be released at the Node::cleanup. // By James Chen // JSCallFuncWrapper::setTargetForNativeNode(node, (JSCallFuncWrapper *)this); }); js_proxy_t *proxy = js_get_or_create_proxy<cocos2d::CallFunc>(cx, ret); JS_SET_RVAL(cx, vp, OBJECT_TO_JSVAL(proxy->obj)); JS_SetReservedSlot(proxy->obj, 0, argv[0]); if(argc > 1) { JS_SetReservedSlot(proxy->obj, 1, argv[1]); }// if(argc == 3) {// JS_SetReservedSlot(proxy->obj, 2, argv[2]);// } // test->execute(); return JS_TRUE; } JS_ReportError(cx, "Invalid number of arguments"); return JS_FALSE;}
This is the JS layer that calls cc. callFunc. at create, the C ++ functions executed at the underlying layer use some features of c ++ 11, including std: shared_ptr smart pointers and lambda expressions, if you are not familiar with shoes, you can find your own materials ).
The callback function is encapsulated in the lambda expression, and the external tmpCobj variable is referenced by the = method. This method is similar to the closure of JS. Still use JS_CallFunctionValue for function calling. Note that this call method is similar to the apply method in JS.
A pair of functions are very interesting here, JS_AddValueRoot and JS_RemoveValueRoot. The two functions JS_CallFunctionValue are called and wrapped. Because the valArr or senderVal is generated temporarily on the stack, the corresponding root is not specified. However, the JS function is called in the middle, so these two values may be reclaimed by the SpiderMonkey VM when the JS function is called. You can check the principles of the JS garbage collection mechanism ). So we need to give them a root account to protect them from being recycled.
Ii. Call Code
Let's take a look at the constructor.
CallFuncN * CallFuncN::create(const std::function<void(Node*)> &func){ auto ret = new CallFuncN(); if (ret && ret->initWithFunction(func) ) { ret->autorelease(); return ret; } CC_SAFE_DELETE(ret); return nullptr;}
bool CallFuncN::initWithFunction(const std::function<void (Node *)> &func){ _functionN = func; return true;}
The passed lambda expression is saved asstd::function<
void
(Node *)> type.
The call code is very simple. Use _ functionN to call the code.
void CallFuncN::execute() { if (_callFuncN) { (_selectorTarget->*_callFuncN)(_target); } else if (_functionN) { _functionN(_target); }}
Compared with the method in the previous article, I think this call method is more reasonable, because this call method hides the script Mechanism for the C ++ layer Core code. The previous call method is shown to be called by the script engine.
After reading this article and the previous article, we carefully analyzed the writing of the callback function in the Cocos2d-x JSB, detailed for the reader to implement a callback function is not particularly difficult.
When I just finished this article, I suddenly found that there was such a post about the JSB callback function, which was well written and still IAP. It can be used as an additional reading reference:
Cocos2d-x Using iOS in-game paid IAP (JSB)
There is another article to learn:
Parameter construction note for JS callback functions -- Web Learning note 18)
Let's talk about the callback function first.
Next, let's continue to discuss how to register a function.
This article is from the "Old G hut" blog, please be sure to keep this source http://4137613.blog.51cto.com/4127613/1354903