首页 > 代码库 > Cocos2d-x学习笔记(六)CCAction分析

Cocos2d-x学习笔记(六)CCAction分析

原创文章,转载请注明出处:http://blog.csdn.net/sfh366958228/article/details/38821319


前言


千呼万唤始出来,不知你与我的心情是否一样,终于是等到了CCAction的出场。如果说CCSprite是身体,那么CCAction一定就是灵魂,它的组合,让整个游戏充满活力,当然,充满活力的方法也并非仅此而已。


源码分析

class CC_DLL CCAction : public CCObject 
{
public:
    CCAction(void);
    virtual ~CCAction(void);
    const char* description();

    //
    virtual CCObject* copyWithZone(CCZone *pZone);

    // 动作是否完成
    virtual bool isDone(void);

    // 在动作开始之前被调用,它将设置一个target
    virtual void startWithTarget(CCNode *pTarget);

    // 在动作完成后调用,它将把target设置为空。注意,不要调用这个方法,取而代之的十target->stopAction(action)
    virtual void stop(void);

    // 调用每一帧。注意:不要重构它,除非你确保自己在做什么
    virtual void step(float dt);

    /** 
     *每帧会调用一次,time是一个0~1之间的值
     *
     * For example: 
     * - 0 意味着动作刚开始
     * - 0.5 意味着动作处于中间
     * - 1 意味着动作结束了
     **/
    virtual void update(float time);

    // 获取/设置动作的target
    inline CCNode* getTarget(void) { return m_pTarget; }
    inline void setTarget(CCNode *pTarget) { m_pTarget = pTarget; }
    
    // 获取/设置动作最初的target(一般不调用set)
    inline CCNode* getOriginalTarget(void) { return m_pOriginalTarget; }
    inline void setOriginalTarget(CCNode *pOriginalTarget) { m_pOriginalTarget = pOriginalTarget; }

    // 获取/设置Tag
    inline int getTag(void) { return m_nTag; }
    inline void setTag(int nTag) { m_nTag = nTag; }

public:
    // 创建一个Action
    static CCAction* create();
protected:
    CCNode    *m_pOriginalTarget;
    CCNode    *m_pTarget;
    int     m_nTag;
};

CCNode子类层级分析

CCAction是整个动作体系的基类,在CCNode上使用runAction即可执行,不仅可以完成一个独立的动作,也可以定义动作序列、组合动作、反向动作等。

这张图清晰的说明了整个CCAction的层级体系。主要分为三大类:CCFiniteTimeAction(有限次动作执行类)、CCSpeed(节点执行速度类)、CCFollow(节点跟随另一个节点移动)。
而CCFiniteTimeAction分为CCActionInstant(瞬时动作)和CCActionInterval(延迟动作)。我们看到的动作一般都是后者。
下面介绍一些常用的延时动作:
1)CCMoveTo / CCMoveBy 移动
2)CCRotateTo / CCRotateBy 旋转
3)CCSkewTo / CCSkewBy 扭曲
4)CCJumpTo / CCJumpBy 跳跃
5)CCBezierTo / CCBezierBy 贝塞尔曲线
6)CCBink 闪烁
7)CCScaleTo / CCScaleBy 缩放
8)CCFadeIn / CCFadeOut 淡入淡出
9)CCTintTo  / CCTintBy 染色
所有动作都是通过create方法来完成创建。例如:
CCMoveTo *move = CCMoveTo::create(2.0f, cpp(50, 50));
细心的话会发现很多方法都会有To和By两种,他们的区别是To是指定坐标,By是相对坐标。

CCActionManager

之前分析CCNode的时候有提到过CCActionManager,它是一个管理所有动作的单例,工作原理十:当CCNode执行runAction时,该函数会把动作通过动作管理类的addAction函数将对象传递给CCActionManager的单例该实例再把动作添加到自己的动作序列中。
一般情况下十不需要使用这个类的,我们完全可以使用CCNode类中的stopAction、StopActionByTag和stopAllAction等函数来管理,但是有两种特殊情况需要使用CCActionManager类单例:
1)动作的执行者不是同一个节点;
2)需要暂停、重启活动时。
class CC_DLL CCActionManager : public CCObject
{
public:
    CCActionManager(void);
    ~CCActionManager(void);

    // 为指定目标添加动作
    void addAction(CCAction *pAction, CCNode *pTarget, bool paused);

    // 删除所有动作
    void removeAllActions(void);

    // 删除制定目标所有动作
    void removeAllActionsFromTarget(CCObject *pTarget);

    // 删除指定动作
    void removeAction(CCAction *pAction);

    //根据标签删除/获取指定动作
    void removeActionByTag(unsigned int tag, CCObject *pTarget);
    CCAction* getActionByTag(unsigned int tag, CCObject *pTarget);

    // 获取目标的动作数
    unsigned int numberOfRunningActionsInTarget(CCObject *pTarget);

    // 暂停目标动作
    void pauseTarget(CCObject *pTarget);

    // 恢复目标动作
    void resumeTarget(CCObject *pTarget);
    
    // 暂停所有运行中的动作
    CCSet* pauseAllRunningActions();
    
    // 恢复指定集合所有动作
    void resumeTargets(CCSet *targetsToResume);

    ...
};

// 以上是CCActionManager源码分析
// 使用示例如下

pNode->runAction(CCScaleBy::create(2, 2));
CCDirector * pDirector = CCDirector::shareDirector();
pDirector->getActionManager()->pauseTarget(pNode);
pDirector->getActionManager()->resumeTarget(pNode);

注意:不要轻易使用动作管理类,除非是不同动作目标或者暂停重启动作。


CCRepeat和CCRepeatForever

CCRepeat和CCRepeatForever都是继承自CCActionInterval,可以实现动作的多次执行和重复执行。
// times是执行pAction的次数,而CCRepeat则是重复执行直到stop
static CCRepeat * create(CCFiniteTimeAction *pAction, unsigned int times);
static CCRepeatForever *create(CCActionInterval *pAction);

CCSequence和CCSpawn

CCSequence类可以实现按序列执行动作,让节点顺序执行这几个动作。
CCSpawn类实现同时执行几个动作,最终动作的持续时间由时间最长的动作决定。
例如:
CCSequence::create(move, move_back, NULL);// 记得在动作序列的结尾加上NULL

CCReverseTime

CCReverseTime就是反向执行某个动作,支持针对动作序列的反动作序列。不是所有的类都支持反动作,XxxTo通常不支持,XxxBy通常支持。

我们一般在做某个一个Action的时候,我们需要返回会用到该sprite的reverse,但是又时候,我需要这种行为再reverse的时候,我就可以利用CCReverseTime达到这种效果,例子代码如下:

CCSprite *spriteTint = CCSprite::create("blocks.png");
spriteTint->setPosition(ccp(size.width / 2.0f, size.height / 2.0f));
this->addChild(spriteTint, 1);

CCActionInterval *forwardBy = CCTintBy::create(4, 255, 0, 0);
CCActionInterval *back = forwardBy->reverse();
CCReverseTime *reverseTime = CCReverseTime::create(back); //在这里也就是倒序播放它的Action了
CCAction *action = CCSequence::create(forwardBy, back, NULL);
spriteTint->runAction(action);

CCSpeed和CCEaseAction

基本动作和组合动作实现了针对CCNode的各种属性改变,但这样改变的速度是不变的,通过CCEaseAction和CCSpeed类可以很方便的修改CCNode执行的速度。

CCSpeed也是一个包装器,可以改变内部动作执行时间。

CCSpeed *speed = CCSpeed::create(CCMoveBy::create(3, cpp(350, 0)), 2.0f);
CCEaseAction也是包装器,但是它一般用来让动作执行起来更加自然:

1)EaseIn // 由慢至快

2)EaseOut // 由快至慢
3)EaseInOut // 由慢至快,再由快至慢

4)EaseSineIn // 由慢至快
5)EaseSineOut // 由快至慢
6)EaseSineInOut // 由慢至快,再由快至慢

7)EaseExponentialIn // 由慢至极快
8)EaseExponentialOut // 由极快至慢
9)EaseExponentialInOut // 由慢至极快,再由极快至慢

具体的差异可以通过调试查看。


CCFollow

CCFollow可以让一个节点跟随另一个节点作位移。
static CCFollow *create(CCNode *pFollowNode, const CCRect &rect = CCRectZero);
通过该方法来创建一个CCFollow跟随动作,可以设置一个跟随范围,离开范围就不再跟随。
CCFollow经常用来设置Layer跟随sprite,可以实现类似摄像机的跟拍效果,TestCpp中也有类似的例子。
void ActionFollow::onEnter()
{
    ActionsDemo::onEnter();

    centerSprites(1);
    CCSize s = CCDirector::sharedDirector()->getWinSize();

    m_grossini->setPosition(ccp(-200, s.height / 2));
    CCActionInterval* move      = CCMoveBy::create(2, ccp(s.width * 3, 0));
    CCActionInterval* move_back = move->reverse();
    CCSequence* seq       = CCSequence::create(move, move_back, NULL);
    CCAction* rep               = CCRepeatForever::create(seq);

    m_grossini->runAction(rep);

    this->runAction(CCFollow::create(m_grossini, CCRectMake(0, 0, s.width * 2 - 100, s.height)));
}

组合技

无论是CCRepeat、CCRepeatForever、CCSequence、CCSpawen、CCSpeed、CCEaseAction,都是包容器,包含一个或多个动作,组成一个特定的动作,如下,我们将模拟一个心脏跳动的动画。
CCSprite* pSprite = CCSprite::create("Heart.png");
this->addChild(pSprite, 0);
auto *scale = CCScaleBy::create(1.0, 1.5);
auto *scaleBack = scale->reverse();
auto *scaleForever = CCRepeatForever::create(CCSequence::create(scale, scaleBack, NULL));
pSprite->runAction(scaleForever);


Cocos2d-x学习笔记(六)CCAction分析