30 December 2015
版权声明:本文基于署名 2.5 中国大陆许可协议发布,欢迎转载,演绎或用于商业目的,但是必须保留本文的署名elloop(包含链接)

#前言

前段时间写STL系列的文章的感受是,技术这东西你说深就深,说浅也浅。拿一个STL算法来说,对它的描述可以简单到给出几个示例用法,把代码往上一贴,OK搞定。或者也可以写的深入一些,分析一下它的源代码实现,分析一下它的效率、边界条件检测、线程安全性等等方面,再加上组织语言,一两个小时都是不够的。虽说现在我年纪轻轻,但是空闲时间并不多,而技术的东西实在太多,所以要做到详略得当,把握住重点,不能所有的东西都写的面面俱到。对于有价值的技术点,因为时间有限,我可以先写一下它的基础含义,假设它的深度有七层,我先写到一、二层,以后有时间或者碰巧对它有新的领悟的时候,再来继续在这一点上写,重复这个过程直到完成七层。今天开始《C++ STL学习与应用总结》系列的文章更新频率会开始降低,近期开始重点写cocos的东西了。

今天在YouTube上看到一个非常好的VIM视频,这几天打算录一个翻版分享出来,提醒自己别忘了。

今晚肚子疼头也疼,先挖个坑,明天来填。这篇文章将写一下cocos中的CallFunc系列“动作”用法和注意事项。

现在是2015年最后一天的最后一个小时了,时间不等人,点滴积累要坚持下去。现在开始填昨天的坑。


CallFunc系列对象是Action的一种,具体来说它是瞬时动作(ActionInstant)的一种,其它的瞬时动作还包括Show, Hide, ToggleVisibility, RemoveSelf, FlipX, FlipY, Place等,这么多的瞬时动作,为什么把CallFunc提取出来呢,它有什么好说的呢?

首先是从使用频率上来看的,CallFunc通常和DelayTime一起组成一个Sequence来实现一种延迟调用的效果,这种动作序列在实际开发中很常用。

其次,是从CallFunc的构造方式上来看,它的create方法接受的参数类型比较有代表性。这个参数是一个std::function类型的对象,std::function可以使用C++中任何的Callable来构造,具体包括函数(函数指针,函数引用)、函数对象、闭包(lambda表达式)等一些可调用的对象,这要归功于C++11带来的便利。3.x版本对C++11新特性和标准库的使用使得它比2.x版本在创建回调(包括CallFunc系列, Menu Callback系列, Scheduler系列)方面灵活了很多。这篇文章说CallFunc,也是因为CallFunc的创建方式能很好体现3.x新的创建回调的灵活方式。

前面写的几篇cocos的文章代码示例代码是自己写的,我觉得有些浪费时间,不如直接拿cocos官方示例代码来说,剩下的时间还是自己写游戏更好,在实战中对技术点的体会也会更深刻。

为了不在编写示例代码上花费更多的时间和精力,今后的总结里我会尽可能的使用cocos官方的示例代码。测试代码所在文件路径是相对于cocos2d-x-3.x/tests/cpp-tests/Classes这个目录的。

CallFunc的基本用法

对CallFunc的使用主要是掌握怎么调用create()方法来创建它,create()方法的原型如下:

// create参数是一个std::function<void()>类型对象的常引用
static CallFunc * create(const std::function<void()>& func);

创建std::function<void()>类型对象的方法太多了,详情请参考这里.

下面的示例是cocos 3.9版本官方的示例代码:

**ActionsTest/ActionTest.cpp : ActionCallFunction **

void ActionCallFunction::onEnter()
{
    ActionsDemo::onEnter();

    // 居中放置三个测试用的精灵
    centerSprites(3);


    // 动作序列1: 移动2秒 + 两个CallFunc, 
    // 第一个CallFunc使用bind来构造一个std::function<void()>类型的对象.
    // 第二个CallFunc使用lambda来构造一个std::function<void()>类型的对象
    // 两个CallFunc的回调函数都是一个用一个不带参数的函数来构造的,
    // 第一个bind,把一个不带参数的成员函数ActionCallFunction::callback1, 适配成std::function<void()>.
    // 第二个,lambda的定义则直接是一个不带参数的函数, 以引用的方式捕获了外层作用域里的this。
	auto action1 = Sequence::create(
                        MoveBy::create(2, Vec2(200,0)),
                        CallFunc::create( std::bind(&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);

    // 动作序列2: 缩放两秒 + 淡出两秒 + 回调
    // 这里的回调是用bind来构造的,bind把一个接收Node*类型形参的成员函数ActionCallFunction::callback2, 
    // 适配成一个CallFunc需要的std::function<void()>类型的对象。
    // bind把_tamara绑定到形参Node*上面。
    auto action2 = Sequence::create(
                        ScaleBy::create(2 ,  2),
                        FadeOut::create(2),
                        CallFunc::create( std::bind(&ActionCallFunction::callback2, this, _tamara) ),
                        nullptr);

    // 动作序列3: 缩放3秒 + 淡出2秒 + 回调
    // 回调同样是使用bind来创建,这次是把一个接收两个参数:Node*和int的成员函数ActionCallFunction::callback3, 
    // 适配成一个std::function<void()>类型的对象
    auto action3 = Sequence::create(
                        RotateBy::create(3 , 360),
                        FadeOut::create(2),
                        CallFunc::create( std::bind(&ActionCallFunction::callback3, this, _kathia, 42) ),
                        nullptr);

    _grossini->runAction(action1);
    _tamara->runAction(action2);
    _kathia->runAction(action3);
}

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);
}

上面的例子中所有的使用bind来创建回调的地方都可以用这个cocos的一个宏来代替,它就是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);

运行效果如下图所示:

call_func

关于CC_CALLBACK_0的使用请参考【cocos2d-x 3.x 学习与应用总结】4: 理解CC_CALLBACK_0, CC_CALLBACK_1, CC_CALLBACK_2, CC_CALLBACK_3.

CallFuncN的基本用法

CallFuncN::create的原型:

static CallFuncN * create(const std::function<void(Node*)>& func);

CallFuncN与CallFunc的区别仅仅在于前者的function回调需要一个Node*类型的参数。

void ActionCallFuncN::onEnter()
{
    ActionsDemo::onEnter();

    centerSprites(1);

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

    _grossini->runAction(action);

    // 其实,使用CallFunc也能达到这个效果:

    auto actionUseCallfunc = Sequence::create(
        MoveBy::create(2.0, Vec2(150, 0)),
        CallFunc::create(
            CC_CALLBACK_0(ActionCallFuncN::callback, this, _grossini)),
        nullptr);
    _grossini->runAction(actionUseCallfunc);
}

void ActionCallFuncN::callback(Node* sender )
{
    auto a = JumpBy::create(5, Vec2(0,0), 100, 5);
    sender->runAction(a);
}

CallFuncND的基本用法

// CallFuncND不再需要了,可以使用std::bind来模拟。
// CallFuncND is no longer needed. It can simulated with std::bind()

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);

    _grossini->runAction(action);
}

void ActionCallFuncND::doRemoveFromParentAndCleanup(Node* sender, bool cleanup)
{
    _grossini->removeFromParentAndCleanup(cleanup);
}

作者水平有限,对相关知识的理解和总结难免有错误,还望给予指正,非常感谢!

在这里也能看到这篇文章:github博客, CSDN博客, 欢迎访问



分享到