zoukankan      html  css  js  c++  java
  • cocos2d-x动作原理

    首先CCAction是所有动作的基类,如下图继承关系:

    那么来看看CCAction的定义: 

    class CC_DLL CCAction : public CCObject 
    {
    public:
        CCAction(void);
        virtual ~CCAction(void);
    
        const char* description();
        virtual CCObject* copyWithZone(CCZone *pZone);
        //! return true if the action has finished
        virtual bool isDone(void);
        virtual void startWithTarget(CCNode *pTarget);
        
        virtual void stop(void);
        //! called every frame with it's delta time. DON'T override unless you know what you are doing.
        virtual void step(float dt);
        virtual void update(float time);   
        inline CCNode* getTarget(void) { return m_pTarget; }
        inline void setTarget(CCNode *pTarget) { m_pTarget = pTarget; }    
        inline CCNode* getOriginalTarget(void) { return m_pOriginalTarget; } 
        inline void setOriginalTarget(CCNode *pOriginalTarget) { m_pOriginalTarget = pOriginalTarget; }
        inline int getTag(void) { return m_nTag; }
        inline void setTag(int nTag) { m_nTag = nTag; }
    
    public:
        static CCAction* create();
    protected:
        CCNode    *m_pOriginalTarget;
        CCNode    *m_pTarget;
        int     m_nTag;
    };
    

    在类定义最后有三个成员变量,而继承自CCAction的CCFiniteTimeAction主要新增加了一个用于保存该动作总完成时间的成员变量float m_fDuration;

    对于其两个子类CCActionInstant和CCActionInterval,前者没有新增任何函数和变量,而后者增加了两个成员变量:float m_elapsed(记录从动作开始起逝去的时间);和bool   m_bFirstTick(一个控制变量);

    那么动作是如何执行的呢?

    当一个节点调用runAction方法时,动作管理类CCActionManager(单例类)会将新的动作和节点添加到其管理的动作表中。

    CCAction * CCNode::runAction(CCAction* action)
    {
        CCAssert( action != NULL, "Argument must be non-nil");
        m_pActionManager->addAction(action, this, !m_bRunning);
        return action;
    }
    


    在addAction中,将动作添加到动作队列后,就会对该动作调用其成员函数startWithTarget(CCNode* pTarget)来绑定该动作的执行节点,和初始化动作类的成员变量。

    这些工作都完成后,每一帧刷新屏幕时,系统就会在CCActionManager中遍历动作表中的每一个动作,并调用动作的step(float)方法。而step方法主要负责计算m_elapsed的值,并调用update(float)方法。

    void CCActionInterval::step(float dt)
    {
        if (m_bFirstTick)
        {
            m_bFirstTick = false;
            m_elapsed = 0;
        }
        else
        {
            m_elapsed += dt;
        }
        
        this->update(MAX (0,                // needed for rewind. elapsed could be negative
                          MIN(1, m_elapsed / MAX(m_fDuration, FLT_EPSILON)   // division by 0
                              )
                          )
                     );
    }
    

     传入update方法的float型参数表示逝去的时间与动作完成需要的时间的比值,介于0-1之间,即动作完成的百分比。然后在update方法中,通过完成比例对节点的属性进行操作来达到动作的效果。
    例如:对MoveBy调用update时,通过传入的比例调用setPosition直接修改节点的属性。

    最后在每一帧结束后,CCActionManager的update会检查动作队列中每个动作的isDone函数是否返回true,如果返回true,则动作结束,将其从队列中删除。

    —————————————————————————————————————————————————————————————————————————— 

    从上面知道:动作都是由CCActionManager来管理。那我们再来看看CCActionManager的工作原理。

    在CCDirector初始化时,执行了如下代码:

        // scheduler
        m_pScheduler = new CCScheduler();
        // action manager
        m_pActionManager = new CCActionManager();
        m_pScheduler->scheduleUpdateForTarget(m_pActionManager, kCCPrioritySystem, false);
    

    可见动作管理类在创建时就注册了Update定时器,那么CCScheduler在游戏的每一帧mainLoop更新中都会触发CCActionManager注册的update(float )方法。调度器原理请参照此链接:http://www.cnblogs.com/songcf/p/3162414.html

    // main loop
    void CCActionManager::update(float dt)
    {
        //枚举动作表中的每一个节点
        for (tHashElement *elt = m_pTargets; elt != NULL; )
        {
            m_pCurrentTarget = elt;
            m_bCurrentTargetSalvaged = false;
    
            if (! m_pCurrentTarget->paused)
            {
                //枚举节点的每一个动作     actions数组可能会在循环中被修改
                for (m_pCurrentTarget->actionIndex = 0; m_pCurrentTarget->actionIndex < m_pCurrentTarget->actions->num;
                    m_pCurrentTarget->actionIndex++)
                {
                    m_pCurrentTarget->currentAction = (CCAction*)m_pCurrentTarget->actions->arr[m_pCurrentTarget->actionIndex];
                    if (m_pCurrentTarget->currentAction == NULL)
                    {
                        continue;
                    }
    
                    m_pCurrentTarget->currentActionSalvaged = false;
    
                    m_pCurrentTarget->currentAction->step(dt);
    
                    if (m_pCurrentTarget->currentActionSalvaged)
                    {
                        // The currentAction told the node to remove it. To prevent the action from
                        // accidentally deallocating itself before finishing its step, we retained
                        // it. Now that step is done, it's safe to release it.
                        m_pCurrentTarget->currentAction->release();
                    } else
                    if (m_pCurrentTarget->currentAction->isDone())
                    {
                        m_pCurrentTarget->currentAction->stop();
    
                        CCAction *pAction = m_pCurrentTarget->currentAction;
                        // Make currentAction nil to prevent removeAction from salvaging it.
                        m_pCurrentTarget->currentAction = NULL;
                        removeAction(pAction);
                    }
    
                    m_pCurrentTarget->currentAction = NULL;
                }
            }
    
            // elt, at this moment, is still valid
            // so it is safe to ask this here (issue #490)
            elt = (tHashElement*)(elt->hh.next);
    
            // only delete currentTarget if no actions were scheduled during the cycle (issue #481)
            if (m_bCurrentTargetSalvaged && m_pCurrentTarget->actions->num == 0)
            {
                deleteHashElement(m_pCurrentTarget);
            }
        }
    
        // issue #635
        m_pCurrentTarget = NULL;
    }
  • 相关阅读:
    NSPrediccate 查询
    集合 不可变集合
    集合 不可变
    考核题 7
    考核题 6
    考核题 4
    练习题12
    练习题3
    iOS 实现在string任意位置添加新的表情
    在 ZBarSDK 中使用Block回调传值 Block在扫描成功后 变为空
  • 原文地址:https://www.cnblogs.com/MiniHouse/p/3960066.html
Copyright © 2011-2022 走看看