一:菜单事件的回调
回到主题,我们先创建一个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)
- <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
- );
- 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);
- 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吧。
auto move = MoveTo::create(0.05, Point(Point(visible.width/2 + origin.x - mfWidth*mpUpComputerSelectArr.size()/2, visible.height/2 - 100)));
auto time = DelayTime::create(0.05);
auto sequence = Sequence::create(move, time, CallFuncN::create(CC_CALLBACK_1(GameScene::setAnimationOfOutPoker, this, 1)), NULL);
sp->runAction(sequence);
void GameScene::setAnimationOfOutPoker(Ref* r, int player){
}