初始化构造函数

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

析构函数

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

删除哈希元素

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

分配存放动作对象的空间

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

通过索引移除动作

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

  1. void ActionManager::removeActionAtIndex(ssize_t index, tHashElement *element)
  2. {
  3. //获取要移除的action对象
  4. Action *action = static_cast<Action*>(element->actions->arr[index]);
  5. //判断是否是正在运行的action,一般不会进入这个判断,
  6. //除非是正在执行update的step的时候,异步线程调用了remove来移除正在执行的这个action,
  7. //才会进到这个判断中,currentActionSalvaged就是一个安全锁,确保了动作执行不会出错
  8. if (action == element->currentAction && (! element->currentActionSalvaged))
  9. {
  10. element->currentAction->retain(); //引用计数+1
  11. element->currentActionSalvaged = true; //标记将要删除
  12. }
  13. //从actions中移除索引index的action
  14. ccArrayRemoveObjectAtIndex(element->actions, index, true);
  15. // update actionIndex in case we are in tick. looping over the actions
  16. if (element->actionIndex >= index)
  17. {
  18. //索引总数-1
  19. element->actionIndex--;
  20. }
  21. //如果节点没有动作,将节点删除(延迟删除和立即删除)
  22. if (element->actions->num == 0)
  23. {
  24. if (_currentTarget == element)
  25. {
  26. _currentTargetSalvaged = true;
  27. }
  28. else
  29. {
  30. deleteHashElement(element);
  31. }
  32. }
  33. }

暂停动作

  1. void ActionManager::pauseTarget(Node *target)
  2. {
  3. tHashElement *element = nullptr;
  4. HASH_FIND_PTR(_targets, &target, element);//找到目标节点返回element
  5. if (element)//标记暂停
  6. {
  7. element->paused = true;
  8. }
  9. }

恢复动作

  1. void ActionManager::resumeTarget(Node *target)
  2. {
  3. tHashElement *element = nullptr;
  4. HASH_FIND_PTR(_targets, &target, element);//找到目标节点返回element
  5. if (element) //标记不暂停
  6. {
  7. element->paused = false;
  8. }
  9. }

暂停所有的动作

  1. Vector<Node*> ActionManager::pauseAllRunningActions()
  2. {
  3. Vector<Node*> idsWithActions;
  4. for (tHashElement *element=_targets; element != nullptr; element = (tHashElement *)element->hh.next) //遍历target链表,paused是true就修改为false
  5. {
  6. if (! element->paused)
  7. {
  8. element->paused = true;
  9. idsWithActions.pushBack(element->target);
  10. }
  11. }
  12. return idsWithActions;
  13. }

恢复所有的动作

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

添加动作

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

移除所有的动作

  1. void ActionManager::removeAllActions()
  2. {
  3. for (tHashElement *element = _targets; element != nullptr; )//清空所有的target的动作
  4. {
  5. auto target = element->target;
  6. element = (tHashElement*)element->hh.next;
  7. removeAllActionsFromTarget(target);
  8. }
  9. }

移除target中的所有动作

  1. void ActionManager::removeAllActionsFromTarget(Node *target)
  2. {
  3. // explicit null handling
  4. if (target == nullptr)
  5. {
  6. return;
  7. }
  8. //在target列表中查找目标target
  9. tHashElement *element = nullptr;
  10. HASH_FIND_PTR(_targets, &target, element);
  11. //找到目标
  12. if (element)
  13. {
  14. //并且当前的action在action列表中,并且没有被标记为Salvaged
  15. //salvaged标记的意义是,当前动作需要被释放,但它还没有完成,需要完成之后在释放,所以先做标记,之后动作完成之后释放,类似于延迟release
  16. if (ccArrayContainsObject(element->actions, element->currentAction) && (! element->currentActionSalvaged))
  17. {
  18. element->currentAction->retain();
  19. element->currentActionSalvaged = true;
  20. }
  21. ccArrayRemoveAllObjects(element->actions);//移除目标节点的所有动作
  22. /*
  23. while (arr->num > 0)
  24. {
  25. (arr->arr[--arr->num])->release();
  26. }
  27. */
  28. //当前节点的动作都被移除了,节点没有作用了,之后也会被删除
  29. if (_currentTarget == element)
  30. {
  31. _currentTargetSalvaged = true; //当前正在运行,之后移除
  32. }
  33. else
  34. {
  35. deleteHashElement(element); //不在运行,立即release
  36. }
  37. }
  38. }

移除动作

  1. void ActionManager::removeAction(Action *action)
  2. {
  3. // explicit null handling
  4. if (action == nullptr)
  5. {
  6. return;
  7. }
  8. tHashElement *element = nullptr;
  9. Ref *target = action->getOriginalTarget();//获取和动作绑定的节点
  10. HASH_FIND_PTR(_targets, &target, element);
  11. //在actions数组中查找需要移除的action,找到就移除
  12. if (element)
  13. {
  14. auto i = ccArrayGetIndexOfObject(element->actions, action);
  15. if (i != CC_INVALID_INDEX)
  16. {
  17. removeActionAtIndex(i, element);
  18. }
  19. }
  20. }

通过Tag移除Action

  1. void ActionManager::removeActionByTag(int tag, Node *target)
  2. {
  3. CCASSERT(tag != Action::INVALID_TAG, "Invalid tag value!");
  4. CCASSERT(target != nullptr, "target can't be nullptr!");
  5. if (target == nullptr)
  6. {
  7. return;
  8. }
  9. tHashElement *element = nullptr;
  10. //在target列表中查找需要的target,返回element+
  11. HASH_FIND_PTR(_targets, &target, element);
  12. if (element)
  13. {
  14. auto limit = element->actions->num; //获取action的个数
  15. for (int i = 0; i < limit; ++i) //遍历action,找到对应的tag即删除action
  16. {
  17. Action *action = static_cast<Action*>(element->actions->arr[i]);
  18. if (action->getTag() == (int)tag && action->getOriginalTarget() == target)
  19. {
  20. removeActionAtIndex(i, element);
  21. break;
  22. }
  23. }
  24. }
  25. }

通过Tag移除所有的action

  1. void ActionManager::removeAllActionsByTag(int tag, Node *target)
  2. {
  3. CCASSERT(tag != Action::INVALID_TAG, "Invalid tag value!");
  4. CCASSERT(target != nullptr, "target can't be nullptr!");
  5. if (target == nullptr)
  6. {
  7. return;
  8. }
  9. tHashElement *element = nullptr;
  10. HASH_FIND_PTR(_targets, &target, element);
  11. if (element)
  12. {
  13. auto limit = element->actions->num;
  14. for (int i = 0; i < limit;) //遍历动作列表,删除所有与需要删除的tag匹配的action
  15. {
  16. Action *action = static_cast<Action*>(element->actions->arr[i]);
  17. if (action->getTag() == (int)tag && action->getOriginalTarget() == target)
  18. {
  19. removeActionAtIndex(i, element);
  20. --limit;
  21. }
  22. else
  23. {
  24. ++i;
  25. }
  26. }
  27. }
  28. }

获取指定target下的action个数

  1. ssize_t ActionManager::getNumberOfRunningActionsInTarget(const Node *target) const
  2. {
  3. tHashElement *element = nullptr;
  4. HASH_FIND_PTR(_targets, &target, element);
  5. if (element)
  6. {
  7. return element->actions ? element->actions->num : 0;
  8. }
  9. return 0;
  10. }

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

  1. size_t ActionManager::getNumberOfRunningActionsInTargetByTag(const Node *target,
  2. int tag)
  3. {
  4. CCASSERT(tag != Action::INVALID_TAG, "Invalid tag value!");
  5. tHashElement *element = nullptr;
  6. HASH_FIND_PTR(_targets, &target, element);
  7. if(!element || !element->actions)
  8. return 0;
  9. int count = 0;
  10. auto limit = element->actions->num;
  11. for(int i = 0; i < limit; ++i) //循环action列表,tag符合则计数器+1
  12. {
  13. auto action = static_cast<Action*>(element->actions->arr[i]);
  14. if(action->getTag() == tag)
  15. ++count;
  16. }
  17. return count;
  18. }

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

  1. ssize_t ActionManager::getNumberOfRunningActions() const
  2. {
  3. ssize_t count = 0;
  4. struct _hashElement* element = nullptr;
  5. struct _hashElement* tmp = nullptr;
  6. HASH_ITER(hh, _targets, element, tmp)//循环target列表
  7. {
  8. count += (element->actions ? element->actions->num : 0);
  9. }
  10. return count;
  11. }

!ActionManager的刷新函数

  1. void ActionManager::update(float dt)
  2. {
  3. //遍历targets
  4. for (tHashElement *elt = _targets; elt != nullptr; )
  5. {
  6. _currentTarget = elt; //将当前遍历到的target保存为currentTarget
  7. _currentTargetSalvaged = false;//将延迟删除标记设为false
  8. if (! _currentTarget->paused) //如果不是暂停状态
  9. {
  10. // The 'actions' MutableArray may change while inside this loop.
  11. //遍历当前节点的所有action
  12. for (_currentTarget->actionIndex = 0; _currentTarget->actionIndex < _currentTarget->actions->num;
  13. _currentTarget->actionIndex++)
  14. {
  15. //获取当前action
  16. _currentTarget->currentAction = static_cast<Action*>(_currentTarget->actions->arr[_currentTarget->actionIndex]);
  17. //如果当前节点的当前动作是空,则遍历下一个action
  18. if (_currentTarget->currentAction == nullptr)
  19. {
  20. continue;
  21. }
  22. //先设置释放标记为false
  23. _currentTarget->currentActionSalvaged = false;
  24. //刷新当前动作的进程(根据经过时间和设置的时间判断动作需要执行多少)
  25. _currentTarget->currentAction->step(dt);
  26. //如果被标记为需要释放则释放(正常不会)
  27. if (_currentTarget->currentActionSalvaged)
  28. {
  29. // The currentAction told the node to remove it. To prevent the action from
  30. // accidentally deallocating itself before finishing its step, we retained
  31. // it. Now that step is done, it's safe to release it.
  32. //对应上面延时删除时的retain
  33. _currentTarget->currentAction->release();
  34. } else
  35. //否则判断是否已经完成 ,完成则停止动作
  36. if (_currentTarget->currentAction->isDone())
  37. {
  38. _currentTarget->currentAction->stop();
  39. //获取当前action对象
  40. Action *action = _currentTarget->currentAction;
  41. // Make currentAction nil to prevent removeAction from salvaging it.
  42. //将当前动作变量设置为空
  43. _currentTarget->currentAction = nullptr;
  44. removeAction(action);//释放临时变量中已经完成的action
  45. }
  46. //初始化currentAction变量
  47. _currentTarget->currentAction = nullptr;
  48. }
  49. }
  50. // elt, at this moment, is still valid
  51. // so it is safe to ask this here (issue #490)
  52. //取得下一个target
  53. elt = (tHashElement*)(elt->hh.next);
  54. // only delete currentTarget if no actions were scheduled during the cycle (issue #481)
  55. //如果当前target被标记为需要释放,并且当前节点的动作列表为空,则立即释放当前节点
  56. if (_currentTargetSalvaged && _currentTarget->actions->num == 0)
  57. {
  58. deleteHashElement(_currentTarget);
  59. }
  60. //if some node reference 'target', it's reference count >= 2 (issues #14050)
  61. //否则判断其引用标记是否为1,是则释放当前节点
  62. else if (_currentTarget->target->getReferenceCount() == 1)
  63. {
  64. deleteHashElement(_currentTarget);
  65. }
  66. }
  67. // issue #635
  68. //遍历结束,哈希表中的当前节点指针设置为空
  69. _currentTarget = nullptr;
  70. }

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的结构是这样的:

从零开始のcocos2dx生活(四)ActionManager的更多相关文章

  1. 从零开始のcocos2dx生活(七)ParticleSystem

    CCParticleSystem是用来设置粒子效果的类 1.粒子分为两种模式:重力模式 和 半径模式 重力模式独占属性: gravity 重力方向,Vec2类型,可以分别指定不同方向的重力大小 spe ...

  2. 从零开始のcocos2dx生活(二)Node

    节点 Node 文章目录 节点 Node 前言 变量初始化 创建一个节点对象 获取节点依赖的计数器 获取节点的描述(获取节点的Tag) 节点的局部层顺序值(LocalZOrder) 设置节点的Loca ...

  3. 从零开始のcocos2dx生活(十一)TableView

    目录 简述 主要变量 主要方法 setVerticalFillOrder reloadData cellAtIndex updateCellAtIndex insertCellAtIndex remo ...

  4. 从零开始のcocos2dx生活(十)ScrollView

    目录 简介 基础变量 ScrollViewDelegate Direction _dragging _container _touchMoved _bounceable _touchLength 方法 ...

  5. 从零开始のcocos2dx生活(九)CCBReader

    NodeLoaderLibrary是用来存储节点加载器类型的类,通过registerDefaultNodeLoaders()可以注册所有默认类型的加载器 在CocosBuilder的使用手册中: 1. ...

  6. 从零开始のcocos2dx生活(八)ParticleSystemQuad

    https://learnopengl-cn.github.io/01%20Getting%20started/04%20Hello%20Triangle/#_1 写的真的非常好-最近没时间拜读,只看 ...

  7. 从零开始のcocos2dx生活(六)EventDispatcher

    EventDispatcher可能是所有的里面比较不容易理解也不容易看的 我说自己的理解可能会误导到你们-[索了你们看不下去>< 我写了几乎所有的代码的注释,有的是废话跳过就好 主要的代码 ...

  8. 从零开始のcocos2dx生活(一)内存管理

    cocos中所有的对象都是继承自Ref基类,Ref的职责就是对对象进行引用计数管理 内存管理中最重要的是三个方法retain().release().autorelease() 在cocos中创建对象 ...

  9. 从零开始のcocos2dx生活(五)ActionEase

    文章目录 sineEaseIn sineEaseOut sineEaseInOut expoEaseIn expoEaseOut expoEaseInOut easeIn easeOut easeIn ...

随机推荐

  1. JVM -XX: 参数列表

    功能开关: 参数 默认值或限制 说明 参数 默认值 功能 -XX:-AllowUserSignalHandlers 限于Linux和Solaris,默认不启用 允许为java进程安装信号处理器,信号处 ...

  2. Wood Processing牛客第十场 斜率优化DP

    卧槽我感觉写的是对的,但是就是样例都过不了...留坑 #include<iostream> #include<stdio.h> #include<string.h> ...

  3. 用户注册页的布局及js逻辑实现(正则,注册按钮)

    文章地址:https://www.cnblogs.com/sandraryan/ 先写一个简单的静态页面,然后对用户输入的内容进行验证,判断输入的值是否符合规则,符合规则进行注册 先上静态页面 < ...

  4. Python 函数参数有冒号 声明后有-> 箭头 返回值注释 参数类型注释

    在python3.7 环境下 函数声明时能在参数后加冒号,如图: 1 def f(ham: str, eggs: str = 'eggs') -> str : 2 print("Ann ...

  5. DataTable添加单个或多个字段组成的主键,实现查找

    单列主键 DataTable fdt = CmmDb.GetDataTable(orgsql); fdt.PrimaryKey = new DataColumn[] { fdt.Columns[&qu ...

  6. [全+转载] solaris 网络配置

    ===================== 较为重要的几个文件: /etc/nodename                      主机名(即 hostname命令的输出) /etc/defaul ...

  7. JAVA配置系统变量

    CLASSPATH= .;%JAVA_HOME%/lib/dt.jar;%JAVA_HOME%/lib/tools.jar JAVA_HOME = C:/Program Files/Java/jdk1 ...

  8. 2019-8-2-WPF-从文件加载字体

    title author date CreateTime categories WPF 从文件加载字体 lindexi 2019-08-02 17:10:33 +0800 2018-2-13 17:2 ...

  9. H3C ACL的标识

  10. 【codeforces 761A】Dasha and Stairs

    time limit per test2 seconds memory limit per test256 megabytes inputstandard input outputstandard o ...