从零开始の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的结构是这样的:

在这里插入图片描述

原文地址:https://www.cnblogs.com/sakuraneo/p/11992058.html