从零开始のcocos2dx生活(四)ActionManager
文章目录
初始化构造函数
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的更多相关文章
- 从零开始のcocos2dx生活(七)ParticleSystem
CCParticleSystem是用来设置粒子效果的类 1.粒子分为两种模式:重力模式 和 半径模式 重力模式独占属性: gravity 重力方向,Vec2类型,可以分别指定不同方向的重力大小 spe ...
- 从零开始のcocos2dx生活(二)Node
节点 Node 文章目录 节点 Node 前言 变量初始化 创建一个节点对象 获取节点依赖的计数器 获取节点的描述(获取节点的Tag) 节点的局部层顺序值(LocalZOrder) 设置节点的Loca ...
- 从零开始のcocos2dx生活(十一)TableView
目录 简述 主要变量 主要方法 setVerticalFillOrder reloadData cellAtIndex updateCellAtIndex insertCellAtIndex remo ...
- 从零开始のcocos2dx生活(十)ScrollView
目录 简介 基础变量 ScrollViewDelegate Direction _dragging _container _touchMoved _bounceable _touchLength 方法 ...
- 从零开始のcocos2dx生活(九)CCBReader
NodeLoaderLibrary是用来存储节点加载器类型的类,通过registerDefaultNodeLoaders()可以注册所有默认类型的加载器 在CocosBuilder的使用手册中: 1. ...
- 从零开始のcocos2dx生活(八)ParticleSystemQuad
https://learnopengl-cn.github.io/01%20Getting%20started/04%20Hello%20Triangle/#_1 写的真的非常好-最近没时间拜读,只看 ...
- 从零开始のcocos2dx生活(六)EventDispatcher
EventDispatcher可能是所有的里面比较不容易理解也不容易看的 我说自己的理解可能会误导到你们-[索了你们看不下去>< 我写了几乎所有的代码的注释,有的是废话跳过就好 主要的代码 ...
- 从零开始のcocos2dx生活(一)内存管理
cocos中所有的对象都是继承自Ref基类,Ref的职责就是对对象进行引用计数管理 内存管理中最重要的是三个方法retain().release().autorelease() 在cocos中创建对象 ...
- 从零开始のcocos2dx生活(五)ActionEase
文章目录 sineEaseIn sineEaseOut sineEaseInOut expoEaseIn expoEaseOut expoEaseInOut easeIn easeOut easeIn ...
随机推荐
- JVM -XX: 参数列表
功能开关: 参数 默认值或限制 说明 参数 默认值 功能 -XX:-AllowUserSignalHandlers 限于Linux和Solaris,默认不启用 允许为java进程安装信号处理器,信号处 ...
- Wood Processing牛客第十场 斜率优化DP
卧槽我感觉写的是对的,但是就是样例都过不了...留坑 #include<iostream> #include<stdio.h> #include<string.h> ...
- 用户注册页的布局及js逻辑实现(正则,注册按钮)
文章地址:https://www.cnblogs.com/sandraryan/ 先写一个简单的静态页面,然后对用户输入的内容进行验证,判断输入的值是否符合规则,符合规则进行注册 先上静态页面 < ...
- Python 函数参数有冒号 声明后有-> 箭头 返回值注释 参数类型注释
在python3.7 环境下 函数声明时能在参数后加冒号,如图: 1 def f(ham: str, eggs: str = 'eggs') -> str : 2 print("Ann ...
- DataTable添加单个或多个字段组成的主键,实现查找
单列主键 DataTable fdt = CmmDb.GetDataTable(orgsql); fdt.PrimaryKey = new DataColumn[] { fdt.Columns[&qu ...
- [全+转载] solaris 网络配置
===================== 较为重要的几个文件: /etc/nodename 主机名(即 hostname命令的输出) /etc/defaul ...
- JAVA配置系统变量
CLASSPATH= .;%JAVA_HOME%/lib/dt.jar;%JAVA_HOME%/lib/tools.jar JAVA_HOME = C:/Program Files/Java/jdk1 ...
- 2019-8-2-WPF-从文件加载字体
title author date CreateTime categories WPF 从文件加载字体 lindexi 2019-08-02 17:10:33 +0800 2018-2-13 17:2 ...
- H3C ACL的标识
- 【codeforces 761A】Dasha and Stairs
time limit per test2 seconds memory limit per test256 megabytes inputstandard input outputstandard o ...