初始化构造函数

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

从零开始の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. ROS 设置串口USB软连接

    原创:未经同意,请勿转载 我们在windows 通过USB连接串口,在设备串口中可以观测到COM0或者COMx.当我们插入不同的USB口时会显示不同的COM. 在UBUNTU下,ROS下接收串口信息时 ...

  2. Knative 初体验:Eventing Hello World

    作者 | 阿里云智能事业群高级开发工程师 元毅 基于事件驱动是Serveless的核心功能之一,通过事件驱动服务,满足了用户按需付费(Pay-as-you-go)的需求.在之前的文章中我们介绍过 Kn ...

  3. @bzoj - 2395@ [Balkan 2011]Timeismoney

    目录 @description@ @solution@ @accepted code@ @details@ @description@ 有n个城市(编号从0..n-1),m条公路(双向的),从中选择n ...

  4. Python基础:26模块

    一:模块和文件 1:模块是逻辑上组织 Python 代码的方法,文件是物理层上组织模块的方法.因此,一个文件被看作是一个独立模块,一个模块也可以被看作是一个文件. 模块的文件名就是模块的名字加上扩展名 ...

  5. Duplicate keys detected: '0'. This may cause an update error.

    在运行vue项目的时候报了:[Vue warn]: Duplicate keys detected: ‘0’. This may cause an update error(错误,检测到重复的key值 ...

  6. H3C-PPPOE

    --------------pppoe拨号V7------------------------------- dialer-group 10 rule ip permit interface Dial ...

  7. H3C 用交换机扩展以太网拓扑

  8. [转][ASP.NET Core 3框架揭秘] 跨平台开发体验: Windows [下篇]

    由于ASP.NET Core框架在本质上就是由服务器和中间件构建的消息处理管道,所以在它上面构建的应用开发框架都是建立在某种类型的中间件上,整个ASP.NET Core MVC开发框架就是建立在用来实 ...

  9. HDU 1051

    题意:给你n个木块的长和宽,现在要把它送去加工,这里怎么说呢,就是放一个木块花费一分钟,如果后面木块的长和宽大于等于前面木块的长和宽就不需要花费时间,否则时间+1,问把这个木块送去加工的最短时间. 思 ...

  10. 浅谈集合框架五——集合框架扩展:Collections工具类的使用,自定义比较器

    最近刚学完集合框架,想把自己的一些学习笔记与想法整理一下,所以本篇博客或许会有一些内容写的不严谨或者不正确,还请大神指出.初学者对于本篇博客只建议作为参考,欢迎留言共同学习. 之前有介绍集合框架的体系 ...