cocos2dx中精灵如何run一个动作
2015-07-30 17:45
525 查看
首先,从Action * Node::runAction(Action* action)看它的实现:
然后看到了_actionManager,这个东西是ActionManager,看看他是从哪来的:
跟到Director中,真相大白:
然后接着看_actionManager->addAction(action, this, !_running);,我们是不是要到ActionManager的addAction中看看呢
这里先插一下:ActionManager有一个HASH表来存储动作CCAction,HASH表的键还是CCObject类型的指针。CCActionManager将这些动作放在一个数组当中(tHashElement->actions)。ActionManager::addAction先根据穿进去的target作为键值查看HASH表中是否存在相应的Bucket。如果没有找到会创建一个,然后,将现在的CCAction添加到数组的末尾。并把对应的Node传给action的startWithTarget,作为对应action的target。此时,我们已经把动作添加到ActionManager的动作列表中了。
上面我们说过的,ActionManager在Director的int中会初始化,并且开启一个update调度器。然后在每帧都会调用到ActionManager的update函数。那么现在让我们来看看ActionManager的update函数的实现:
在ActionManager::update的外层循环中HASH表中所有的Bucket,内层循环遍历当前动画集中所有的actions。执行动作是通过调用action中的step函数(多态调用的),在不同的action中有不同的实现。currentActionSalvaged用来标记动作是否被要求删除。CCAction的stop虚函数是在清理动作之前执行的回调函数(这个不能手动调用,动作执行完成后会自动调用)。如果动作执行完step之后,已经完成了,那么动作就会从HASH表中Bucket的数组上删除。最后,如果某个CCSprite上已经没有附加任何动作,就删除HASH表中这个Bucket。
因为在ActionManager::update中的_currentTarget->currentAction->step(dt)会调用action的step函数,有的action(比如moveBy)没有step方法,会到父类中调用父类的step,看一下他的实现:
ActionInterval::step会调用update 方法,因为是子类对象调用的,所以this指针指向子类的对象,所以this->update会调用到子类的update函数。然后就可以播放一个完整的动作了。
先这样吧,不知道写点什么了。还是没有完全闹明白啊
Action * Node::runAction(Action* action) { CCASSERT( action != nullptr, "Argument must be non-nil"); _actionManager->addAction(action, this, !_running); return action; }
然后看到了_actionManager,这个东西是ActionManager,看看他是从哪来的:
//初始化node类的ActionManager,从_director获取过来,得到一个全局的ActionManager _actionManager = _director->getActionManager();
跟到Director中,真相大白:
_actionManager = new (std::nothrow) ActionManager(); //初始化一个ActionManager,初始化成功立即开启scheduler,保证每一帧action可以每一帧更新 _scheduler->scheduleUpdate(_actionManager, Scheduler::PRIORITY_SYSTEM, false);</span></span>
然后接着看_actionManager->addAction(action, this, !_running);,我们是不是要到ActionManager的addAction中看看呢
void ActionManager::addAction(Action *action, Node *target, bool paused) { CCASSERT(action != nullptr, ""); CCASSERT(target != nullptr, ""); tHashElement *element = nullptr; // we should convert it to Ref*, because we save it as Ref* Ref *tmp = target; HASH_FIND_PTR(_targets, &tmp, element);//通过target找到对应的哈希表项返回给element if (! element) { //如果找不到。则node对应的哈希表还不存在 则申请内存创建此哈希表项,并将其加入哈希表中。 //创建一个新的哈希表项 element = (tHashElement*)calloc(sizeof(*element), 1); element->paused = paused; //设置暂停状态 target->retain(); //引用计数加1代表被管理器使用中 element->target = target; //设置哈希表项中的演员为参数指定的演员 HASH_ADD_PTR(_targets, target, element);//将哈希表项添加到哈希表 } //为哈希表申请内存 用来存放action actionAllocWithHashElement(element); //动作已经在动作列表 中断 //判断pAction是否在pElement的动画集中。确保只放入一次 CCASSERT(! ccArrayContainsObject(element->actions, action), ""); ccArrayAppendObject(element->actions, action);//添加到动作列表 action->startWithTarget(target);//动作绑定目标 //设置是哪个CCNode要进行当前动画 }
这里先插一下:ActionManager有一个HASH表来存储动作CCAction,HASH表的键还是CCObject类型的指针。CCActionManager将这些动作放在一个数组当中(tHashElement->actions)。ActionManager::addAction先根据穿进去的target作为键值查看HASH表中是否存在相应的Bucket。如果没有找到会创建一个,然后,将现在的CCAction添加到数组的末尾。并把对应的Node传给action的startWithTarget,作为对应action的target。此时,我们已经把动作添加到ActionManager的动作列表中了。
上面我们说过的,ActionManager在Director的int中会初始化,并且开启一个update调度器。然后在每帧都会调用到ActionManager的update函数。那么现在让我们来看看ActionManager的update函数的实现:
void ActionManager::update(float dt) { //遍历哈希表中所有项 for (tHashElement *elt = _targets; elt != nullptr; ) { //将当前哈希表项保存到变量_currentTarget中。并将此项对应的回收标记m_currentTargetSalvaged 设为false, _currentTarget = elt; _currentTargetSalvaged = false; if (! _currentTarget->paused)//当前动作没有暂停 { //遍历当前动画集中所有的actions for (_currentTarget->actionIndex = 0; _currentTarget->actionIndex < _currentTarget->actions->num; _currentTarget->actionIndex++) { //取得遍历项的动画,即当前action _currentTarget->currentAction = (Action*)_currentTarget->actions->arr[_currentTarget->actionIndex]; if (_currentTarget->currentAction == nullptr) { continue; } //设置回收标志位false _currentTarget->currentActionSalvaged = false; //更新当前播放的动画 _currentTarget->currentAction->step(dt); if (_currentTarget->currentActionSalvaged) { //如果当前的回收标志位true,进行释放处理 _currentTarget->currentAction->release(); } else if (_currentTarget->currentAction->isDone()) { //如果当前动画处于结束状态,则停止动画 _currentTarget->currentAction->stop(); //为了在removeAction中正确释放动画,这里先创建一个临时变量pAction记录一下要释放的动画。 Action *action = _currentTarget->currentAction; _currentTarget->currentAction = nullptr; removeAction(action); } _currentTarget->currentAction = nullptr; } } elt = (tHashElement*)(elt->hh.next);//使for循环能够继续 // 如果当前哈希表项处于回收状态且其动画集为空,删除此哈希表项。 if (_currentTargetSalvaged && _currentTarget->actions->num == 0) { deleteHashElement(_currentTarget); } } _currentTarget = nullptr; }
在ActionManager::update的外层循环中HASH表中所有的Bucket,内层循环遍历当前动画集中所有的actions。执行动作是通过调用action中的step函数(多态调用的),在不同的action中有不同的实现。currentActionSalvaged用来标记动作是否被要求删除。CCAction的stop虚函数是在清理动作之前执行的回调函数(这个不能手动调用,动作执行完成后会自动调用)。如果动作执行完step之后,已经完成了,那么动作就会从HASH表中Bucket的数组上删除。最后,如果某个CCSprite上已经没有附加任何动作,就删除HASH表中这个Bucket。
因为在ActionManager::update中的_currentTarget->currentAction->step(dt)会调用action的step函数,有的action(比如moveBy)没有step方法,会到父类中调用父类的step,看一下他的实现:
void ActionInterval::step(float dt) { if (_firstTick) { _firstTick = false; _elapsed = 0; } else { _elapsed += dt; } this->update(MAX (0, // needed for rewind. elapsed could be negative MIN(1, _elapsed / MAX(_duration, FLT_EPSILON) // division by 0 ) ) ); }
ActionInterval::step会调用update 方法,因为是子类对象调用的,所以this指针指向子类的对象,所以this->update会调用到子类的update函数。然后就可以播放一个完整的动作了。
先这样吧,不知道写点什么了。还是没有完全闹明白啊
相关文章推荐
- Cocos2dx.3x入门三部曲-Hello Game项目创建(二)
- cocos2d-x多分辨率和随后的自适应CCListView的bug修复
- cocos2d-js添加艾盟插屏(通过jsb反射机制)
- Cocos2d-x 3.0final 终结者系列教程12-Vector&map&value
- cocos2dx飞机大战开发记录(3)
- cocos2d-x 音效音乐 SimpleAudioEngine
- 【玩转cocos2d-x之二十三】多线程和同步03-图片异步加载
- cocos2dx创建精灵的五种方法(包括使用图片名获取图片)
- 【Cocos2d-x】CCLayer
- cocos2d-x 如何保持屏幕常亮
- Cocos2d-x TitleMap A*算法实现。(初版)
- cocos2d-x 切换两个菜单项 不用MenuItemToggle
- Cocos2D
- 【win10升级】 cocos2d-x编译丢失MSVCR110.dll解决办法
- 五毛的cocos2d-x学习笔记05-场景与场景动画,动作
- cocos2d-x 切换两个菜单项 MenuItemToggle
- 【Cocos2d-x】之使用两张图片制作动画
- Cocos2dx之CCLabelBMFont类
- cocos2d-x移植安卓时解决引用sqlite3库问题
- Cocos2dx之精灵批处理