守望者--AIR技术交流

 找回密码
 立即注册

QQ登录

只需一步,快速开始

扫一扫,访问微社区

搜索
热搜: ANE FlasCC 炼金术
查看: 809|回复: 0

[技术资料] cocos2d-x3.3 源码分析之-动作Action和ActionManager

[复制链接]
  • TA的每日心情
    擦汗
    2018-4-10 15:18
  • 签到天数: 447 天

    [LV.9]以坛为家II

    1742

    主题

    2094

    帖子

    13万

    积分

    超级版主

    Rank: 18Rank: 18Rank: 18Rank: 18Rank: 18

    威望
    562
    贡献
    29
    金币
    51782
    钢镚
    1422

    开源英雄守望者

    发表于 2015-4-3 17:35:59 | 显示全部楼层 |阅读模式



    从入口开始分析,我们来看看addaction函数,他接受一个动作,并绑定自己,是否暂停取决于节点的running属性。 我们来具体看看ActionManager这个类

    1. class CC_DLLActionManager : public Ref
    2. {
    3. public:
    4.        ActionManager(void);
    5.         ~ActionManager(void);

    6.       void addAction(Action *action, Node*target, bool paused);

    7.         void removeAllActions();

    8.        void removeAllActionsFromTarget(Node*target);

    9.       void removeAction(Action *action);

    10.    
    11.     void removeActionByTag(int tag, Node*target);
    12.    
    13.    
    14.     void removeAllActionsByTag(int tag, Node*target);

    15.      Action* getActionByTag(int tag, const Node*target) const;
    16.      ssize_tgetNumberOfRunningActionsInTarget(const Node *target) const;


    17.       void pauseTarget(Node *target);

    18.        void resumeTarget(Node *target);
    19.        Vector<Node*>pauseAllRunningActions();
    20.    
    21.         void resumeTargets(constVector<Node*>& targetsToResume);

    22.     void update(float dt);
    23.    
    24. protected:


    25.     void removeActionAtIndex(ssize_t index,struct _hashElement *element);
    26.     void deleteHashElement(struct _hashElement*element);
    27.     void actionAllocWithHashElement(struct_hashElement *element);

    28. protected:
    29.     struct _hashElement    *_targets;
    30.     struct_hashElement    *_currentTarget;
    31.     bool            _currentTargetSalvaged;
    32. };
    复制代码
    struct _hashElement   *_targets;    struct_hashElement    *_currentTarget;如果是对定时器数据结构很熟悉的人没看到这个结构体应该不会陌生。的确,内部结构是相同的,只不过把之前的定时器对象改为现在的action.

    类似的,每个hash节点一般由一个target标志,根据target找到节点。每个节点弱引用一个动作的array,也就是说,每个节点可以绑定多个动作,paused记录了当前动作的暂停状态。  下面来看看addAction函数:
    1. voidActionManager::addAction(Action *action, Node *target, bool paused)
    2. {
    3.     CCASSERT(action != nullptr, "");
    4.     CCASSERT(target != nullptr, "");

    5. //查找hash表,看看是否有相同的target,如果没有新建一个插入表中。
    6.     tHashElement *element = nullptr;
    7.     // we should convert it to Ref*, because wesave it as Ref*
    8.     Ref *tmp = target;
    9.     HASH_FIND_PTR(_targets, &tmp, element);
    10.     if (! element)
    11.     {
    12.         element =(tHashElement*)calloc(sizeof(*element), 1);
    13.         element->paused = paused;
    14.         target->retain();
    15.         element->target = target;
    16.         HASH_ADD_PTR(_targets, target,element);
    17.     }
    18. //为弱引用的数组分配内存。
    19.      actionAllocWithHashElement(element);

    20. //同一个动作结束前,如果被同一个节点包含两次,触发断言
    21.      CCASSERT(!ccArrayContainsObject(element->actions, action), "");
    22. //将新的动作加入数组
    23.      ccArrayAppendObject(element->actions,action);


    24.      action->startWithTarget(target);
    25. }
    26. 现在我们不介绍最后一句,因为我们需要回过头去看看Action及其子类
    27. Action:
    28. class CC_DLL Action: public Ref, public Clonable
    29. {
    30. public:
    31.    //动作的默认tag,一般不可以用默认tag索引动作
    32.     static const int INVALID_TAG = -1;
    33.     /**
    34.      * @js NA
    35.      * @lua NA
    36.      */
    37.     virtual std::string description() const;

    38.     /** returns a clone of action */
    39.     virtual Action* clone() const
    40.     {
    41.         CC_ASSERT(0);
    42.         return nullptr;
    43.     }

    44.     /** returns a new action that performs theexactly the reverse action */
    45.     virtual Action* reverse() const
    46.     {
    47.         CC_ASSERT(0);
    48.         return nullptr;
    49.     }

    50.     //! return true if the action has finished
    51. //判断动作是否结束
    52.     virtual bool isDone() const;

    53.     //! called before the action start. It willalso set the target.
    54. //将_tagget和originTarget都赋值为新的绑定对象
    55.     virtual void startWithTarget(Node *target);

    56.     /**
    57.     called after the action has finished. Itwill set the 'target' to nil.
    58.     IMPORTANT: You should never call"[action stop]" manually. Instead, use:"target->stopAction(action);"
    59.     */
    60. //动作结束后,调用该函数,把_target设置为null
    61.     virtual void stop();

    62.     //! called every frame with it's deltatime. DON'T override unless you know what you are doing.
    63. //类似定时器中的update函数
    64.     virtual void step(float dt);

    65.     /**
    66.     called once per frame. time a value between0 and 1

    67.     For example:
    68.     - 0 means that the action just started
    69.     - 0.5 means that the action is in themiddle
    70.     - 1 means that the action is over
    71.     */
    72. //类似定时器中的trigger函数

    73.     virtual void update(float time);
    74.    
    75.     inline Node* getTarget() const { return_target; }
    76.     /** The action will modify the targetproperties. */
    77.     inline void setTarget(Node *target) {_target = target; }
    78.    
    79.     inline Node* getOriginalTarget() const {return _originalTarget; }
    80.     /** Set the original target, since targetcan be nil.
    81.     Is the target that were used to run theaction. Unless you are doing something complex, like ActionManager, you shouldNOT call this method.
    82.     The target is 'assigned', it is not'retained'.
    83.     @since v0.8.2
    84.     */
    85.     inline void setOriginalTarget(Node*originalTarget) { _originalTarget = originalTarget; }

    86.     inline int getTag() const { return _tag; }
    87.     inline void setTag(int tag) { _tag = tag; }

    88. CC_CONSTRUCTOR_ACCESS:
    89.     Action();
    90.     virtual ~Action();

    91. protected:
    92.     Node   *_originalTarget;
    93.     /** The "target".
    94.     The target will be set with the'startWithTarget' method.
    95.     When the 'stop' method is called, targetwill be set to nil.
    96.     The target is 'assigned', it is not'retained'.
    97.     */
    98.     Node    *_target;
    99.     /** The action tag. An identifier of theaction */
    100.     int    _tag;

    101. private:
    102.     CC_DISALLOW_COPY_AND_ASSIGN(Action);
    103. };
    复制代码
    isDownupdatestep这三个函数是虚函数,action并没有做什么实际的事情,所以我们现在还不知道他干了些什么,我们之后去看看具体的动作类就会清楚了。_originalTarget_target这两者有什么区分现在也不清楚 我们来看看最重要的一个子类FiniteTimeAction:
    1. class CC_DLLFiniteTimeAction : public Action
    2. {
    3. public:
    4.     //! get duration in seconds of the action
    5.     inline float getDuration() const { return_duration; }
    6.     //! set duration in seconds of the action
    7.     inline void setDuration(float duration) {_duration = duration; }

    8.     //
    9.     // Overrides
    10.     //
    11.     virtual FiniteTimeAction* reverse() constoverride
    12.     {
    13.         CC_ASSERT(0);
    14.         return nullptr;
    15.     }
    16.     virtual FiniteTimeAction* clone() constoverride
    17.     {
    18.         CC_ASSERT(0);
    19.         return nullptr;
    20.     }

    21. CC_CONSTRUCTOR_ACCESS:
    22.     FiniteTimeAction()
    23.     : _duration(0)
    24.     {}
    25.     virtual ~FiniteTimeAction(){}

    26. protected:
    27.     //! duration in seconds
    28.     float _duration;

    29. private:
    30.    CC_DISALLOW_COPY_AND_ASSIGN(FiniteTimeAction);
    31. };
    复制代码
    只是增加了一个_duration属性,也就是动作的持续时间,应该是为了他的延时动作子类准备的。  我么接着看ActionInstant这个瞬时动作:
    1. class CC_DLLActionInstant : public FiniteTimeAction //<NSCopying>
    2. {
    3. public:
    4.     //
    5.     // Overrides
    6.     //
    7.     virtual ActionInstant* clone() constoverride
    8.     {
    9.         CC_ASSERT(0);
    10.         return nullptr;
    11.     }
    12.    
    13.     virtual ActionInstant * reverse() constoverride
    14.     {
    15.         CC_ASSERT(0);
    16.         return nullptr;
    17.     }

    18.     virtual bool isDone() const override;
    19.     virtual void step(float dt) override;
    20.     virtual void update(float time) override;
    21. };
    复制代码
    瞬时动作类重写了step函数和isDown函数,其他的isDone和update函数并没有做出变化step:

    调用了update函数,并传入参数1。我们可以看看update函数的注释
    1. called once perframe. time a value between 0 and 1

    2.     For example:
    3.      0 means that the action just started
    4.     0.5 means that the action is in the middle
    5.      1 means that the action is over
    6.     */
    复制代码
    1表示动作结束了 isDown:

    isDown也返回true。我们或许会疑惑,不过接下来看了ActionManager中的update函数之后你就会明白了:该函数在导演类的init函数中被注册到定时器:

    该函数有最高的定时触发优先级,每一帧动作的运行都会触发该函数。
    1. voidActionManager::update(float dt)
    2. {
    3.     for (tHashElement *elt = _targets; elt !=nullptr; )
    4.     {
    5.         _currentTarget = elt;
    6.         _currentTargetSalvaged = false;

    7.         if (! _currentTarget->paused)
    8.         {
    9.             // The 'actions' MutableArray maychange while inside this loop.
    10.             for (_currentTarget->actionIndex= 0; _currentTarget->actionIndex < _currentTarget->actions->num;
    11.                _currentTarget->actionIndex++)
    12.             {
    13.                _currentTarget->currentAction =(Action*)_currentTarget->actions->arr[_currentTarget->actionIndex];
    14.                 if(_currentTarget->currentAction == nullptr)
    15.                 {
    16.                     continue;
    17.                 }

    18.                _currentTarget->currentActionSalvaged = false;

    19.                 _currentTarget->currentAction->step(dt);

    20.                 if(_currentTarget->currentActionSalvaged)
    21.                 {
    22.                     // The currentAction toldthe node to remove it. To prevent the action from
    23.                     // accidentallydeallocating itself before finishing its step, we retained
    24.                     // it. Now that step isdone, it's safe to release it.
    25.                    _currentTarget->currentAction->release();
    26.                 } else
    27.                 if (_currentTarget->currentAction->isDone())
    28.                 {
    29.                     _currentTarget->currentAction->stop();

    30.                     Action *action =_currentTarget->currentAction;
    31.                     // Make currentAction nilto prevent removeAction from salvaging it.
    32.                    _currentTarget->currentAction = nullptr;
    33.                     removeAction(action);
    34.                 }

    35.                _currentTarget->currentAction = nullptr;
    36.             }
    37.         }

    38.         // elt, at this moment, is still valid
    39.         // so it is safe to ask this here(issue #490)
    40.         elt = (tHashElement*)(elt->hh.next);

    41.         // only delete currentTarget if noactions were scheduled during the cycle (issue #481)
    42.         if (_currentTargetSalvaged &&_currentTarget->actions->num == 0)
    43.         {
    44.             deleteHashElement(_currentTarget);
    45.         }
    46.     }

    47.     // issue #635
    48.     _currentTarget = nullptr;
    49. }
    复制代码
    我们可以看到,step就相当于一个触发函数,而动作中的update函数就是触发函数中调用的函数,出发完成后,判断动作是否结束,如果结束调用stop函数,并移除动作。我们现在可以回到之前的瞬时动作,update函数被传值为1isDown返回true,就很好理解了。瞬时动作的基类没有实现update函数,我们以一个具体的瞬时动作来做例子:
    1. class CC_DLL Show : public ActionInstant
    2. {
    3. public:
    4.     /** Allocates and initializes the action */
    5.     static Show * create();


    6.     //
    7.     // Overrides
    8.     //
    9.     virtual void update(float time) override;
    10.     virtual ActionInstant* reverse() constoverride;
    11.     virtual Show* clone() const override;

    12. CC_CONSTRUCTOR_ACCESS:
    13.     Show(){}
    14.     virtual ~Show(){}

    15. private:
    16.     CC_DISALLOW_COPY_AND_ASSIGN(Show);
    17. };
    复制代码
    重写了update函数:
    接着看void ActionManager::update(float dt)中的那个removeAction函数:

    好了一目了然,之前的_originTarget_targrt的区别也就知道了,_target是为了实现节点动作用的,二_target置为空之后,_originTarget便可以用来删除这个动作,动作删除后被release  好了,我们可以接着看延时动作了:
    1. class CC_DLLActionInterval : public FiniteTimeAction
    2. {
    3. public:
    4.     /** how many seconds had elapsed since theactions started to run. */
    5.     inline float getElapsed(void) { return_elapsed; }

    6.     //extension in GridAction
    7.     void setAmplitudeRate(float amp);
    8.     float getAmplitudeRate(void);

    9.     //
    10.     // Overrides
    11.     //
    12.     virtual bool isDone(void) const override;
    13.     virtual voidstep(float dt) override;
    14.     virtual void startWithTarget(Node *target)override;
    15.     virtual ActionInterval* reverse() constoverride
    16.     {
    17.         CC_ASSERT(0);
    18.         return nullptr;
    19.     }

    20.     virtual ActionInterval *clone() constoverride
    21.     {
    22.         CC_ASSERT(0);
    23.         return nullptr;
    24.     }

    25. CC_CONSTRUCTOR_ACCESS:
    26.     /** initializes the action */
    27.     bool initWithDuration(float d);

    28. protected:
    29.     float_elapsed;
    30.     bool  _firstTick;
    31. };
    复制代码
    _elapsed表示延时动作度过的时间isDone:

    很简单,只要度过的时间大于等于动作的生命周期就表示动作结束。step:

    很简单的定时逻辑,ActionManager中的update函数每一次出发后调用step函数,dt一般是每一帧的时间(大多数是1 /60)。每一次调用后,把dt加到_elapsed就可以做到计时了。update的参数被限制在0-1之间,具体的实现得看一个具体的例子:

    这里的initWithDuration就是我们创建动作时候传递的动作生命周期。update:

    很简单吧,每次变化后的属性重新设置给Node就达到了动画的目的。  我们接下来看看移除动作: void removeAction(Action *action):

    移除很简单,找到绑定对象,从对象的动作数组中移除即可。  接下来看看Node中的stopAction函数:
    函数原理都是一样的,拿其中一个举例:

    间接调用了移除动作的函数。 注意事项:不要用action中的stop函数来停止动作,因为判断动作是否结束的标志是isDown函数,并且判断过程还是在动作执行之后。stop函数会将_target置为null,这样运行动作就会试图去修改_target的属性,导致程序奔溃。还有一点就是动作运行结束后会被删除,如果想多次运行动作,请retain。  总结:动作最重要的几个函数isDownstepstopupdate以及ActionManager::update还有动作的重要属性:_target_origintarget_elapsed


    本文作者:zhancaihua

    来源:http://blog.csdn.net/zhancaihua/article/details/44856289


    本帖子中包含更多资源

    您需要 登录 才可以下载或查看,没有帐号?立即注册

    x
    守望者AIR技术交流社区(www.airmyth.com)
    回复

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    
    关闭

    站长推荐上一条 /4 下一条

    QQ|手机版|Archiver|网站地图|小黑屋|守望者 ( 京ICP备14061876号

    GMT+8, 2019-11-13 10:31 , Processed in 0.044825 second(s), 34 queries .

    守望者AIR

    守望者AIR技术交流社区

    本站成立于 2014年12月31日

    快速回复 返回顶部 返回列表