cocos2d-x3.0 中使用了C++11的性特性,因此回调的方式也相应地做了一些改变,因此我们需要先去了解一下C++11中的std::function,std::bind和lambda表达式,可以参考下面这篇博客http://www.cnblogs.com/nzbbody/p/3489573.html,在此还是感谢那些不辞劳苦分享经验的朋友。
一:菜单事件的回调
回到主题,我们先创建一个HelloWorld的工程并打开源代码,可以看到
// add a "close" icon to exit the progress. it's an autorelease object auto closeItem = MenuItemImage::create( "CloseNormal.png", "CloseSelected.png", CC_CALLBACK_1(HelloWorld::menuCloseCallback, this));
这个和2.X版本的回调函数有了不一样,原来是用menu_selector()的。我们查看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_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::bind的方式,其中##__VA_ARGS__表示的是可变参数列表,也就是后面还是可以定义任意个参数的,而std::placeholders则是为不预先绑定的参数预留传参的位置,这里的0,1,2,3就是表示回调函数中不事先绑定的参数的个数。
下面我们可以对Helloworld的例子来进行改写,也就是
CC_CALLBACK_1(HelloWorld::menuCloseCallback, this, 1 ,1));
void HelloWorld::menuCloseCallback(Ref* pSender, int a, int b) { //Director::getInstance()->end(); int c = a+b; CCLog("this is a test"); }
因为CC_CALLBACK_X是std::bind的宏定义,所以也可以直接使用如下所示:
auto closeItem = MenuItemImage::create( "CloseNormal.png", "CloseSelected.png",
std::bind(&HelloWorld::menuCloseCallback, this, std::placeholders::_1) );
通过查看MenuItemImage::create可以看到
MenuItemImage * MenuItemImage::create(const std::string& normalImage, const std::string& selectedImage, const ccMenuCallback& callback)接着查询ccMenuCallback&可看到
<span style="font-family:SimSun;font-size:14px;">typedef std::function<void(Ref*)> ccMenuCallback;</span>
也就是如果你喜欢,也可以使用std::function的形式来使用回调函数,如下:
std::function<void(Ref*)> callback = std::bind(&HelloWorld::menuCloseCallback, this, std::placeholders::_1); auto closeItem = MenuItemImage::create( "CloseNormal.png", "CloseSelected.png", callback );接下来就要说一种比较有趣的方法,使用lambda表达式,先看代码
auto closeItem = MenuItemImage::create( "CloseNormal.png", "CloseSelected.png", [&](Ref*pSender){CCLog("this is a test"); } );
使用这种方式有什么好处呢?我觉得这样调用的话函数是匿名的,可以防止别人的访问,同时不用我们再去创建函数,对于函数体较少的函数而言,这样的确省去一些功夫。下面我们简单说说lambda表达式的语法:
这里假设我们定义了一个如上图的lambda表达式。现在来介绍途中标有编号的各个部分是什么意思。
1. Lambda表达式的引入标志,在‘[]’里面可以填入‘=’或‘&’表示该lambda表达式“捕获”(lambda表达式在一定的scope可以访问的数据)的数据时以什么方式捕获的,‘&’表示一引用的方式;‘=’表明以值传递的方式捕获,除非专门指出。
2. Lambda表达式的参数列表
3. Mutable 标识
4. 异常标识
5. 返回值
6. “函数”体,也就是lambda表达式需要进行的实际操作二:定时器事件的回调
这个貌似和之前
2.x
的版本一样,分
3
种定时器:
1.
默认调度器
:schedulerUpdate()
添加启动定时器代码
this->scheduleUpdate();然后重载update(float dt)即可
2.
自定义调度器
:schedule(SEL_SCHEDULE selector, float interval, unsignedintrepeat, float delay)
,其中
1.
第一个参数
selector
即为你要添加的事件函数
2.
第二个参数
interval
为事件触发时间间隔
3.
第三个参数
repeat
为触发一次事件后还会触发的次数,默认值为
kRepeatForever
,表示无限触发次数
4.
第四个参数
delay
表示第一次触发之前的延时
其中回调的方式还是使
schedule_selector
的方式,如下面的例子:
<span style="font-family:SimSun;font-size:14px;"> //testthe shedule this->schedule(schedule_selector(HelloWorld::testSchedule),2.0f);</span>
不过要注意的是testSchedule要带一个float的参数,否则不起效果。
3.
单次调度器
:scheduleOnce(SEL_SCHEDULE selector, float delay)
这个使用同上,只不过只执行一次而已。
三:动作回调函数
当一个
node
执行完成了某个动作后,我们可能需要它响应一些其他的事件,这时候我们就用到回调函数这个功能。
在
2.x
的版本里,这种类型的函数有
4
种形式可供我们选择使用,分别为:
1) CCCallFunc:无参回调函数
2) CCCallFuncN:带一个CCNode*参数回调函数,“N”表示CCNode
3) CCCallFuncND:带一个CCNode*和一个void*参数的回调函数,“N”表示CCNode,"D"表示Data
4) CCCallFunO:带一个CCObject*参数的回调函数,"O"表示CCObject
使用的方式也是
callfunc_selector, callfuncX_selector
这样的形式,但是到
3.0
之后,只保留了
CallFunc
和
CallFuncN
两个动作,用来在动作中进行方法的调用。不过我们还是可以利用
CallFunc
和
CallFuncN
来等价实现
CallFuncND
和
CallFuncO
。下面我们用例子来说明使用。
//test the CallFunc //adda sprite with a normal piture autosprite = Sprite::create("CloseNormal.png"); Size winSize = Director::getInstance()->getWinSize(); sprite->setPosition(Point(winSize.width*0.5, winSize.height*0.1)); this->addChild(sprite,0); automove1 = MoveTo::create(1.5f, Point(winSize.width*0.9, winSize.height*0.5)); automove2 = MoveTo::create(1.5f, Point(winSize.width*0.5, winSize.height*0.9)); automove3 = MoveTo::create(1.5f, Point(winSize.width*0.1, winSize.height*0.5)); automove4 = MoveTo::create(1.5f, Point(winSize.width*0.5, winSize.height*0.1)); //definethe CallFunc //CallFunc autoaction1 = CallFunc::create(CC_CALLBACK_0(HelloWorld::callback1, this)); //CallFuncN autoaction2 = CallFuncN::create(CC_CALLBACK_1(HelloWorld::callback2, this)); //CallFuncND autoaction3 = CallFuncN::create(CC_CALLBACK_1(HelloWorld::callback3, this , 1)); //CallFuncO auto action4 = CallFunc::create(CC_CALLBACK_0(HelloWorld::callback4, this, sprite)); auto seq = Sequence::create(move1, action1, move2, action2, move3, action3, move4, action4, NULL);
<span style="font-size: 12px;"> </span><span style="font-family: Arial, Helvetica, sans-serif; font-size: 12px;">sprite->runAction(seq);</span>
void HelloWorld::callback1() { CCLog("the callback test begin"); } void HelloWorld::callback2(Node* node) { CCLog("the sprite's position is x=%f, y=%f", node->getPositionX(),node->getPositionY()); } void HelloWorld::callback3(Node* node, inta) { CCLog("the sprite's position is x=%f, y=%f", node->getPositionX(),node->getPositionY()); CCLog("%d",a); } void HelloWorld::callback4(Ref* ref) { autosprite = static_cast<Sprite*>(ref); CCLog("the sprite's position is x=%f, y=%f",sprite->getPositionX(), sprite->getPositionY()); }
最后看看输出,测试成功
同理,也可以像上面所提到的利用std::bind,std::function和lambda表达式来实现也可以。推荐还是lambda吧。
四:触摸回调函数
这个准备在总结触摸机制的时候再慢慢讨论拉。
文章就写到这里了,如果有理解不妥的地方,还请各位朋友提出指正,谢谢!!