cocos2dx中精灵如何run一个动作

前端之家收集整理的这篇文章主要介绍了cocos2dx中精灵如何run一个动作前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。


首先,从Action * Node::runAction(Action* action)看它的实现:

Action * Node::runAction(Action* action)
{
   CCASSERT( action != nullptr,"Argument must be non-nil");
    _actionManager->addAction(action,this,!_running);
    return action;
}


然后看到了_actionManager,这个东西是ActionManager,看看他是从哪来的:

//初始化node类的ActionManager,从_director获取过来,得到一个全局的ActionManager
_actionManager = _director->getActionManager(); 


跟到Director中,真相大白:
_actionManager = new (std::nothrow) ActionManager(); 
   //初始化一个ActionManager,初始化成功立即开启scheduler,保证每一帧action可以每一帧更新
    
_scheduler->scheduleUpdate(_actionManager,Scheduler::PRIORITY_SYSTEM,false);</span></span>



然后接着看_actionManager->addAction(action,!_running);,我们是不是要到ActionManager的addAction中看看呢

void ActionManager::addAction(Action *action,Node *target,bool paused)
{
    CCASSERT(action != nullptr,"");
    CCASSERT(target != nullptr,"");

    tHashElement *element = nullptr;
    // we should convert it to Ref*,because we save it as Ref*
    Ref *tmp = target;
    HASH_FIND_PTR(_targets,&tmp,element);//通过target找到对应的哈希表项返回给element
    if (! element)
    {   //如果找不到。则node对应的哈希表还不存在 则申请内存创建此哈希表项,并将其加入哈希表中。

        //创建一个新的哈希表项
        element = (tHashElement*)calloc(sizeof(*element),1);
        element->paused = paused;   //设置暂停状态
        target->retain();           //引用计数加1代表被管理器使用中
        element->target = target;   //设置哈希表项中的演员为参数指定的演员 
        HASH_ADD_PTR(_targets,target,element);//将哈希表项添加到哈希表  
    }
    //为哈希表申请内存 用来存放action 
     actionAllocWithHashElement(element);
    //动作已经在动作列表 中断 //判断pAction是否在pElement的动画集中。确保只放入一次
     CCASSERT(! ccArrayContainsObject(element->actions,action),"");
     ccArrayAppendObject(element->actions,action);//添加到动作列表  
 
     action->startWithTarget(target);//动作绑定目标  //设置是哪个CCNode要进行当前动画 
}



这里先插一下:ActionManager有一个HASH表来存储动作CCAction,HASH表的键还是CCObject类型的指针。CCActionManager将这些动作放在一个数组当中(tHashElement->actions)。ActionManager::addAction先根据穿进去的target作为键值查看HASH表中是否存在相应的Bucket。如果没有找到会创建一个,然后,将现在的CCAction添加到数组的末尾。并把对应的Node传给action的startWithTarget,作为对应action的target。此时,我们已经把动作添加到ActionManager的动作列表中了。

上面我们说过的,ActionManager在Director的int中会初始化,并且开启一个update调度器。然后在每帧都会调用到ActionManager的update函数。那么现在让我们来看看ActionManager的update函数的实现:

void ActionManager::update(float dt)
{   //遍历哈希表中所有项
    for (tHashElement *elt = _targets; elt != nullptr; )
    {   //将当前哈希表项保存到变量_currentTarget中。并将此项对应的回收标记m_currentTargetSalvaged 设为false,
        _currentTarget = elt;
        _currentTargetSalvaged = false;
        if (! _currentTarget->paused)//当前动作没有暂停
        {
            //遍历当前动画集中所有的actions
            for (_currentTarget->actionIndex = 0; _currentTarget->actionIndex < _currentTarget->actions->num;
                _currentTarget->actionIndex++)
            {   //取得遍历项的动画,即当前action
                _currentTarget->currentAction = (Action*)_currentTarget->actions->arr[_currentTarget->actionIndex];
                if (_currentTarget->currentAction == nullptr)
                {
                    continue;
                }
                //设置回收标志位false
                _currentTarget->currentActionSalvaged = false;
                //更新当前播放的动画
                _currentTarget->currentAction->step(dt);

                if (_currentTarget->currentActionSalvaged)
                {   //如果当前的回收标志位true,进行释放处理
                    _currentTarget->currentAction->release();
                } else
                if (_currentTarget->currentAction->isDone())
                {   //如果当前动画处于结束状态,则停止动画
                    _currentTarget->currentAction->stop();
                    //为了在removeAction中正确释放动画,这里先创建一个临时变量pAction记录一下要释放的动画。
                    Action *action = _currentTarget->currentAction;
                    _currentTarget->currentAction = nullptr;
                    removeAction(action);
                }

                _currentTarget->currentAction = nullptr;
            }
        }
        elt = (tHashElement*)(elt->hh.next);//使for循环能够继续 

        // 如果当前哈希表项处于回收状态且其动画集为空,删除此哈希表项。
        if (_currentTargetSalvaged && _currentTarget->actions->num == 0)
        {
            deleteHashElement(_currentTarget);
        }
    }
    _currentTarget = nullptr;
}



ActionManager::update的外层循环中HASH表中所有的Bucket,内层循环遍历当前动画集中所有的actions。执行动作是通过调用action中的step函数(多态调用的),在不同的action中有不同的实现。currentActionSalvaged用来标记动作是否被要求删除。CCAction的stop虚函数是在清理动作之前执行的回调函数(这个不能手动调用,动作执行完成后会自动调用)。如果动作执行完step之后,已经完成了,那么动作就会从HASH表中Bucket的数组上删除。最后,如果某个CCSprite上已经没有附加任何动作,就删除HASH表中这个Bucket。

因为在ActionManager::update中的_currentTarget->currentAction->step(dt)会调用action的step函数,有的action(比如moveBy)没有step方法,会到父类调用父类的step,看一下他的实现:

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


ActionInterval::step会调用update 方法,因为是子类对象调用的,所以this指针指向子类的对象,所以this->update会调用到子类的update函数。然后就可以播放一个完整的动作了。


先这样吧,不知道写点什么了。还是没有完全闹明白啊

原文链接:https://www.f2er.com/cocos2dx/341966.html

猜你在找的Cocos2d-x相关文章