天天看點

cocos2d-x學習筆記——cocos2d-x3.0回調函數的變化

原文轉自Cocos2d-x 3.0 新特性體驗(2) 回調函數的變化

在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 版本中:

[cpp]  view plain copy

cocos2d-x學習筆記——cocos2d-x3.0回調函數的變化
cocos2d-x學習筆記——cocos2d-x3.0回調函數的變化
  1. CCMenuItemImage *pCloseItem = CCMenuItemImage::create(  
  2.                                         "CloseNormal.png",  
  3.                                         "CloseSelected.png",  
  4.                                         this,  
  5.                                         menu_selector(HelloWorld::menuCloseCallback));  

在cocos2d-x 3.0 版本中:

[cpp]  view plain copy

cocos2d-x學習筆記——cocos2d-x3.0回調函數的變化
cocos2d-x學習筆記——cocos2d-x3.0回調函數的變化
  1. auto closeItem = MenuItemImage::create(  
  2.                                            "CloseNormal.png",  
  3.                                            "CloseSelected.png",  
  4.                                            CC_CALLBACK_1(HelloWorld::menuCloseCallback, this));  

[cpp]  view plain copy

cocos2d-x學習筆記——cocos2d-x3.0回調函數的變化
cocos2d-x學習筆記——cocos2d-x3.0回調函數的變化
  1. void HelloWorld::menuCloseCallback(Object* pSender)  
  2. {  
  3.     Director::getInstance()->end();  
  4. #if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS)  
  5.     exit(0);  
  6. #endif  
  7. }  

注意到在3.0版本中使用到 CC_CALLBACK_1 這樣一個宏定義。

[cpp]  view plain copy

cocos2d-x學習筆記——cocos2d-x3.0回調函數的變化
cocos2d-x學習筆記——cocos2d-x3.0回調函數的變化
  1. // new callbacks based on C++11  
  2. #define CC_CALLBACK_0(__selector__,__target__, ...) std::bind(&__selector__,__target__, ##__VA_ARGS__)  
  3. #define CC_CALLBACK_1(__selector__,__target__, ...) std::bind(&__selector__,__target__, std::placeholders::_1, ##__VA_ARGS__)  
  4. #define CC_CALCC_CALLBACK_1(HelloWorld::menuCloseCallback,this)LBACK_2(__selector__,__target__, ...) std::bind(&__selector__,__target__, std::placeholders::_1, std::placeholders::_2, ##__VA_ARGS__)  
  5. #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方法:

[cpp]  view plain copy

cocos2d-x學習筆記——cocos2d-x3.0回調函數的變化
cocos2d-x學習筆記——cocos2d-x3.0回調函數的變化
  1. MenuItemImage * MenuItemImage::create(const std::string& normalImage, const std::string& selectedImage, const ccMenuCallback& callback)  

其中的回調參數是 ccMenuCallback 

[cpp]  view plain copy

cocos2d-x學習筆記——cocos2d-x3.0回調函數的變化
cocos2d-x學習筆記——cocos2d-x3.0回調函數的變化
  1. typedef std::function<void(Object*)> ccMenuCallback  

原來這裡使用到了 C++ 中的 function 文法。

注意到 在 CC_CALLBACK_  的宏定義的中使用到的是 C++ 的 bind 文法,怎麼不一緻了呢? -- 見下面第四點 function

2、看回 CC_CALLBACK_  的宏定義

原來 CC_CALLBACK_  的宏定義中後面的 0 1 2 3分别表示的是 不事先指定回調函數參數的個數。

例如說 CC_CALLBACK_ 1 表示的是,回調函數中不事先指定參數是一個,而事先指定的回調函數的參數 可以任意多個。

而且要注意到其中 不指定回調函數參數  和  指定回調函數參數  的順序,注意不事先指定的在前,事先指定的在後。

下面通過例子說明這一點:

假設回調函數:

[cpp]  view plain copy

cocos2d-x學習筆記——cocos2d-x3.0回調函數的變化
cocos2d-x學習筆記——cocos2d-x3.0回調函數的變化
  1. // a selector callback  
  2.     void menuCloseCallback(Object* pSender,int a,int b);  

[cpp]  view plain copy

cocos2d-x學習筆記——cocos2d-x3.0回調函數的變化
cocos2d-x學習筆記——cocos2d-x3.0回調函數的變化
  1. void HelloWorld::menuCloseCallback(Object* pSender,int a,int b)  
  2. {  
  3.     std::cout<<a<<"  "<<b<<std::endl;  
  4.     Director::getInstance()->end();  
  5. #if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS)  
  6.     exit(0);  
  7. #endif  
  8. }  

注意到在回調函數中輸出 a b

[cpp]  view plain copy

cocos2d-x學習筆記——cocos2d-x3.0回調函數的變化
cocos2d-x學習筆記——cocos2d-x3.0回調函數的變化
  1. auto closeItem = MenuItemImage::create(  
  2.                                            "CloseNormal.png",  
  3.                                            "CloseSelected.png",  
  4.                                            CC_CALLBACK_1(HelloWorld::menuCloseCallback,this,1,2));  

注意中其中 指定了兩個參數 1 2 

運作,在 點選closeItem  的時候,就會輸出這兩個事先指定的參數 1  2。

那麼,不事先指定的參數是在什麼時候傳入的呢?

[cpp]  view plain copy

cocos2d-x學習筆記——cocos2d-x3.0回調函數的變化
cocos2d-x學習筆記——cocos2d-x3.0回調函數的變化
  1. void MenuItem::activate()  
  2. {  
  3.     if (_enabled)  
  4.     {  
  5.         if( _callback )  
  6.         {  
  7.             _callback(this);  
  8.         }  
  9.         if (kScriptTypeNone != _scriptType)  
  10.         {  
  11.             BasicScriptData data(this);  
  12.             ScriptEvent scriptEvent(kMenuClickedEvent,&data);  
  13.             ScriptEngineManager::getInstance()->getScriptEngine()->sendEvent(&scriptEvent);  
  14.         }  
  15.     }  
  16. }  

注意到其中的  _callback(this);  對了,這個時候就傳入了 這個不事先指定的回調函數參數。

這樣,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。

如下:

[cpp]  view plain copy

cocos2d-x學習筆記——cocos2d-x3.0回調函數的變化
cocos2d-x學習筆記——cocos2d-x3.0回調函數的變化
  1. auto closeItem = MenuItemImage::create(  
  2.                                            "CloseNormal.png",  
  3.                                            "CloseSelected.png",  
  4.                                            std::bind(&HelloWorld::menuCloseCallback, this,std::placeholders::_1,1,2));  

4、function

最後就解決上面的一個疑惑。

[cpp]  view plain copy

cocos2d-x學習筆記——cocos2d-x3.0回調函數的變化
cocos2d-x學習筆記——cocos2d-x3.0回調函數的變化
  1. std::function<void(Object*)> func = std::bind(&HelloWorld::menuCloseCallback,this, std::placeholders::_1,1,2);  
  2.     auto closeItem = MenuItemImage::create(  
  3.                                            "CloseNormal.png",  
  4.                                            "CloseSelected.png",  
  5.                                            func);  

5、使用lambda表達式

[cpp]  view plain copy

cocos2d-x學習筆記——cocos2d-x3.0回調函數的變化
cocos2d-x學習筆記——cocos2d-x3.0回調函數的變化
  1. auto closeItem = MenuItemImage::create(  
  2.                                            "CloseNormal.png",  
  3.                                            "CloseSelected.png",  
  4.                                            [&](Object *sender){  
  5.                                                Director::getInstance()->end();  
  6.                                                #if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS)  
  7.                                                exit(0);  
  8.                                                #endif  
  9.                                            });  

可見使用lambda表達式可以極大的簡化代碼程式,不需要再定義一個回調函數,直接将在回調中的操作在閉包中展現即可。

二、在cocos2d-x中,還有一個地方是需要大量使用到回調函數的,這就是回調動作:CCCallFunc、CCCallFuncN、CCCallFuncND、CCCallFuncO。

但是這四個回調動作在 3.0 版本中已經都提示 deprecate 了。那麼在3.0 版本中已經隻剩下 CallFunc 和 CallFuncN.

下面是官方文檔中的說明:

  • CallFunc

     can be created with an 

    std::function<void()>

  • CallFuncN

     can be created with an 

    std::function<void(Node*)>

  • CallFuncND

     and 

    CallFuncO

     were removed since it can be created with simulated with 

    CallFuncN

     and 

    CallFunc

    . See ActionsTest.cpp for more examples

其中:CallFuncND 和 CallFuncO 都可以通過 CallFunc 和 CallFuncN 進行實作。

下面通過例子詳細的介紹這兩個回調動作的用法。

1、CallFunc

[cpp]  view plain copy

cocos2d-x學習筆記——cocos2d-x3.0回調函數的變化
cocos2d-x學習筆記——cocos2d-x3.0回調函數的變化
  1. static CallFunc * create(const std::function<void()>& func);  

關于CallFunc的例子,在文檔中已經有展現:

[cpp]  view plain copy

cocos2d-x學習筆記——cocos2d-x3.0回調函數的變化
cocos2d-x學習筆記——cocos2d-x3.0回調函數的變化
  1. // in v2.1  
  2. CCCallFunc *action1 = CCCallFunc::create( this, callfunc_selector( MyClass::callback_0 ) );  
  3. // in v3.0 (short version)  
  4. auto action1 = CallFunc::create( CC_CALLBACK_0(MyClass::callback_0,this));  
  5. auto action2 = CallFunc::create( CC_CALLBACK_0(MyClass::callback_1,this, additional_parameters));  
  6. // in v3.0 (long version)  
  7. auto action1 = CallFunc::create( std::bind( &MyClass::callback_0, this));  
  8. auto action2 = CallFunc::create( std::bind( &MyClass::callback_1, this, additional_parameters));  
  9. // in v3.0 you can also use lambdas or any other "Function" object  
  10. auto action1 = CallFunc::create(  
  11.                  [&](){  
  12.                      auto s = Director::sharedDirector()->getWinSize();  
  13.                      auto label = LabelTTF::create("called:lambda callback", "Marker Felt", 16);  
  14.                      label->setPosition(ccp( s.width/4*1,s.height/2-40));  
  15.                      this->addChild(label);  
  16.                  }  );  

2、CallFuncN

[cpp]  view plain copy

cocos2d-x學習筆記——cocos2d-x3.0回調函數的變化
cocos2d-x學習筆記——cocos2d-x3.0回調函數的變化
  1. static CallFuncN * create(const std::function<void(Node*)>& func);  

注意到該回調動作帶有一個Node*參數。

假設回調函數:

[cpp]  view plain copy

cocos2d-x學習筆記——cocos2d-x3.0回調函數的變化
cocos2d-x學習筆記——cocos2d-x3.0回調函數的變化
  1. void ActionCallFuncN::callback(Node* sender )  

[cpp]  view plain copy

cocos2d-x學習筆記——cocos2d-x3.0回調函數的變化
cocos2d-x學習筆記——cocos2d-x3.0回調函數的變化
  1. auto action = Sequence::create(  
  2.        MoveBy::create(2.0f, Point(150,0)),  
  3.        CallFuncN::create( CC_CALLBACK_1(ActionCallFuncN::callback, this)),  
  4.        NULL);  

[cpp]  view plain copy

cocos2d-x學習筆記——cocos2d-x3.0回調函數的變化
cocos2d-x學習筆記——cocos2d-x3.0回調函數的變化
  1. auto action = Sequence::create(  
  2.                                    MoveBy::create(2.0f, Point(150,0)),  
  3.                                    CallFuncN::create(std::bind(&ActionCallFuncN::callback,this,std::placeholders::_1)),  
  4.                                    NULL);  

[cpp]  view plain copy

cocos2d-x學習筆記——cocos2d-x3.0回調函數的變化
cocos2d-x學習筆記——cocos2d-x3.0回調函數的變化
  1. auto action = Sequence::create(  
  2.                                    MoveBy::create(2.0f, Point(150,0)),  
  3.                                    CallFuncN::create([&](Node* sender){  
  4.                                     //回調動作代碼  
  5.                                     }),  
  6.                                    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表達式可以極大的簡化回調代碼,推薦使用。

原文轉自Cocos2d-x 3.0 新特性體驗(2) 回調函數的變化