Cocos2dx Action动画机制
1 动画执行过程cocos中的动画通过节点执行,如: void ActionManager::addAction(Action *action,Node *target,bool paused)
{
if(action == nullptr || target == nullptr)
return;
tHashElement *element = nullptr;
Ref *tmp = target;
// _targets: target节点作为key,element作为value
HASH_FIND_PTR(_targets,&tmp,element);
if (! element)
{
element = (tHashElement*)calloc(sizeof(*element),1);
element->paused = paused;
// 当element销毁时 target将执行release
target->retain();
element->target = target;
// 加入至当前hash表
HASH_ADD_PTR(_targets,target,element);
}
actionAllocWithHashElement(element);
// 将action添加至element中
ccArrayAppendObject(element->actions,action);
// 动画关联至节点
action->startWithTarget(target);
}
addAction方法负责加入动画,update方法负责动画执行的核心逻辑。该方法完成了几个十分重要的工作:动画的更新操作step()、动画的移除与已完成动画的析构。 // main loop
void ActionManager::update(float dt)
{
for (tHashElement *elt = _targets; elt != nullptr; )
{
_currentTarget = elt;
_currentTargetSalvaged = false;//标志是否release该动画
if (! _currentTarget->paused)
{
for (_currentTarget->actionIndex = 0; _currentTarget->actionIndex < _currentTarget->actions->num;
_currentTarget->actionIndex++)
{
_currentTarget->currentAction = static_cast<Action*>(_currentTarget->actions->arr[_currentTarget->actionIndex]);
if (_currentTarget->currentAction == nullptr)
{
continue;
}
_currentTarget->currentActionSalvaged = false;
_currentTarget->currentAction->step(dt);
if (_currentTarget->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.
_currentTarget->currentAction->release();
}
else if (_currentTarget->currentAction->isDone())
{
_currentTarget->currentAction->stop();
Action *action = _currentTarget->currentAction;
// Make currentAction nil to prevent removeAction from salvaging it.
_currentTarget->currentAction = nullptr;
removeAction(action);
}
_currentTarget->currentAction = nullptr;
}
}
// 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 (_currentTargetSalvaged && _currentTarget->actions->num == 0)
{
deleteHashElement(_currentTarget);
}
//if some node reference 'target',it's reference count >= 2 (issues #14050)
else if (_currentTarget->target->getReferenceCount() == 1)
{
deleteHashElement(_currentTarget);
}
}
// issue #635
_currentTarget = nullptr;
}
2 动画中的update机制2.1 Sequence
2.2 RepeatRepeat的update的执行分两中情形:1)总进度在下一次动作之前;2)总进度在下一次动作之后。对于第一种情形,计算当前正在执行动画的进度,计算公式为 // issue #80. Instead of hooking step:,hook update:
// since it can be called by any container action like Repeat,Sequence,Ease,etc..
void Repeat::update(float dt)
{
if (dt >= _nextDt) // _nextDt:进入下一次重复动作需要完成的进度
{
while (dt >= _nextDt && _total < _times) // 进入下一个重复动作阶段 但还未完成所有动作 此处用while 可将所有instant action全部执行完成
{
if (!(sendUpdateEventToScript(1.0f,_innerAction)))
_innerAction->update(1.0f); // 完成前一个动作
_total++;
_innerAction->stop();
_innerAction->startWithTarget(_target); // 开始下一个动作
_nextDt = _innerAction->getDuration()/_duration * (_total+1); // 更新下一个动作进度
}
// fix for issue #1288,incorrect end value of repeat
if (dt >= 1.0f && _total < _times) // 总进度已经完成 最后一次动作还未结束 直接结束
{
if (!(sendUpdateEventToScript(1.0f,_innerAction)))
_innerAction->update(1.0f); // 直接结束最后一次动作
_total++;
}
// don't set an instant action back or update it,it has no use because it has no duration
if (!_actionInstant)
{
if (_total == _times)
{
_innerAction->stop(); // 终止最后一次动作
}
else
{
// issue #390 prevent jerk,use right update
// 理论上每一个action的update操作都在下面的else中完成 但完全在else中进行动作更新可能会使得动作在某个时刻发生剧烈抖动 因此无论是dt >= _nextDt还是dt < _nextDt,都对动画进行更新。
if (!(sendUpdateEventToScript(dt - (_nextDt - _innerAction->getDuration()/_duration),_innerAction)))
_innerAction->update(dt - (_nextDt - _innerAction->getDuration()/_duration));
}
}
}
else
{
if (!(sendUpdateEventToScript(fmodf(dt * _times,1.0f),_innerAction)))
_innerAction->update(fmodf(dt * _times,1.0f)); // fmodf(dt * _times,1.0f):将总进度dt转换为局部进度(当前的action执行进度) _times可看作两个区间的缩放比
}
}
2.3 RepeatForeverRepeat更新的方式是 void RepeatForever::step(float dt)
{
_innerAction->step(dt);
if (_innerAction->isDone()) // 检测动作是否已完成
{
float diff = _innerAction->getElapsed() - _innerAction->getDuration(); // 当前Action时间超出部分
if (diff > _innerAction->getDuration())
diff = fmodf(diff,_innerAction->getDuration()); // 超出时间大于一个周期 取模校正
_innerAction->startWithTarget(_target);
// to prevent jerk. issue #390,1247
_innerAction->step(0.0f);
_innerAction->step(diff);
}
}
3 缓动动画(编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |