热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Cocos2dx3.0新特性体验(2)回调函数的变化

在cocos2d-x2.x版本中的回调函数的用法想必大家都很是熟悉,例如在menuitem,callbackaction中都需要大量的使用到回调函数,但是在使用过程中总是感觉到比较

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

CCMenuItemImage *pCloseItem = CCMenuItemImage::create(
"CloseNormal.png",
"CloseSelected.png",
this,
menu_selector(HelloWorld::menuCloseCallback));

在cocos2d-x 3.0 版本中:

auto closeItem = MenuItemImage::create(
"CloseNormal.png",
"CloseSelected.png",
CC_CALLBACK_1(HelloWorld::menuCloseCallback, this));

void HelloWorld::menuCloseCallback(Object* pSender)
{
Director::getInstance()->end();
#if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS)
exit(0);
#endif
}
注意到在3.0版本中使用到 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_CALCC_CALLBACK_1(HelloWorld::menuCloseCallback,this)LBACK_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__)
原来还有 CC_CALLBACK_0 1 2 3;而其中又有什么区别呢?

1、首先我们看看3.0版本中MenuItemImage的create方法:

MenuItemImage * MenuItemImage::create(const std::string& normalImage, const std::string& selectedImage, const ccMenuCallback& callback)其中的回调参数是 ccMenuCallback 

typedef std::function ccMenuCallback原来这里使用到了 C++ 中的 function 语法。

注意到 在 CC_CALLBACK_  的宏定义的中使用到的是 C++ 的 bind 语法,怎么不一致了呢? -- 见下面第四点 function


2、看回 CC_CALLBACK_  的宏定义

原来 CC_CALLBACK_  的宏定义中后面的 0 1 2 3分别表示的是 不事先指定回调函数参数的个数。

例如说 CC_CALLBACK_ 1 表示的是,回调函数中不事先指定参数是一个,而事先指定的回调函数的参数 可以任意多个

而且要注意到其中 不指定回调函数参数  和  指定回调函数参数  的顺序,注意不事先指定的在前,事先指定的在后

下面通过例子说明这一点:

假设回调函数:

// a selector callback
void menuCloseCallback(Object* pSender,int a,int b);


void HelloWorld::menuCloseCallback(Object* pSender,int a,int b)
{
std::cout< Director::getInstance()->end();
#if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS)
exit(0);
#endif
}注意到在回调函数中输出 a b

auto closeItem = MenuItemImage::create(
"CloseNormal.png",
"CloseSelected.png",
CC_CALLBACK_1(HelloWorld::menuCloseCallback,this,1,2));注意中其中 指定了两个参数 1 2 

运行,在 点击closeItem  的时候,就会输出这两个事先指定的参数 1  2。

那么,不事先指定的参数是在什么时候传入的呢?

void MenuItem::activate()
{
if (_enabled)
{
if( _callback )
{
_callback(this);
}

if (kScriptTypeNone != _scriptType)
{
BasicScriptData data(this);
ScriptEvent scriptEvent(kMenuClickedEvent,&data);
ScriptEngineManager::getInstance()->getScriptEngine()->sendEvent(&scriptEvent);
}
}
}注意到其中的  _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。

如下:

auto closeItem = MenuItemImage::create(
"CloseNormal.png",
"CloseSelected.png",
std::bind(&HelloWorld::menuCloseCallback, this,std::placeholders::_1,1,2));

4、function

最后就解决上面的一个疑惑。

std::function func = std::bind(&HelloWorld::menuCloseCallback,this, std::placeholders::_1,1,2);
auto closeItem = MenuItemImage::create(
"CloseNormal.png",
"CloseSelected.png",
func);

5、使用lambda表达式

auto closeItem = MenuItemImage::create(
"CloseNormal.png",
"CloseSelected.png",
[&](Object *sender){
Director::getInstance()->end();
#if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS)
exit(0);
#endif
});可见使用lambda表达式可以极大的简化代码程序,不需要再定义一个回调函数,直接将在回调中的操作在闭包中体现即可。


二、在cocos2d-x中,还有一个地方是需要大量使用到回调函数的,这就是回调动作:CCCallFunc、CCCallFuncN、CCCallFuncND、CCCallFuncO。

但是这四个回调动作在 3.0 版本中已经都提示 deprecate 了。那么在3.0 版本中已经只剩下 CallFunc 和 CallFuncN.

下面是官方文档中的说明:


  • CallFunc can be created with
    an std::function
  • CallFuncN can be created with
    an std::function
  • 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

static CallFunc * create(const std::function& func);

关于CallFunc的例子,在文档中已经有体现:

// in v2.1
CCCallFunc *action1 = CCCallFunc::create( this, callfunc_selector( MyClass::callback_0 ) );
// in v3.0 (short version)
auto action1 = CallFunc::create( CC_CALLBACK_0(MyClass::callback_0,this));
auto action2 = CallFunc::create( CC_CALLBACK_0(MyClass::callback_1,this, additional_parameters));
// in v3.0 (long version)
auto action1 = CallFunc::create( std::bind( &MyClass::callback_0, this));
auto action2 = CallFunc::create( std::bind( &MyClass::callback_1, this, additional_parameters));
// in v3.0 you can also use lambdas or any other "Function" object
auto action1 = CallFunc::create(
[&](){
auto s = Director::sharedDirector()->getWinSize();
auto label = LabelTTF::create("called:lambda callback", "Marker Felt", 16);
label->setPosition(ccp( s.width/4*1,s.height/2-40));
this->addChild(label);
} );


2、CallFuncN

static CallFuncN * create(const std::function& func);

注意到该回调动作带有一个Node*参数。

假设回调函数:

void ActionCallFuncN::callback(Node* sender )


auto action = Sequence::create(
MoveBy::create(2.0f, Point(150,0)),
CallFuncN::create( CC_CALLBACK_1(ActionCallFuncN::callback, this)),
NULL);

auto action = Sequence::create(
MoveBy::create(2.0f, Point(150,0)),
CallFuncN::create(std::bind(&ActionCallFuncN::callback,this,std::placeholders::_1)),
NULL);

auto action = Sequence::create(
MoveBy::create(2.0f, Point(150,0)),
CallFuncN::create([&](Node* sender){
//回调动作代码

}),
NULL);
受益于C&#43;&#43;11的新语法特性 std::bind ; CallFuncND 和 CallFuncO 都可以通过 CallFunc 和 CallFuncN 进行实现

3、CallFuncND :回调动作中带有一个Node*参数和一个void*参数

实现过程类&#20284;于 CallFuncN  

假设回调函数是 :void ActionCallFuncND::doRemoveFromParentAndCleanup(Node* sender,
bool cleanup)

那么在回调动作中:

CallFuncN::create( CC_CALLBACK_1(ActionCallFuncND::doRemoveFromParentAndCleanup,
this, true))

这样就实现了等价于 CallFuncND
的回调动作。


4、CallFuncO :回调动作中带有一个Object*参数

实现过程类&#20284;于 CallFunc 

假设回调函数是: void ActionCallFuncO::callback(Node* node, bool cleanup)

那么在回调动作中:

CallFunc::create( CC_CALLBACK_0(ActionCallFuncO::callback, this, _grossini,
true)

这样就实现了等价于 CallFuncO  的回调动作。


三、总结

在新版的回调处理中,采用了C&#43;&#43;11中的 std::function 、std::bind 、lambda 表达式,使得回调的处理变得形式多样,代码灵活了,而其中的lambda表达式可以极大的简化回调代码,推荐使用。



推荐阅读
  • 拖拉切割直线 ... [详细]
  • 本文探讨了Lua中元表和元方法的使用,通过具体的代码示例展示了如何利用这些特性来实现类似C语言中的运算符重载功能。 ... [详细]
  • Python中调用Java代码的方法与实践
    本文探讨了如何在Python环境中集成并调用Java代码,通过具体的步骤和示例展示了这一过程的技术细节。适合对跨语言编程感兴趣的开发者阅读。 ... [详细]
  • VS Code 中 .vscode 文件夹配置详解
    本文介绍了 VS Code 中 .vscode 文件夹下的配置文件及其作用,包括常用的预定义变量和三个关键配置文件:launch.json、tasks.json 和 c_cpp_properties.json。 ... [详细]
  • MVC框架下使用DataGrid实现时间筛选与枚举填充
    本文介绍如何在ASP.NET MVC项目中利用DataGrid组件增强搜索功能,具体包括使用jQuery UI的DatePicker插件添加时间筛选条件,并通过枚举数据填充下拉列表。 ... [详细]
  • 深入解析Android Activity生命周期
    本文详细探讨了Android中Activity的生命周期,通过实例代码和详细的步骤说明,帮助开发者更好地理解和掌握Activity各个阶段的行为。 ... [详细]
  • SQLite是一种轻量级的关系型数据库管理系统,尽管体积小巧,却能支持高达2TB的数据库容量,每个数据库以单个文件形式存储。本文将详细介绍SQLite在Android开发中的应用,包括其数据存储机制、事务处理方式及数据类型的动态特性。 ... [详细]
  • BeautifulSoup4 是一个功能强大的HTML和XML解析库,它能够帮助开发者轻松地从网页中提取信息。本文将介绍BeautifulSoup4的基本功能、安装方法、与其他解析工具的对比以及简单的使用示例。 ... [详细]
  • 本文详细介绍了JQuery Mobile框架中特有的事件和方法,帮助开发者更好地理解和应用这些特性,提升移动Web开发的效率。 ... [详细]
  • 解析 HTTP 头 'Vary: Accept-Encoding' 的作用与重要性
    本文详细探讨了 'Vary: Accept-Encoding' HTTP 头的作用,即指导缓存系统(如代理服务器和 CDN)根据不同的编码需求存储和提供适当的资源版本,确保不同类型的客户端能够接收到适合自己的内容。 ... [详细]
  • 本文详细介绍了一种实现PopupWindow全屏显示且能有效隐藏虚拟按键的技术方案,适用于Android开发。此方法经过实际测试,表现良好,兼容性优秀。 ... [详细]
  • Python3爬虫入门:pyspider的基本使用[python爬虫入门]
    Python学习网有大量免费的Python入门教程,欢迎大家来学习。本文主要通过爬取去哪儿网的旅游攻略来给大家介绍pyspid ... [详细]
  • 本文探讨了异步编程的发展历程,从最初的AJAX异步回调到现代的Promise、Generator+Co以及Async/Await等技术。文章详细分析了Promise的工作原理及其源码实现,帮助开发者更好地理解和使用这一重要工具。 ... [详细]
  • 在Qt框架中,信号与槽机制是一种独特的组件间通信方式。本文探讨了这一机制相较于传统的C风格回调函数所具有的优势,并分析了其潜在的不足之处。 ... [详细]
  • 本文详细介绍了`android.os.Binder.getCallingPid()`方法的功能和应用场景,并提供了多个实际的代码示例。通过这些示例,开发者可以更好地理解如何在不同的开发场景中使用该方法。 ... [详细]
author-avatar
炜一爱妮
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有