Cocos2dx学习笔记(25)——回调函数

之前我们在学习的过程中,总是提到回调函数这个概念,那么就让我们来了解一下Cocos中的回调函数吧。

本文仅供个人记录和复习,不用于其他用途

函数适配器

std::bind是C++11的函数适配器,我的另一篇文章中也介绍过。那么什么是函数适配器呢?比如我们使用find_if()查找vector中的某些元素时,需要我们提供一个一元函数对象。但是呢,如果我们提供的是二元函数对象,那么就无法使用find_if()。为了将二元函数对象转换成一元函数对象,我们使用函数适配器。

1
2
3
4
5
6
vector<int>::iterator ite = ivec.begin();
while((ite = find_if(ite, ivec.end(), bind2nd(greater<int>(), 5))) != ivec.end())
{
cout << *ite << " ";
ite++;
}

这里的greater<>()是一个二元函数对象,我们把5绑定到第二个参数。这样,find_if()只需传递一个参数给函数对象。

可能有人有疑问,为什么会提供一个二元函数对象给find_if(),而不是直接写一个一元函数对象呢?那么举个简单的例子,我们提供的函数对象用于查找vector中大于5的数,那么你当然可以把vector中的元素和5比较。但如果我需要和6、7或者其他数比较呢?每次都要修改的话十分麻烦,而且如果你不能修改源码的话,那么就是一个很头疼的问题。这样,我们就只好写一个二元函数,而函数适配器的作用就体现出来了。

新版本回调函数

新版本的回调函数有以下几种:

  • CC_CALLBACK_0
  • CC_CALLBACK_1
  • CC_CALLBACK_2
  • CC_CALLBACK_3

它们的区别就在于参数的多少,最后的数字就是参数的个数。之前的menu_selector之类的回调函数将被这些所取代。

回调函数与bind

说了这么多,最主要的还是得讲一讲bind和回调函数的关系。

1
2
3
4
#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_CALLBACK_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__)

std::placeholders::_1是不定参数,在调用的时候传入,而##VA_ARGS是可变参数宏,至于回调函数其实就是bind。bind的使用方法如下:

1
auto newCallBack = bind(callback, arg_list);

arg_list就是可变参数列表,我们在调用newCallBack时,newCallBack就会调用callback,并且将arg_list传入到callback。我们再来说得详细一点:

1
2
3
int callback(int one, char two, double three);
auto newCallBack = bind(callback, _1, _2, 1.5);
int x = newCallBack(10,'h'); // 这句相当于:int x = callback(10,'h',1.5);

这里_1代表着一个占位符对象,当callback通过newCallBack被调用时,指代newCallBack中的第一个参数在函数callback的参数列表中的位置。newCallBack的第一个参数就是_1,后面以此类推。1.5就是默认参数,这里就代表着three。_1等占位符定义在placeholders命名空间中,完整的使用就是std::placeholders::_1

变更的回调函数

理清了回调函数与bind的关系,接下来就看看哪些回调函数被弃用了:

  • 动作函数:callfunc_selectorcallfuncN_selectorcallfuncND_selector
  • 菜单项函数:menu_selector
  • 触摸函数:onTouchBeganonTouchMovedonTouchEnded

下面将一一介绍。

动作函数CallFunc

  • CallFunc:使用CC_CALLBACK_0
  • CallFuncN:使用CC_CALLBACK_1,不定参数为执行动作的对象
  • CallFuncND: 被弃用,用CallFuncN代替,使用CC_CALLBACK_1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// 2.x版本
CallFunc::create (this, callfunc_selector(HelloWorld::callback0));
CCCallFuncN::create (this, callfuncN_selector(HelloWorld::callback1));
CCCallFuncND::create(this, callfuncND_selector(HelloWorld::callback2), (void *)10);
void HelloWorld::callback0() {} // CCCallFunc回调函数
void HelloWorld::callback1(CCNode* node) {} // CCCallFuncN回调函数
void HelloWorld::callback2(CCNode* node,void* a) {} // CCCallFuncND回调函数,参数必须为void*
// 3.x版本中CallFuncND被弃用,只能使用CallFuncN代替
CallFunc::create (CC_CALLBACK_0(HelloWorld::callback0, this));
CallFuncN::create(CC_CALLBACK_1(HelloWorld::callback1, this));
CallFuncN::create(CC_CALLBACK_1(HelloWorld::callback2, this, 0.5));
// bind也可以实现,sprite为执行该动作的精灵
CallFunc::create (std::bind(&HelloWorld::callback0, this ) );
CallFuncN::create(std::bind(&HelloWorld::callback1, this, sprite);
CallFuncN::create(std::bind(&HelloWorld::callback2, this, sprite, 0.5));
void HelloWorld::callback0() {}
void HelloWorld::callback1(Node* node) {}
void HelloWorld::callback2(Node* node, float a) {} // 可自定义参数类型float

菜单项回调menu_selector

1
2
3
4
5
6
7
8
// CC_CALLBACK_1
MenuItemImage::create("1.png", "2.png", CC_CALLBACK_1(HelloWorld::callback1, this));
// std::bind
MenuItemImage::create("1.png", "2.png", std::bind(&HelloWorld::callback1, this, std::placeholders::_1));
// 回调函数
void HelloWorld::callback(Node* sender) {}

触摸函数

1
2
3
4
5
6
7
8
9
10
11
12
// 创建一个事件监听器类型为 单点触摸
auto touchLisner = EventListenerTouchOneByOne::create();
// 绑定事件
touchLisner->onTouchBegan = CC_CALLBACK_2(HelloWorld::onTouchBegan, this);
touchLisner->onTouchMoved = CC_CALLBACK_2(HelloWorld::onTouchMoved, this);
touchLisner->onTouchEnded = CC_CALLBACK_2(HelloWorld::onTouchEnded, this);
// 回调函数
virtual bool HelloWorld::onTouchBegan(Touch *touch, Event *unused_event);
virtual void HelloWorld::onTouchMoved(Touch *touch, Event *unused_event);
virtual void HelloWorld::onTouchEnded(Touch *touch, Event *unused_event);

总结

除了上述所说的回调函数,其他的都没有发生变化,依旧使用原来的回调函数。