-
Notifications
You must be signed in to change notification settings - Fork 43
cocos2d x 3.3 013 动作Action原理
cheyiliu edited this page Dec 29, 2014
·
6 revisions
- Action及子类, 提供如何改变Node属性的算法
- Node,被Action改变的对象
- ActionManager,Action的驱动器
// Move sprite 20 points to right in 2 seconds
auto moveBy = MoveBy::create(2, Vec2(20,0));
mySprite2->runAction(moveTo);
- MoveBy::create(2, Vec2(20,0));
//两段构造
MoveBy* MoveBy::create(float duration, const Vec2& deltaPosition)
{
MoveBy *ret = new (std::nothrow) MoveBy();
ret->initWithDuration(duration, deltaPosition);
ret->autorelease();
return ret;
}
bool MoveBy::initWithDuration(float duration, const Vec2& deltaPosition)
{
//调用父类函数将duration设置
if (ActionInterval::initWithDuration(duration))
{
//设置deltaPosition
_positionDelta = deltaPosition;
return true;
}
return false;
}
- mySprite2->runAction(moveTo);
Action * Node::runAction(Action* action)
{
CCASSERT( action != nullptr, "Argument must be non-nil");
_actionManager->addAction(action, this, !_running);
return action;
}
- 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表中查找target
//(这里的hash结构由uthash实现,在schedule 中也用过 https://github.com/cheyiliu/All-in-One/wiki/cocos2d-x-3.3-011-%E8%B0%83%E5%BA%A6%E5%99%A8 )
HASH_FIND_PTR(_targets, &tmp, element);
if (! element)//没找到
{
element = (tHashElement*)calloc(sizeof(*element), 1);
element->paused = paused;
target->retain();
element->target = target;
HASH_ADD_PTR(_targets, target, element);
}
//确保element的actions数组有容量存储
actionAllocWithHashElement(element);
CCASSERT(! ccArrayContainsObject(element->actions, action), "");
//将action放入element->actions
ccArrayAppendObject(element->actions, action);
//这一步很关键,建立Node和action的关系
action->startWithTarget(target);
}
-
小结下前面的代码: 1.构造出了action; 2.将action交个action manager管理起来了; 3. action manager将action和Node建立了关联。 前面的runAction并没有真的'run', 那谁让action真的run起来了?答案在ActionManager
-
ActionManager的创建
1. ActionManager的创建
bool Director::init(void)
{
//其他略
// action manager
_actionManager = new (std::nothrow) ActionManager();
_scheduler->scheduleUpdate(_actionManager, Scheduler::PRIORITY_SYSTEM, false);
}
2. scheduleUpdate, 用到了Scheduler的知识, https://github.com/cheyiliu/All-in-One/wiki/cocos2d-x-3.3-011-%E8%B0%83%E5%BA%A6%E5%99%A8
template <class T>
void scheduleUpdate(T *target, int priority, bool paused)
{
this->schedulePerFrame([target](float dt){
target->update(dt);
}, target, priority, paused);
}
//这里的流程:导演在创建的时候, 创建了ActionManager,并注册到Scheduler中。 这使得之后每一帧里ActionManager的update方法都会被Scheduler的update方法调用到(```mainLoop->drawScene->scheduler::update->callback到ActionManager的update```)。
- ActionManager的update如何驱动action 'run'起来
void ActionManager::update(float dt)
{
//一重循环,遍历Node
for (tHashElement *elt = _targets; elt != nullptr; )
{
_currentTarget = elt;
_currentTargetSalvaged = false;
//未被暂停
if (! _currentTarget->paused)
{
//二重循环,遍历Node对应的action
// The 'actions' MutableArray may change while inside this loop.
for (_currentTarget->actionIndex = 0; _currentTarget->actionIndex < _currentTarget->actions->num;
_currentTarget->actionIndex++)
{
_currentTarget->currentAction = (Action*)_currentTarget->actions->arr[_currentTarget->actionIndex];
if (_currentTarget->currentAction == nullptr)
{
continue;
}
_currentTarget->currentActionSalvaged = false;
//!!!
//关键点, action::step, 将action驱动起来
_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);
}
}
// issue #635
_currentTarget = nullptr;
}
- 最后看MoveBy的step方法
1. MoveBy的step是直接继承的基类的
void ActionInterval::step(float dt)
{
if (_firstTick)
{
//第一次调用则初始化
_firstTick = false;
_elapsed = 0;
}
else
{
//非第一次调用,开始累计逝去的美好时光
_elapsed += dt;
}
//最终调用到虚函数update,注意这个参数的意义
this->update(MAX (0, // needed for rewind. elapsed could be negative
MIN(1, _elapsed /
MAX(_duration, FLT_EPSILON) // division by 0
)
)
);
}
- MoveBy::update
void MoveBy::update(float t)
{
//具体算法我不关注了, 我们现在关注的流程是action最中如何改变Node属性的。
//下面的关键代码 _target->setPosition(xxx);
//到此, Action真正起作用了, 'run'起来了。
if (_target)
{
#if CC_ENABLE_STACKABLE_ACTIONS
Vec2 currentPos = _target->getPosition();
Vec2 diff = currentPos - _previousPosition;
_startPosition = _startPosition + diff;
Vec2 newPos = _startPosition + (_positionDelta * t);
_target->setPosition(newPos);
_previousPosition = newPos;
#else
_target->setPosition(_startPosition + _positionDelta * t);
#endif // CC_ENABLE_STACKABLE_ACTIONS
}
}
- 执行流程都类似, 只不过不同的action改变的属性不同和改变属性的算法不同而已, 一般体现在Action的step或者update的实现不同。
- 使用action的标准步骤是create action+ xxNode runAction
- 但runAction并非真正的run,这步仅调用了action manager的add action, 并在action manager的撮合下action结识了xxNode
- 驱动action真正run的是action manager, 他是导演的一个成员变量, 在初始化后就被加入到了调度器导致游戏每一帧里都会调用action manager的update方法。 action manger的update方法最终调用action的step方法进行相应的算法处理并最终调用到action的update方法来改变xxNode的相关属性。
Just build something.