初始化构造函数

ActionManager::ActionManager()
: _targets(nullptr), //目标节点的表头
_currentTarget(nullptr), //当前正在运行的节点
_currentTargetSalvaged(false) //释放标记
{}
typedef struct _hashElement
{
struct _ccArray *actions; //和taget绑定的所有action组成的数组
Node *target; //执行action的节点
int actionIndex; //actions的索引
Action *currentAction; //当前正在执行的action,(异步线程才会用到
bool currentActionSalvaged; //释放标记(异步线程使用
bool paused; //是否暂停
UT_hash_handle hh; //哈希表的句柄
} tHashElement; 在调用了node->runAction后,ActionManager会将node和action信息加入到hash表中。在ActionManger的update函数中遍历_targets的所有元素,并执行和每个target绑定的actions,然后对actions遍历执行step。
在Direction的初始化时调用_scheduler->scheduleUpdate(_actionManager, Scheduler::PRIORITY_SYSTEM, false);
保证了每帧都会调用ActionManager的update方法

析构函数

ActionManager::~ActionManager()
{
CCLOGINFO("deallocing ActionManager: %p", this); removeAllActions(); //移除所有的动作
}

删除哈希元素

void ActionManager::deleteHashElement(tHashElement *element)
{
ccArrayFree(element->actions); //释放element的所有动作
HASH_DEL(_targets, element); //从targets中删除element
element->target->release(); //target引用计数-1
free(element); //释放element的内存空间
}

分配存放动作对象的空间

void ActionManager::actionAllocWithHashElement(tHashElement *element)
{
// 4 actions per Node by default
if (element->actions == nullptr) //如果当前动作数组为空则分配四个空间
{
element->actions = ccArrayNew(4);
}else
if (element->actions->num == element->actions->max) //如果将要超过最大容量,就将内存加倍
{
ccArrayDoubleCapacity(element->actions);
} }

通过索引移除动作

除了移除全部动作的方法以外,都是调用这个方法来移除action

void ActionManager::removeActionAtIndex(ssize_t index, tHashElement *element)
{
//获取要移除的action对象
Action *action = static_cast<Action*>(element->actions->arr[index]);
//判断是否是正在运行的action,一般不会进入这个判断,
//除非是正在执行update的step的时候,异步线程调用了remove来移除正在执行的这个action,
//才会进到这个判断中,currentActionSalvaged就是一个安全锁,确保了动作执行不会出错
if (action == element->currentAction && (! element->currentActionSalvaged))
{
element->currentAction->retain(); //引用计数+1
element->currentActionSalvaged = true; //标记将要删除
} //从actions中移除索引index的action
ccArrayRemoveObjectAtIndex(element->actions, index, true); // update actionIndex in case we are in tick. looping over the actions
if (element->actionIndex >= index)
{
//索引总数-1
element->actionIndex--;
} //如果节点没有动作,将节点删除(延迟删除和立即删除)
if (element->actions->num == 0)
{
if (_currentTarget == element)
{
_currentTargetSalvaged = true;
}
else
{
deleteHashElement(element);
}
}
}

暂停动作

void ActionManager::pauseTarget(Node *target)
{
tHashElement *element = nullptr;
HASH_FIND_PTR(_targets, &target, element);//找到目标节点返回element
if (element)//标记暂停
{
element->paused = true;
}
}

恢复动作

void ActionManager::resumeTarget(Node *target)
{
tHashElement *element = nullptr;
HASH_FIND_PTR(_targets, &target, element);//找到目标节点返回element
if (element) //标记不暂停
{
element->paused = false;
}
}

暂停所有的动作

Vector<Node*> ActionManager::pauseAllRunningActions()
{
Vector<Node*> idsWithActions; for (tHashElement *element=_targets; element != nullptr; element = (tHashElement *)element->hh.next) //遍历target链表,paused是true就修改为false
{
if (! element->paused)
{
element->paused = true;
idsWithActions.pushBack(element->target);
}
} return idsWithActions;
}

恢复所有的动作

void ActionManager::resumeTargets(const Vector<Node*>& targetsToResume)
{
for(const auto &node : targetsToResume)//遍历所有的target,将其中的所有pause设为false
{
this->resumeTarget(node);
}
}

添加动作

void ActionManager::addAction(Action *action, Node *target, bool paused)
{
CCASSERT(action != nullptr, "action can't be nullptr!");
CCASSERT(target != nullptr, "target can't be nullptr!");
if(action == nullptr || target == nullptr)
return; tHashElement *element = nullptr;
// we should convert it to Ref*, because we save it as Ref*
Ref *tmp = target;
//在target列表中查找需要的target,返回element
HASH_FIND_PTR(_targets, &tmp, element);
//如果没查到,就新建一个element 并且初始化
if (! element)
{
element = (tHashElement*)calloc(sizeof(*element), 1);
element->paused = paused;
target->retain();
element->target = target;
HASH_ADD_PTR(_targets, target, element); //创建新的表头
} actionAllocWithHashElement(element); //target创建存储动作的列表,如果放不下了,让空间加倍 CCASSERT(! ccArrayContainsObject(element->actions, action), "action already be added!");//判断是否已经加入列表中
ccArrayAppendObject(element->actions, action);//将action加入actions数组中 action->startWithTarget(target); //(重新)设定action的target
}

移除所有的动作

void ActionManager::removeAllActions()
{
for (tHashElement *element = _targets; element != nullptr; )//清空所有的target的动作
{
auto target = element->target;
element = (tHashElement*)element->hh.next;
removeAllActionsFromTarget(target);
}
}

移除target中的所有动作

void ActionManager::removeAllActionsFromTarget(Node *target)
{
// explicit null handling
if (target == nullptr)
{
return;
}
//在target列表中查找目标target
tHashElement *element = nullptr;
HASH_FIND_PTR(_targets, &target, element);
//找到目标
if (element)
{
//并且当前的action在action列表中,并且没有被标记为Salvaged
//salvaged标记的意义是,当前动作需要被释放,但它还没有完成,需要完成之后在释放,所以先做标记,之后动作完成之后释放,类似于延迟release
if (ccArrayContainsObject(element->actions, element->currentAction) && (! element->currentActionSalvaged))
{
element->currentAction->retain();
element->currentActionSalvaged = true;
} ccArrayRemoveAllObjects(element->actions);//移除目标节点的所有动作
/*
while (arr->num > 0)
{
(arr->arr[--arr->num])->release();
}
*/
//当前节点的动作都被移除了,节点没有作用了,之后也会被删除
if (_currentTarget == element)
{
_currentTargetSalvaged = true; //当前正在运行,之后移除
}
else
{
deleteHashElement(element); //不在运行,立即release
}
}
}

移除动作

void ActionManager::removeAction(Action *action)
{
// explicit null handling
if (action == nullptr)
{
return;
} tHashElement *element = nullptr;
Ref *target = action->getOriginalTarget();//获取和动作绑定的节点
HASH_FIND_PTR(_targets, &target, element);
//在actions数组中查找需要移除的action,找到就移除
if (element)
{
auto i = ccArrayGetIndexOfObject(element->actions, action);
if (i != CC_INVALID_INDEX)
{
removeActionAtIndex(i, element);
}
}
}

通过Tag移除Action

void ActionManager::removeActionByTag(int tag, Node *target)
{
CCASSERT(tag != Action::INVALID_TAG, "Invalid tag value!");
CCASSERT(target != nullptr, "target can't be nullptr!");
if (target == nullptr)
{
return;
} tHashElement *element = nullptr;
//在target列表中查找需要的target,返回element+
HASH_FIND_PTR(_targets, &target, element);
if (element)
{
auto limit = element->actions->num; //获取action的个数
for (int i = 0; i < limit; ++i) //遍历action,找到对应的tag即删除action
{
Action *action = static_cast<Action*>(element->actions->arr[i]); if (action->getTag() == (int)tag && action->getOriginalTarget() == target)
{
removeActionAtIndex(i, element);
break;
}
}
}
}

通过Tag移除所有的action

void ActionManager::removeAllActionsByTag(int tag, Node *target)
{
CCASSERT(tag != Action::INVALID_TAG, "Invalid tag value!");
CCASSERT(target != nullptr, "target can't be nullptr!");
if (target == nullptr)
{
return;
} tHashElement *element = nullptr;
HASH_FIND_PTR(_targets, &target, element); if (element)
{
auto limit = element->actions->num;
for (int i = 0; i < limit;) //遍历动作列表,删除所有与需要删除的tag匹配的action
{
Action *action = static_cast<Action*>(element->actions->arr[i]); if (action->getTag() == (int)tag && action->getOriginalTarget() == target)
{
removeActionAtIndex(i, element);
--limit;
}
else
{
++i;
}
}
}
}

获取指定target下的action个数

ssize_t ActionManager::getNumberOfRunningActionsInTarget(const Node *target) const
{
tHashElement *element = nullptr;
HASH_FIND_PTR(_targets, &target, element);
if (element)
{
return element->actions ? element->actions->num : 0;
} return 0;
}

获取指定target下的指定tag的action的个数

size_t ActionManager::getNumberOfRunningActionsInTargetByTag(const Node *target,
int tag)
{
CCASSERT(tag != Action::INVALID_TAG, "Invalid tag value!"); tHashElement *element = nullptr;
HASH_FIND_PTR(_targets, &target, element); if(!element || !element->actions)
return 0; int count = 0;
auto limit = element->actions->num;
for(int i = 0; i < limit; ++i) //循环action列表,tag符合则计数器+1
{
auto action = static_cast<Action*>(element->actions->arr[i]);
if(action->getTag() == tag)
++count;
} return count;
}

获取所有在actions列表中的action个数

ssize_t ActionManager::getNumberOfRunningActions() const
{
ssize_t count = 0;
struct _hashElement* element = nullptr;
struct _hashElement* tmp = nullptr;
HASH_ITER(hh, _targets, element, tmp)//循环target列表
{
count += (element->actions ? element->actions->num : 0);
}
return count;
}

!ActionManager的刷新函数

void ActionManager::update(float dt)
{
//遍历targets
for (tHashElement *elt = _targets; elt != nullptr; )
{
_currentTarget = elt; //将当前遍历到的target保存为currentTarget
_currentTargetSalvaged = false;//将延迟删除标记设为false if (! _currentTarget->paused) //如果不是暂停状态
{
// The 'actions' MutableArray may change while inside this loop.
//遍历当前节点的所有action
for (_currentTarget->actionIndex = 0; _currentTarget->actionIndex < _currentTarget->actions->num;
_currentTarget->actionIndex++)
{
//获取当前action
_currentTarget->currentAction = static_cast<Action*>(_currentTarget->actions->arr[_currentTarget->actionIndex]);
//如果当前节点的当前动作是空,则遍历下一个action
if (_currentTarget->currentAction == nullptr)
{
continue;
}
//先设置释放标记为false
_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.
//对应上面延时删除时的retain
_currentTarget->currentAction->release();
} else
//否则判断是否已经完成 ,完成则停止动作
if (_currentTarget->currentAction->isDone())
{
_currentTarget->currentAction->stop();
//获取当前action对象
Action *action = _currentTarget->currentAction;
// Make currentAction nil to prevent removeAction from salvaging it.
//将当前动作变量设置为空
_currentTarget->currentAction = nullptr;
removeAction(action);//释放临时变量中已经完成的action
}
//初始化currentAction变量
_currentTarget->currentAction = nullptr;
}
} // elt, at this moment, is still valid
// so it is safe to ask this here (issue #490)
//取得下一个target
elt = (tHashElement*)(elt->hh.next); // only delete currentTarget if no actions were scheduled during the cycle (issue #481)
//如果当前target被标记为需要释放,并且当前节点的动作列表为空,则立即释放当前节点
if (_currentTargetSalvaged && _currentTarget->actions->num == 0)
{
deleteHashElement(_currentTarget);
}
//if some node reference 'target', it's reference count >= 2 (issues #14050)
//否则判断其引用标记是否为1,是则释放当前节点
else if (_currentTarget->target->getReferenceCount() == 1)
{
deleteHashElement(_currentTarget);
}
} // issue #635
//遍历结束,哈希表中的当前节点指针设置为空
_currentTarget = nullptr;
}

1、actionAllocationWithHashElement方法用来给element的动作容器第一次创建空间,并且会判断当前的actions数组能不能放得下了,放不下了会扩容,容量加倍

2、removeActionAtIndex中会通过index移除对应的action,如果移除的这个action是当前正在使用的,会retain住,并且设Salvaged为true,但还是会从actions数组中移除这个action。如果element中的actions容器也空了,并且这个element是正在使用的,会给它的salvaged设为true,在update的时候再删除(延时删除)。

3、startWithTarget主要是设置执行action的target,但会根据不同的action再执行更多的操作,比如rotateBy的startWithTarget调用了actionInterval的startWithTarget

(actionInterval的startWithTarget又执行了①FiniteTimeAction的startWithTarget,而FiniteTimeAction没有重写这个方法,实际调用的是Action的这个方法,用来设置执行aciton的target,当调用stop的时候会将target设为空。②设置了执行的时间elapsed为0。③设置了第一次执行的标记firstTick。④设置了执行完成标记done为false),

然后又设置了旋转x和y的角度。

4、removeAllAction中遍历了ActionManager中的targets来对每个元素都执行removeAllActionsFromTarget方法,从这里我们可以看到所有绑定了action的target都被放在了targets中,大概可以看出来ActionManager的部分结构。再接着往下看removeAllActionsFromTarget,在这个方法中先查找targets中是否存在要移除action的那个target;然后又判断了要移除的actions中有没有正在执行的action,如果有就ratain住不让它被删除,然后标记为待删除,延时删除,这里猜测在update中可能会有对标记为待删除的对象统一处理的代码,后面再看;然后对action容器中所有对象都release,除了上面retain的没有被删除外,都被删除了;最后判断了target是不是也正在使用,如果是,就标记为延时删除,否则release删除掉,因为actions如果已经空了,那target在ActionManager中存在就没有意义了,如果有正在执行的action,这里肯定是正在执行动作的target,延时删除它。

5、遍历targets中所有的元素(target) ,初始化延时标记为false,如果没有被暂停,遍历当前target的actions容器;

先获取当前正在执行的动作,判断是否存在,判断是不是空的,如果不是空,会将actionSalvaged置为false,然后执行action的step,刷新动作的进程;

下面判断了动作的salvaged,一般是false,只有在多线程的时候才可能会在这句代码之前执行了remove的操作,可能会让actionSalvaged变成true;

然后判断了动作是否执行完成了,因为有些动作是持续执行一段时间的,如果执行完了会在step中将done置为true;动作执行完了就执行stop方法,将action的_target设为空,并且移除了这个action;

将currentAction置为空;

往后递增;

下面判断了几个数组是不是空的,是就移除;

最后将currentTarget置为空;

ActionManager刷新方法中需要注意的是actionSalvaged标记,这个是安全锁,在update中我们可以看到它先被置为false,然后才判断是不是true,正常来说是不会执行这一步if的,但是如果在step中,也就是action正在执行的时候,异步线程执行了remove操作,就会在step的下面对执行完的action进行remove,保证的线程的安全。正常来说,如果要移除action,在removeActionAtIndex或者removeAllActions中是可以直接移除的。

如果这样写

断点之后会发现,在执行remove的时候currentTarget和currenAction都是空的,因为remove是在两次update中间执行的。

最后我们会发现ActionManager的结构是这样的:

最新文章

  1. 学习 git基础命令
  2. 【01-05】hibernate BaseDao
  3. Android网络操作的几种方法
  4. C#写入和读出文本文件
  5. C语言-两个库函数
  6. [.net 面向对象编程基础] (3) 基础中的基础——数据类型
  7. 搭建企业内部yum仓库(centos6+centos7+epel源)
  8. Spring源码追踪2——xml解析入口
  9. angularjs ngRoute demo
  10. ThreadPoolExecutor使用和思考(上)-线程池大小设置与BlockingQueue的三种实现区别
  11. DevExpress12.2.6 安装顺序记录
  12. 七中滤波方法测试matlab实现
  13. 自定义View-6 状态按钮 滑动 点击
  14. 备机大地院系项目dataguard archived_log及standby_log
  15. Android自己定义控件(状态提示图表)
  16. SecureCRT窗口输出代码关键字高亮设置
  17. Django Rest framework 之 解析器
  18. 微信小程序顶部(navigationBar)设置为透明
  19. Applese走方格-dfs
  20. 危险的浮点数float

热门文章

  1. qt 自定义窗口绘制正弦曲线
  2. @bzoj - 4356@ Ceoi2014 Wall
  3. laravel 随笔
  4. CDQ分治 三维偏序
  5. oracle优化EXPORT和IMPORT
  6. iptables禁止QQ端口
  7. 2002年NOIP普及组复赛题解
  8. 2019-10-7-dotnet-Framework-源代码-·-ScrollViewer
  9. java面向接口编程之适配器模式
  10. Java 自带性能监控工具:监视和管理控制台 jconsole 的使用