Cocos2Dx提供了调度器。结合调度器,我们可以不断地修改节点的属性,从而实现丰富的动态效果。但是这样做过于麻烦。举个例子,我们需要把一个精灵从一个位置移动到另外一个位置。从我们前面提到的内容来思考一个可行的方案:在父节点的update中,将它的位置移动镇间隔时间移动的距离。假定目标位置到初始距离为d,移动时间指定为t秒,帧间隔时间1/60秒,那么每个update我们需要往目标位置移动d/60t。这还是匀速地移动,如果要求加速移动的效果,或者减速移动的效果,情况就变得更加复杂一些了。
为了简化节点的动作执行,一般是CCSprite,Cocos2Dx提供了CAction来支持动作。动作可以简单地分为两类,一是瞬时动作,即立即完成的动作;二是持续性动作,即动作的执行需要持续一段时间。另外,我们还需要有能够组合各种动作的能力,称之为复合动作。下面我们,先看看动作的分类。
瞬时动作。瞬时动作就是下一帧就要完成的动作,比如定位、缩放。其实这样的动作不需要定时机制,修改节点的属性即可,但还是封装为一个动作,目的是方便进行做动作的组合。瞬时动作包括:
-
CCCallFunc:CCCallFunc系列动作包括CCCallFunc、CCCallFuncN、CCCallFuncND,以及CCCall- FuncO四个动作,用来在动作中进行类的实例方法的调用。
持续性动作。持续性动作,以为着动作的执行需要持续一段时间。所以,这些都做都需要指定动作执行的时间:duration。持续性动作还可以根据产生的效果不同进一步细分。
-
位置变化动作:针对位置(position)属性。
-
CCMoveTo和CCMoveBy:用于使节点做直线运动。
-
CCJumpTo和CCJumpBy:使节点以一定的轨迹跳跃到指定位置。
-
CCBezierTo和CCBezierBy:使节点进行曲线运动,运动的轨迹由贝塞尔曲线描述。
-
by是相对于节点的位置;to是绝对位置。
-
视觉特效动作:一些特殊的视觉效果。
-
CCBlink:使目标节点闪烁。
-
CCAnimation:播放帧动画,用帧动画的形式实现动画效果。
-
-
控制动作:对一些列动作进行精细控制。
-
CCDelayTime:将动作延时一定的时间才执行。
-
CCRepeat:把现有的动作重复一定次数。
-
CCRepeatForever:现有动作一直执行下去。
-
复合动作。复合动作是由一些基本动作一起组合成的新动作。可以满足游戏需要的复杂动作需求。
-
CCSpawn:允许一批动作同时执行。
-
CCSequence:顺序执行一系列动作。
-
CCSpeed:改变动作执行的速度。
-
CCActionEase:是一类动作,不同于CCSpeed的匀速执行,CCActionEase允许多样化地控制速度。
-
CCEaseRateAction
-
CCEaseIn
-
CCEaSEOut
-
CCEaseInOut
-
-
CCEaseExponentialIn
-
CCEaseExponentialIn
-
CCEaseExponentialInOut
-
CCEaseSineIn
-
CCEaseSineOut
-
CCEaseSineInOut
-
CCEaseElastic
-
CCEaseElasticIn
-
CCEaseElasticOut
-
CCEaseElasticInOut
-
-
CCEaseBounce
-
CCEaseBounceIn
-
CCEaseBounceOut
-
CCEaseBounceInOut
-
-
CCEaseBackIn
-
CCEaseBackOut
-
CCEaseBackInOut
-
JQuery有个比较详尽的Easing效果示例。可以访问http://jqueryui.com/easing/查看。并不是所有的Easing效果Cocos2Dx都支持。
知道大概有哪些Action后,我们看看Action是怎么得到调度执行的。
我们执行一个Action的方法,一般是是调用CCSripte::runAction,因为动作一般都是作用于某个精灵的。CCSripte继承自CCNodeRGBA,后者继承自CCNode。CCNode提供了runAction来执行动作。CCLayer也继承自CCNode,因此它也是可以执行动作的。后面都假定执行动作的对象是一个CCSprite。
CCNode有一个成员m_pActionManager,它默认初始化为CCDirector内部的CCActionManager。CCDirector在它的init初始化函数中会创建一个CCActionManager对象。这类似于调度器CCScheduler。CCNode拥有指向CCActionManager对象的指针,因此在CCNode::runAction里面,通过CCActionManager的addAction将自己添加到CCActionManager当中。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
void
CCActionManager::addAction(CCAction*pAction,CCNode*pTarget,
bool
paused)
{
tHashElement*pElement=NULL;
CCObject*tmp=pTarget;
HASH_FIND_INT(m_pTargets,&tmp,pElement);
if
(!pElement)
{
pElement=(tHashElement*)
calloc
(
sizeof
(*pElement),1);
pElement->paused=paused;
pTarget->retain();
pElement->target=pTarget;
HASH_ADD_INT(m_pTargets,target,pElement);
}
actionAllocWithHashElement(pElement);
ccArrayAppendObject(pElement->actions,pAction);
pAction->startWithTarget(pTarget);
}
|
CCActionManager同样用一个HASH表来存储动作CCAction,HASH表的键还是CCObject类型的指针。每个CCSprite可能同时在执行多个动作。CCActionManager将这些动作放在一个数组当中(tHashElement->actions)。CCActionManager::addAction先根据传入的pTarget作为键来查看HASH表中是否已经存在对应的Bucket。如果没有找到,就现在HASH表中创建一个。然后,将现在的CCAction添加到数组的末尾。
到目前为止,我们只是将需要执行的动作放在一个HASH表中。什么时候执行呢?这就要看CCDirector::init。CCDirector初始化的时候,先创建一个CCScheduler,然后创建一个CCActionManager,随后马上调用CCScheduler::scheduleUpdateForTarget将自己注册为一个update调度器。并且update调度器的优先级设为了最高优先级kCCPrioritySystem(INT_MIN)。
CCScheduler::scheduleUpdateForTarget的实现我们前面已经讨论过了。它将注册的update调度器根据优先级放到不同的列表当中。update调度器在每个帧间隔时间到期之后就会被调用。CCActionManager把自己注册为一个update调度器,那么在每个帧间隔时间到期之后,就会调用CCActionManager的update函数。由于CCActionManager管理所有的动作,因此CCActionManager::update相当于管理动作的一个主循环。
注:到这里,我们已经讨论过不少Cocos2Dx的主循环:应用程序主循环、CCDirector的主循环、CCScheduler的主循环、CCScene的主循环、负责内存维护的栈循环。现在我们讨论到了负责管理动作的CCActionManager主循环。
CCActionManager::update(
float
dt)
for
(tHashElement*elt=m_pTargets;elt!=NULL;)
{
m_pCurrentTarget=elt;
m_bCurrentTargetSalvaged=
false
;
(!m_pCurrentTarget->paused)
{
(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==NULL)
{
continue
;
}
m_pCurrentTarget->currentActionSalvaged=
;
m_pCurrentTarget->currentAction->step(dt);
(m_pCurrentTarget->currentActionSalvaged)
{
m_pCurrentTarget->currentAction->release();
}
else
(m_pCurrentTarget->currentAction->isDone())
{
m_pCurrentTarget->currentAction->stop();
CCAction*pAction=m_pCurrentTarget->currentAction;
m_pCurrentTarget->currentAction=NULL;
removeAction(pAction);
}
m_pCurrentTarget->currentAction=NULL;
}
}
elt=(tHashElement*)(elt->hh.next);
(m_bCurrentTargetSalvaged&&m_pCurrentTarget->actions->num==0)
{
deleteHashElement(m_pCurrentTarget);
}
}
m_pCurrentTarget=NULL;
CCActionManager::update的外循环遍历所有的HASH表的Bucket,内循环遍历CCSprite上的所有动作。执行动作是通过调用CCAction的step函数(多态调用)。不同类型的动作,可能实现了不同的step。我们后面再看不同的step实现。currentActionSalvaged用来标记动作是否被要求删除。CCAction的isDone虚函数用来帮助CCActionManager判断当前的动作是否已经完成。CCAction的stop虚函数是在清理动作之前执行的回调函数。如果动作执行完step之后,已经完成了,那么动作就会从HASH表中Bucket的数组上删除。最后,如果某个CCSprite上已经没有附加任何动作,就删除HASH表中这个Bucket。
|
CCActionInterval::step(
(m_bFirstTick)
m_bFirstTick=
;
m_elapsed=0;
}
else
{
m_elapsed+=dt;
}
this
->update(MAX(0,MIN(1,m_elapsed/MAX(m_fDuration,FLT_EPSILON))));
|
CCJumpBy::update(
t)
(m_pTarget)
frac=fmodf(t*m_nJumps,1.0f);
y=m_height*4*frac*(1-frac);
y+=m_delta.y*t;
x=m_delta.x*t;
m_pTarget->setPosition(ccpAdd(m_startPosition,ccp(x,y)));
}
CCSpeed
猜你在找的Cocos2d-x相关文章 |