【cocos2d-x 3.x 學習與應用總結】4: 理解CC_CALLBACK_0, CC_CALLBACK_1, CC_CALLBACK_2, CC_CALLBACK_3
前言
得益於C++11的新特性,cocos 3.x版本在很多地方的程式碼看起來都優美了許多。這其中就包括一些回撥函式的寫法,CC_CALLBACK_N
系列巨集的作用是對一個成員函式進行適配並返回一個回撥函式。本文介紹一下我對CC_CALLBACK_N
系列巨集的理解。
使用CC_CALLBACK_N
的例子
下面這段程式碼來自cocos官方示例中的ActionTest.cpp, 這是在建立一個CallFunc的回撥。
使用CC_CALLBACK_0
來代替其原來的建立回撥的方式:
使用CC_CALLBACK_0
來改寫上面三個回撥的建立:
auto action1 = Sequence::create(
MoveBy::create(2 , Vec2(200,0)),
//CallFunc::create(std::bind(&ActionCallFunction::callback1, this)), 原來的方式
CallFunc::create(CC_CALLBACK_0(ActionCallFunction::callback1, this)),
CallFunc::create(
// lambda
[&](){
auto s = Director::getInstance()->getWinSize();
auto label = Label::createWithTTF("called:lambda callback", "fonts/Marker Felt.ttf", 16.0f);
label->setPosition(s.width/4*1,s.height/2-40);
this->addChild(label);
} ),
nullptr );
auto action2 = Sequence::create(
ScaleBy::create(2 , 2),
FadeOut::create(2),
//CallFunc::create(std::bind(&ActionCallFunction::callback2, this, _tamara)), 原來的方式
CallFunc::create(CC_CALLBACK_0(ActionCallFunction::callback2, this, _tamara)),
nullptr);
auto action3 = Sequence::create(
RotateBy::create(3 , 360),
FadeOut::create(2),
//CallFunc::create(std::bind(&ActionCallFunction::callback3, this, _kathia, 42)), 原來的方式
CallFunc::create(CC_CALLBACK_0(ActionCallFunction::callback3, this, _kathia, 42)),
nullptr);
void ActionCallFunction::callback1()
{
auto s = Director::getInstance()->getWinSize();
auto label = Label::createWithTTF("callback 1 called", "fonts/Marker Felt.ttf", 16.0f);
label->setPosition(s.width/4*1,s.height/2);
addChild(label);
}
void ActionCallFunction::callback2(Node* sender)
{
auto s = Director::getInstance()->getWinSize();
auto label = Label::createWithTTF("callback 2 called", "fonts/Marker Felt.ttf", 16.0f);
label->setPosition(s.width/4*2,s.height/2);
addChild(label);
CCLOG("sender is: %p", sender);
}
void ActionCallFunction::callback3(Node* sender, long data)
{
auto s = Director::getInstance()->getWinSize();
auto label = Label::createWithTTF("callback 3 called", "fonts/Marker Felt.ttf", 16.0f);
label->setPosition(s.width/4*3,s.height/2);
addChild(label);
CCLOG("target is: %p, data is: %ld", sender, data);
}
如何理解CC_CALLBACK_0
, CC_CALLBACK_1
, CC_CALLBACK_2
, CC_CALLBACK_3
這個CC_CALLBACK_0
其實就是std::bind,下面是它和它的小夥伴們:
defined in ccMacro.h
// new callbacks based on C++11
using std::bind;
using std::placeholders::_1;
using std::placeholders::_2;
using std::placeholders::_3;
#define CC_CALLBACK_0(__selector__,__target__, ...) bind(&__selector__,__target__, ##__VA_ARGS__)
#define CC_CALLBACK_1(__selector__,__target__, ...) bind(&__selector__,__target__, _1, ##__VA_ARGS__)
#define CC_CALLBACK_2(__selector__,__target__, ...) bind(&__selector__,__target__, _1, _2, ##__VA_ARGS__)
#define CC_CALLBACK_3(__selector__,__target__, ...) bind(&__selector__,__target__, _1, _2, _3, ##__VA_ARGS__)
為了讓這幾個巨集看起來更清晰,在上面我使用了using宣告整理了一下程式碼。
這四個巨集的作用都是用來適配函式,把一個原始函式A,包裝成函式B。這裡面的A需要是一個類的成員函式,其中的:
__selector__
就是這個成員函式,比如MyClass::func;
__target__
是MyClass型別的一個物件(或者是物件的引用和指標,比如最常見的this)
如何理解這幾個引數的命名呢?為什麼是0, 1, 2, 3?
是這樣的,結尾的數字N,代表者CC_CALLBACK_N
這個巨集返回的結果是一個需要N個引數的函式。
CC_CALLBACK_<font color="red">0</font>
意思就是返回一個需要0個引數方可呼叫的函式, 也就是說不需要引數就能呼叫的函式CC_CALLBACK_<font color="red">1</font>
意思就是返回一個需要1個引數方可呼叫的函式CC_CALLBACK_<font color="red">2</font>
意思就是返回一個需要2個引數方可呼叫的函式CC_CALLBACK_<font color="red">3</font>
意思就是返回一個需要3個引數方可呼叫的函式
這裡需要的引數個數其實也就是佔位符的個數(_1, _2, _3
), 佔位符是需要在函式呼叫的時候用具體的實參來替換的。
這樣理解了之後,就很容易知道什麼時候該用哪個巨集了。
比如,我要建立一個CallFunc, static CallFunc * create(const std::function<void()>& func);
, 從其宣告可以看出它需要一個不用引數就能呼叫的函式,那麼我就可以用CC_CALLBACK_0
。
auto action2 = Sequence::create(
ScaleBy::create(2 , 2),
FadeOut::create(2),
//CallFunc::create(std::bind(&ActionCallFunction::callback2, this, _tamara)), 原來的方式
CallFunc::create(CC_CALLBACK_0(ActionCallFunction::callback2, this, _tamara)),
nullptr);
void ActionCallFunction::callback2(Node* sender)
{
auto s = Director::getInstance()->getWinSize();
auto label = Label::createWithTTF("callback 2 called", "fonts/Marker Felt.ttf", 16.0f);
label->setPosition(s.width/4*2,s.height/2);
addChild(label);
CCLOG("sender is: %p", sender);
}
即使callback2接收N個引數,我也可以使用CC_CALLBACK_0
來把它適配成一個不需要引數就能呼叫的函式。這是std::bind的工作方式決定的,我只需把callback2需要的引數填入CC_CALLBACK_0
裡面就好。
auto action2 = Sequence::create(
ScaleBy::create(2 , 2),
FadeOut::create(2),
CallFunc::create(CC_CALLBACK_0(ActionCallFunction::callback5, this, 100, 200, 11, 12, 23)),
nullptr);
void ActionCallFunction::callback5(int i1, int i2, int i3, int i4, int i5)
{
// ...
}
CC_CALLBACK_1
在CallFuncN::create中的使用
CallFuncN::create的原型:
static CallFuncN * create(const std::function<void(Node*)>& func);
從create的引數可以看到,它需要一個“需要一個Node*引數的引數方可呼叫的函式”, 這剛好和前面講到的CC_CALLBACK_1
的作用一樣。
void ActionCallFuncND::onEnter()
{
// ActionCallFuncND::doRemoveFromParentAndCleanup本來是需要兩個引數:(Node*, bool), 使用CC_CALLBACK_1把第二個引數繫結為true,
// 這樣就變成了一個僅需要一個Node*引數的函式。
auto action = Sequence::create(
MoveBy::create(2.0f, Vec2(200,0)),
CallFuncN::create( CC_CALLBACK_1(ActionCallFuncND::doRemoveFromParentAndCleanup, this, true)),
nullptr);
// 這是action的等價定義,可以看到佔位符_1頂替了Node*的位置。
auto action2 = Sequence::create(
MoveBy::create(2.0f, Vec2(200, 0)),
CallFuncN::create(std::bind(&ActionCallFuncND::doRemoveFromParentAndCleanup, this, std::placeholders::_1, true)),
nullptr);
_grossini->runAction(action2);
}
void ActionCallFuncND::doRemoveFromParentAndCleanup(Node* sender, bool cleanup)
{
_grossini->removeFromParentAndCleanup(cleanup);
}
CC_CALLBACK_2
, CC_CALLBACK_3
的使用方式與此類似,不再贅述。
作者水平有限,對相關知識的理解和總結難免有錯誤,還望給予指正,非常感謝!