cocos2d-x 3.x ActionManager解析
撰写于 2018-05-16 修改于 2018-05-16 分类 游戏开发
初始化
ActionManager类中虽然没有单例的处理,但是确实是一个单例,只能通过Director获取,由于Director是一个单例,所以可以认为ActionManager也是一个单例。ActionManager初始化是在Director的初始化函数init()中做的处理,actionManager = new (std::nothrow) ActionManager()
使用
我们在执行Action时,并没有使用到ActionManager,大多数情况下只是使用 target->runAction(),但其实Action都是由ActionManager来处理的,拿Node中的runAction()来说一下:1
2
3
4
5
6Action * Node::runAction(Action* action)
{
CCASSERT( action != nullptr, "Argument must be non-nil");
_actionManager->addAction(action, this, !_running);
return action;
}
可以看到runAction中并没有立即去执行action,而是调用了actionManager的addAction,把action和target(this)传入该函数1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27void 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;
HASH_FIND_PTR(_targets, &tmp, 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);
CCASSERT(! ccArrayContainsObject(element->actions, action), "action already be added!");
ccArrayAppendObject(element->actions, action);
action->startWithTarget(target);
}
addAction()函数,会维护一个uthash类型的数据_targets,里面的内容是自定义的数据结构_hashElement,整个ActionManager都是在使用并维护该数据,所有target的所有action都放在这里面。该函数最重要的就是最后一行代码action->startWithTarget(target) 把动作的执行交由具体的action处理。1
2
3
4
5
6
7
8
9
10typedef struct _hashElement
{
struct _ccArray *actions;
Node *target;
int actionIndex;
Action *currentAction;
bool currentActionSalvaged;
bool paused;
UT_hash_handle hh;
} tHashElement;
其实在startWithTarget(target)中,基本没有做处理(大多数的action),那action究竟是怎么执行的呢? 这就涉及scheduler了,在actionManager初始化的时候,scheduler也做了初始化,并启动了一个帧刷新的计划,在每一帧会执行actionManager的update()函数。1
2
3
4
5
6
7
8
9
10bool Director::init(void)
{
/*...*/
/* scheduler */
_scheduler = new (std::nothrow) Scheduler();
/* action manager */
_actionManager = new (std::nothrow) ActionManager();
_scheduler->scheduleUpdate(_actionManager, Scheduler::PRIORITY_SYSTEM, false);
/*...*/
}
具体的渲染部分都在actionManager的update()中处理1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63void ActionManager::update(float dt)
{
for (tHashElement *elt = _targets; elt != nullptr; )
{
_currentTarget = elt;
_currentTargetSalvaged = false;
if (! _currentTarget->paused)
{
/* The 'actions' MutableArray may change while inside this loop.*/
for (_currentTarget->actionIndex = 0; _currentTarget->actionIndex < _currentTarget->actions->num;
_currentTarget->actionIndex++)
{
_currentTarget->currentAction = (Action*)_currentTarget->actions->arr[_currentTarget->actionIndex];
if (_currentTarget->currentAction == nullptr)
{
continue;
}
_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.*/
_currentTarget->currentAction->release();
} else
if (_currentTarget->currentAction->isDone())
{
_currentTarget->currentAction->stop();
Action *action = _currentTarget->currentAction;
/* Make currentAction nil to prevent removeAction from salvaging it.*/
_currentTarget->currentAction = nullptr;
removeAction(action);
}
_currentTarget->currentAction = nullptr;
}
}
/* elt, at this moment, is still valid
so it is safe to ask this here (issue #490) */
elt = (tHashElement*)(elt->hh.next);
/* only delete currentTarget if no actions were scheduled during the cycle (issue #481) */
if (_currentTargetSalvaged && _currentTarget->actions->num == 0)
{
deleteHashElement(_currentTarget);
}
/*if some node reference 'target', it's reference count >= 2 (issues #14050)*/
else if (_currentTarget->target->getReferenceCount() == 1)
{
deleteHashElement(_currentTarget);
}
}
/* issue #635 */
_currentTarget = nullptr;
}
这个函数就很简单了,遍历_targets,执行所有target的所有action,具体执行action的代码就是_currentTarget->currentAction->step(dt);,也就是说每帧都执行一次action的step()函数。以Show为例吧。Show继承ActionInstant,ActionInstant中的step(dt)会调用update(dt),而update(dt)就是具体的渲染逻辑。1
2
3
4
5
6
7
8
9
10
11void ActionInstant::step(float dt)
{
/* 核心code */
float updateDt = 1;
update(updateDt);
}
/* Show继承自 ActionInstant */
void Show::update(float time)
{
_target->setVisible(true);
}
总结一下ActionManager:
- 所有的antion都会交由ActionManager保存并处理
- ActionManager维护一个自定义的数据数据结构hashElement,里面重要的数据有:动作以及动作的执行对象target
- 动作的具体执行在帧函数update中,交由每一个具体的动作去处理。