643
魔獸
Cocos2d-x 3.0 新特性體驗- 回調函數的變化
在cocos2d-x 2.x版本中的回調函數的用法想必大家都很是熟悉,例如在menu item,call back action中都需要大量的使用到回調函數,但是在使用過程中總是感覺到比較冗餘麻煩的,在3.0版本,使用到了C++11 的新特性,改進增加了回到函數的使用形式,其中最令人欣慰的是,可以使用閉包,對於有過iOS開發經驗的來說,應該很親切,就是 block。
下麵將通過幾個例子詳細介紹在3.0版本中回調函數的各種用法。溫馨提示:由於用到了C++11中的std::function,std::bind和lambda表達式,所以對此不太了解的可以先看看我之前的這篇有關C++11的一些用法介紹 點擊打開鏈接 。
一、通過 HelloWorldScene 中的 closeItem 開始
在cocos2d-x 2.x 版本中:
- CCMenuItemImage *pCloseItem = CCMenuItemImage::create(
- "CloseNormal.png",
- "CloseSelected.png",
- this,
- menu_selector(HelloWorld::menuCloseCallback));
在cocos2d-x 3.0 版本中:
- auto closeItem = MenuItemImage::create(
- "CloseNormal.png",
- "CloseSelected.png",
- CC_CALLBACK_1(HelloWorld::menuCloseCallback, this));
- void HelloWorld::menuCloseCallback(Object* pSender)
- {
- Director::getInstance()->end();
- #if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS)
- exit(0);
- #endif
- }
注意到在3.0版本中使用到 CC_CALLBACK_1 這樣一個宏定義。
- // new callbacks based on C++11
- #define CC_CALLBACK_0(__selector__,__target__, ...) std::bind(&__selector__,__target__, ##__VA_ARGS__)
- #define CC_CALLBACK_1(__selector__,__target__, ...) std::bind(&__selector__,__target__, std::placeholders::_1, ##__VA_ARGS__)
- #define CC_CALCC_CALLBACK_1(HelloWorld::menuCloseCallback,this)LBACK_2(__selector__,__target__, ...) std::bind(&__selector__,__target__, std::placeholders::_1, std::placeholders::_2, ##__VA_ARGS__)
- #define CC_CALLBACK_3(__selector__,__target__, ...) std::bind(&__selector__,__target__, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3 ##__VA_ARGS__)
原來還有 CC_CALLBACK_0 1 2 3;而其中又有什麼區別呢?
1、首先我們看看3.0版本中MenuItemImage的create方法:
- MenuItemImage * MenuItemImage::create(const std::string& normalImage, const std::string& selectedImage, const ccMenuCallback& callback)
- typedef std::function<void(Object*)> ccMenuCallback
注意到 在 CC_CALLBACK_ 的宏定義的中使用到的是 C++ 的 bind 語法,怎麼不一致了呢? -- 見下麵第四點 function
2、看回 CC_CALLBACK_ 的宏定義
原來 CC_CALLBACK_ 的宏定義中後麵的 0 1 2 3分別表示的是 不事先指定回調函數參數的個數。
例如說 CC_CALLBACK_ 1 表示的是,回調函數中不事先指定參數是一個,而事先指定的回調函數的參數 可以任意多個。
而且要注意到其中 不指定回調函數參數 和 指定回調函數參數 的順序,注意不事先指定的在前,事先指定的在後。
下麵通過例子說明這一點:
假設回調函數:
- // a selector callback
- void menuCloseCallback(Object* pSender,int a,int b);
- void HelloWorld::menuCloseCallback(Object* pSender,int a,int b)
- {
- std::cout<<a<<" "<<b<<std::endl;
- Director::getInstance()->end();
- #if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS)
- exit(0);
- #endif
- }
- auto closeItem = MenuItemImage::create(
- "CloseNormal.png",
- "CloseSelected.png",
- CC_CALLBACK_1(HelloWorld::menuCloseCallback,this,1,2));
運行,在 點擊closeItem 的時候,就會輸出這兩個事先指定的參數 1 2。
那麼,不事先指定的參數是在什麼時候傳入的呢?
- void MenuItem::activate()
- {
- if (_enabled)
- {
- if( _callback )
- {
- _callback(this);
- }
- if (kScriptTypeNone != _scriptType)
- {
- BasicScriptData data(this);
- ScriptEvent scriptEvent(kMenuClickedEvent,&data);
- ScriptEngineManager::getInstance()->getScriptEngine()->sendEvent(&scriptEvent);
- }
- }
- }
這樣,closeItem 的回調函數的 void HelloWorld::menuCloseCallback(Object* pSender,int a,int b) 的三個參數都知道了。
第一個 不事先指定,在menu item調用 activate 的時候,_callback(this) 傳入,this 也即是這個 menu item;第二、三個參數是事先指定的 1,2。
3、bind
已經知道 CC_CALLBACK_ 的宏定義是 std::bind 那麼我們可以直接使用std::bind。
如下:
- auto closeItem = MenuItemImage::create(
- "CloseNormal.png",
- "CloseSelected.png",
- std::bind(&HelloWorld::menuCloseCallback, this,std::placeholders::_1,1,2));
4、function
最後就解決上麵的一個疑惑。
- std::function<void(Object*)> func = std::bind(&HelloWorld::menuCloseCallback,this, std::placeholders::_1,1,2);
- auto closeItem = MenuItemImage::create(
- "CloseNormal.png",
- "CloseSelected.png",
- func);
5、使用lambda表達式
- auto closeItem = MenuItemImage::create(
- "CloseNormal.png",
- "CloseSelected.png",
- [&](Object *sender){
- Director::getInstance()->end();
- #if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS)
- exit(0);
- #endif
- });
二、在cocos2d-x中,還有一個地方是需要大量使用到回調函數的,這就是回調動作:CCCallFunc、CCCallFuncN、CCCallFuncND、CCCallFuncO。
但是這四個回調動作在 3.0 版本中已經都提示 deprecate 了。那麼在3.0 版本中已經隻剩下 CallFunc 和 CallFuncN.
下麵是官方文檔中的說明:
-
CallFunc
can be created with anstd::function<void()>
-
CallFuncN
can be created with anstd::function<void(Node*)>
-
CallFuncND
andCallFuncO
were removed since it can be created with simulated withCallFuncN
andCallFunc
. See ActionsTest.cpp for more examples
下麵通過例子詳細的介紹這兩個回調動作的用法。
1、CallFunc
- static CallFunc * create(const std::function<void()>& func);
關於CallFunc的例子,在文檔中已經有體現:
- // in v2.1
- CCCallFunc *action1 = CCCallFunc::create( this, callfunc_selector( MyClass::callback_0 ) );
- // in v3.0 (short version)
- auto action1 = CallFunc::create( CC_CALLBACK_0(MyClass::callback_0,this));
- auto action2 = CallFunc::create( CC_CALLBACK_0(MyClass::callback_1,this, additional_parameters));
- // in v3.0 (long version)
- auto action1 = CallFunc::create( std::bind( &MyClass::callback_0, this));
- auto action2 = CallFunc::create( std::bind( &MyClass::callback_1, this, additional_parameters));
- // in v3.0 you can also use lambdas or any other "Function" object
- auto action1 = CallFunc::create(
- [&](){
- auto s = Director::sharedDirector()->getWinSize();
- auto label = LabelTTF::create("called:lambda callback", "Marker Felt", 16);
- label->setPosition(ccp( s.width/4*1,s.height/2-40));
- this->addChild(label);
- } );
2、CallFuncN
- static CallFuncN * create(const std::function<void(Node*)>& func);
注意到該回調動作帶有一個Node*參數。
假設回調函數:
- void ActionCallFuncN::callback(Node* sender )
- auto action = Sequence::create(
- MoveBy::create(2.0f, Point(150,0)),
- CallFuncN::create( CC_CALLBACK_1(ActionCallFuncN::callback, this)),
- NULL);
- auto action = Sequence::create(
- MoveBy::create(2.0f, Point(150,0)),
- CallFuncN::create(std::bind(&ActionCallFuncN::callback,this,std::placeholders::_1)),
- NULL);
- auto action = Sequence::create(
- MoveBy::create(2.0f, Point(150,0)),
- CallFuncN::create([&](Node* sender){
- //回調動作代碼
- }),
- NULL);
受益於C++11的新語法特性 std::bind ; CallFuncND 和 CallFuncO 都可以通過 CallFunc 和 CallFuncN 進行實現
3、CallFuncND :回調動作中帶有一個Node*參數和一個void*參數
實現過程類似於 CallFuncN
假設回調函數是 :void ActionCallFuncND::doRemoveFromParentAndCleanup(Node* sender, bool cleanup)
那麼在回調動作中:
CallFuncN::create( CC_CALLBACK_1(ActionCallFuncND::doRemoveFromParentAndCleanup, this, true))
這樣就實現了等價於 CallFuncND 的回調動作。
4、CallFuncO :回調動作中帶有一個Object*參數
實現過程類似於 CallFunc
假設回調函數是: void ActionCallFuncO::callback(Node* node, bool cleanup)
那麼在回調動作中:
CallFunc::create( CC_CALLBACK_0(ActionCallFuncO::callback, this, _grossini, true)
這樣就實現了等價於 CallFuncO 的回調動作。
三、總結
在新版的回調處理中,采用了C++11中的 std::function 、std::bind 、lambda 表達式,使得回調的處理變得形式多樣,代碼靈活了,而其中的lambda表達式可以極大的簡化回調代碼,推薦使用。
最後更新:2017-04-03 12:56:03