您的位置:首页 > 移动开发 > Cocos引擎

cocos2dx中精灵如何run一个动作

2015-07-30 17:45 525 查看
首先,从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, this, !_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函数。然后就可以播放一个完整的动作了。

先这样吧,不知道写点什么了。还是没有完全闹明白啊
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: