首页 > 代码库 > Cocos2d-x学习笔记(七)CCAction原理分析
Cocos2d-x学习笔记(七)CCAction原理分析
原创文章,转载请注明出处:http://blog.csdn.net/sfh366958228/article/details/38824987
前言
上一讲学习笔记,我们学习了CCAction,了解了各种CCAction的子类,心跳的效果虽然简单,但是却能让人好奇,它具体是怎么实现,这一讲我们将对此进行揭秘,方法很简单,从runAction方法调用开始。
CCNode::runAction
CCAction * CCNode::runAction(CCAction* action) { CCAssert( action != NULL, "Argument must be non-nil"); m_pActionManager->addAction(action, this, !m_bRunning); return action; }runAction的结构很简单,一个断言,确保动作(action)不为空,紧接着调用了ActionManager的addAction方法,传递action、target(执行动作节点)以及m_bRunning(节点当前是否running)
CCActionManager::addAction
void CCActionManager::addAction(CCAction *pAction, CCNode *pTarget, bool paused) { CCAssert(pAction != NULL, ""); // 确保动作和节点不为空 CCAssert(pTarget != NULL, ""); tHashElement *pElement = NULL; // 创建一个Hash列表元素,并置为空 CCObject *tmp = pTarget; HASH_FIND_INT(m_pTargets, &tmp, pElement); // 通过tmp找到对应的哈希表项返回给pElement if (! pElement) // 如果没有找到该哈希表项 { pElement = (tHashElement*)calloc(sizeof(*pElement), 1); // 为pElement分配空间 pElement->paused = paused; // 将pTarget的运行状态赋给pElement的paused pTarget->retain(); // 增加pTarget引用计数 pElement->target = pTarget; // 将pTarget赋给pElement的target HASH_ADD_INT(m_pTargets, target, pElement); // 添加pElement至哈希表中 } actionAllocWithHashElement(pElement); // 分配动作空间 CCAssert(! ccArrayContainsObject(pElement->actions, pAction), ""); //判断action是否在element的动作集中。确保只放入一次。 ccArrayAppendObject(pElement->actions, pAction); // 将action添加到pElement的动作集中 pAction->startWithTarget(pTarget); // 给动作设置执行的节点 }addAction比runAction稍显复杂但是逐行代码来看,也并不复杂,主要是将动作添加到节点对应的哈希表项中去,如果不存在对应哈希表项,则新建一个。actionAllocWithHashElement函数确保了action有存储空间,随后将pAction存入哈希表项中。一切ok之后再调用pAction的startWithTarget方法,设置执行动作的节点
CCActionManager::actionAllocWithHashElement
void CCActionManager::actionAllocWithHashElement(tHashElement *pElement) { if (pElement->actions == NULL) {// 判断pElement的actions是否为空白,如果为空则分配4个空间 pElement->actions = ccArrayNew(4); } else if (pElement->actions->num == pElement->actions->max) {// 如果pElement的容量已满,则将pElement的容量翻倍 ccArrayDoubleCapacity(pElement->actions); } }
CCActionManager::update
讲到这,一切已经妥当,我们开始讲最为核心的update方法,这个方法才是真正使动作“动”起来的原因。首先,这个update方法隶属于CCActionManager。
据有关资料介绍,它是在CCDisplayLinkDirector的mainLoop->drawScene里调用m_pScheduler->update(m_fDeltaTime)中被调用的。
m_pScheduler是CCSchedule类型,他负责将系统全部子系统更新,包括Touch、Action、脚本、定时器等等,Action是在优先级<0的那个list里面的。日后有机会将仔细研究,我们继续回到CCActionManager的update方法。
void CCActionManager::update(float dt) { for (tHashElement *elt = m_pTargets; elt != NULL; ) // 遍历存储节点的哈希表 { m_pCurrentTarget = elt; // 将当前哈希表项保存至m_pCurrentTarget变量中 m_bCurrentTargetSalvaged = false; // 将当前节点的回收标记置为否 if (! m_pCurrentTarget->paused) // 如果该项处于暂停状态 { for (m_pCurrentTarget->actionIndex = 0; m_pCurrentTarget->actionIndex < m_pCurrentTarget->actions->num; m_pCurrentTarget->actionIndex++) { // 遍历当前节点所有动作 m_pCurrentTarget->currentAction = (CCAction*)m_pCurrentTarget->actions->arr[m_pCurrentTarget->actionIndex]; // 将遍历到的动作保存到m_pCurrentTarget->currentAction中 if (m_pCurrentTarget->currentAction == NULL) { // 如果遍历项动作为空则跳过 continue; } m_pCurrentTarget->currentActionSalvaged = false; //设置回收标记为否 m_pCurrentTarget->currentAction->step(dt); // 更新当前动作的播放 if (m_pCurrentTarget->currentActionSalvaged) // 如果当前动作的回收标记为是,则进行回收处理 { m_pCurrentTarget->currentAction->release(); } else if (m_pCurrentTarget->currentAction->isDone()) // 否则判断当前节点的当前动作是否播放结束 { m_pCurrentTarget->currentAction->stop(); // 停止动作 CCAction *pAction = m_pCurrentTarget->currentAction; // 为了在removeAction中正确释放动作,这里先创建一个临时变量pAction记录一下要释放的动作 m_pCurrentTarget->currentAction = NULL; // 在removeAction之前将当前哈希表项中的当前动作设为NULL,否则不能释放 removeAction(pAction); // 释放临时存储的动作 } m_pCurrentTarget->currentAction = NULL; } } // 将elt指向下一个 elt = (tHashElement*)(elt->hh.next); // 如果当前哈希表项处于回收状态且其动作集为空,删除此哈希表项 if (m_bCurrentTargetSalvaged && m_pCurrentTarget->actions->num == 0) { deleteHashElement(m_pCurrentTarget); } } // 将变量m_pCurrentTarge置空 m_pCurrentTarget = NULL; }其实update的代码很简单,就是遍历m_pTargets哈希列表,然后逐个更新每个tHashElement 里面的动作,当然他会有一些判断,比如动作是否暂停,是否结束之类的,并做相应处理,不如终止了自然要remove掉。如果一切正常,那就调用动作的step方法(这里必须知道多态的概念,因为step和后面的update方法都是virtual的)。
CCActionInterval::step
void CCActionInterval::step(float dt) { if (m_bFirstTick) { // 如果是播放开始第一次进行时间流逝处理,将时间累和值设为0。 m_bFirstTick = false; m_elapsed = 0; } else { m_elapsed += dt; // 动作时间累和 } // 调用update函数更新动作。参数的结果是动作的时间插值结果,它代表了动作的进度,之前讲过它取0~1之间的值。这里MIN和MAX用来将计算结果限定在0~1间 this->update(MAX (0, MIN(1, m_elapsed / MAX(m_fDuration, FLT_EPSILON) ) ) ); }
CCMoveTo::update
void CCMoveTo::update(float time) { if (m_pTarget) // 如果节点不为空 { m_pTarget->setPosition(ccp(m_startPosition.x + m_delta.x * time, m_startPosition.y + m_delta.y * time)); // 设置节点坐标 } }
结语
一个动作的实现原理就这么讲完了,如果有兴趣的话,大家也可以看看Jump、Scale的实现方法,基础的动画实现方法都是大同小异。
参考资料
1)剖析cocos2d-x之Action实现:http://www.linuxidc.com/Linux/2013-04/82436.htm
2)Cocos2d-x 2.0 之Actions (三):http://bbs.9ria.com/thread-198822-1-1.html
3)Cocos2d-x 2.0 之 Actions “三板斧” 之一:http://blog.csdn.net/honghaier/article/details/8197892
Cocos2d-x学习笔记(七)CCAction原理分析
声明:以上内容来自用户投稿及互联网公开渠道收集整理发布,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任,若内容有误或涉及侵权可进行投诉: 投诉/举报 工作人员会在5个工作日内联系你,一经查实,本站将立刻删除涉嫌侵权内容。