原創(chuàng)作品,轉(zhuǎn)載請(qǐng)標(biāo)明:http://blog.csdn.net/jackystudio/article/details/17019023
之前在遇到這么一個(gè)問題,在CCSequence中加入CCRepeatForever,發(fā)現(xiàn)其他動(dòng)作執(zhí)行沒問題,就是CCRepeatForever無法執(zhí)行。代碼并沒有問題,很奇怪。
1.示例
-
CCBlink* blink=CCBlink::create(0.5f,10);//創(chuàng)建閃爍動(dòng)畫,duration=0.5s
-
CCAnimation* animation=CCAnimation::create();
-
animation->addSpriteFrameWithFileName("CloseNormal.png");
-
animation->addSpriteFrameWithFileName("CloseSelected.png");
-
animation->setDelayPerUnit(1.0f);//幀間間隔1s
-
CCAnimate* animate=CCAnimate::create(animation);//創(chuàng)建幀動(dòng)畫
-
CCRepeatForever* repeat=CCRepeatForever::create(animate);
-
CCSequence* sequence=CCSequence::create(blink,repeat,NULL);//創(chuàng)建連續(xù)動(dòng)畫
-
CCSprite* close=CCSprite::create("CloseNormal.png");
-
close->setPosition(ccp(240,160));
-
this->addChild(close);
-
close->runAction(sequence);//執(zhí)行連續(xù)動(dòng)畫
結(jié)果精靈閃爍10次以后,幀動(dòng)畫不執(zhí)行了。
2.原因
先了解一下CCSequence的創(chuàng)建和執(zhí)行原理
2.1.CCSequence的創(chuàng)建
創(chuàng)建CCSequence調(diào)用
-
//創(chuàng)建CCSequence
-
CCSequence* CCSequence::create(CCFiniteTimeAction *pAction1, ...)
內(nèi)部調(diào)用了createWithVariableList,從實(shí)現(xiàn)可以看出這是一個(gè)遞歸調(diào)用。
-
//獲取動(dòng)作列表,創(chuàng)建CCSequence
-
CCSequence* CCSequence::createWithVariableList(CCFiniteTimeAction *pAction1, va_list args)
-
{
-
CCFiniteTimeAction *pNow;//當(dāng)前動(dòng)作
-
CCFiniteTimeAction *pPrev = pAction1;//第一個(gè)動(dòng)作
-
bool bOneAction = true;//只有一個(gè)動(dòng)作的標(biāo)志位
-
-
while (pAction1)
-
{
-
pNow = va_arg(args, CCFiniteTimeAction*);//獲取當(dāng)前動(dòng)作
-
if (pNow)//如果存在
-
{
-
pPrev = createWithTwoActions(pPrev, pNow);//用前兩個(gè)動(dòng)作創(chuàng)建CCSequence并賦給第一個(gè)動(dòng)作
-
bOneAction = false;//置false
-
}
-
else//如果不存在
-
{
-
// If only one action is added to CCSequence, make up a CCSequence by adding a simplest finite time action.
-
if (bOneAction)//如果只有一個(gè)動(dòng)作
-
{
-
pPrev = createWithTwoActions(pPrev, ExtraAction::create());
-
}
-
break;//跳出循環(huán)
-
}
-
}
-
-
return ((CCSequence*)pPrev);//返回第一個(gè)動(dòng)作
-
}
假如有3個(gè)動(dòng)作要被串聯(lián),則先把第1個(gè)和第2個(gè)串聯(lián)一個(gè)CCSequence,再把這個(gè)CCSequence和第3個(gè)動(dòng)作串聯(lián)成最終的CCSequence,然后返回。從CCSequence的成員變量可以看到:
-
CCFiniteTimeAction *m_pActions[2];//表明只包含2個(gè)動(dòng)作對(duì)象指針
使用遞歸多少會(huì)降低程序的運(yùn)行效率,但是卻可以換來代碼的簡(jiǎn)潔性,同樣的CCSpawn也是這么實(shí)現(xiàn)的。
在createWithTwoActions中,調(diào)用了initWithTwoActions函數(shù),實(shí)現(xiàn)了把兩個(gè)動(dòng)作串成一個(gè)CCSequence,關(guān)鍵代碼如下:
-
float d = pActionOne->getDuration() + pActionTwo->getDuration();//獲取兩個(gè)動(dòng)作的duration
-
CCActionInterval::initWithDuration(d);//賦給新的CCSequence
-
-
m_pActions[0] = pActionOne;//同時(shí)把兩個(gè)動(dòng)作賦給m_pActions指針數(shù)組
-
pActionOne->retain();
-
-
m_pActions[1] = pActionTwo;
-
pActionTwo->retain();
2.2.duration
從示例可以看出,閃爍動(dòng)畫blink的duration是0.5s,那CCRepeatForever呢?1s?當(dāng)然不是,1s只是幀動(dòng)畫animate的幀間間隔,每個(gè)幀動(dòng)畫包含2幀,而CCRepeatForever的duration是0。因此,當(dāng)示例中的閃爍動(dòng)畫blink和重復(fù)動(dòng)畫repeat串聯(lián)成CCSequence sequence的時(shí)候,sequence的duration就變成0.5+0=0.5s,這很重要。
2.3.m_split
CCSequence中有這么一個(gè)成員變量
-
float m_split;//記錄了第一個(gè)動(dòng)畫時(shí)長(zhǎng)占總時(shí)長(zhǎng)的比例,也就是2個(gè)動(dòng)畫的時(shí)長(zhǎng)分界
當(dāng)執(zhí)行runAction的時(shí)候,CCSequence會(huì)調(diào)用
-
void CCSequence::startWithTarget(CCNode *pTarget)
-
{
-
CCActionInterval::startWithTarget(pTarget);
-
m_split = m_pActions[0]->getDuration() / m_fDuration;//獲取第一個(gè)動(dòng)畫占總時(shí)長(zhǎng)的比例
-
m_last = -1;
-
}
而這里由于blink占了0.5s,repeat占了0s,總時(shí)長(zhǎng)0.5s,所以m_split是0.5/0.5=1。blink占滿了整個(gè)CCSequence。這時(shí)候再來看CCSequence::update(float
dt)函數(shù)的執(zhí)行,就會(huì)恍然大悟了。
-
int found = 0;//當(dāng)前播放動(dòng)作索引
-
float new_t = 0.0f;//新播放進(jìn)度
-
-
if( t < m_split ) {//播放進(jìn)度<分界進(jìn)度
-
found = 0;//設(shè)置當(dāng)前播放的是第一個(gè)動(dòng)作
-
if( m_split != 0 )//如果第一個(gè)動(dòng)作時(shí)長(zhǎng)占比!=0
-
new_t = t / m_split;//計(jì)算出第一個(gè)動(dòng)作新的播放進(jìn)度
-
else
-
new_t = 1;//設(shè)置第一個(gè)已播放完畢
-
-
} else {//播放進(jìn)度>=分界進(jìn)度
-
found = 1;//設(shè)置當(dāng)前播放的是第二個(gè)動(dòng)作
-
if ( m_split == 1 )//如果第一個(gè)動(dòng)作時(shí)長(zhǎng)占比==1
-
new_t = 1;//設(shè)置第二個(gè)動(dòng)作已完成
-
else
-
new_t = (t-m_split) / (1 - m_split );//計(jì)算出第二個(gè)動(dòng)作新的播放進(jìn)度
-
}
3.注意
(1)CCSpawn也會(huì)有這個(gè)問題,所以CCSpawn也無法執(zhí)行加入其中的CCRepeatForever動(dòng)作。
(2)CCRepeatForever的反轉(zhuǎn)動(dòng)作也是無效了,一個(gè)不會(huì)停止的動(dòng)作從什么地方開始反轉(zhuǎn)?當(dāng)然你可以先把動(dòng)作反轉(zhuǎn)了再加入CCRepeatForever中,這是沒問題的。
4.解決方案
(1)對(duì)于同時(shí)動(dòng)作,不使用CCSpawn,采用分別執(zhí)行
-
close->runAction(blink);
-
close->runAction(repeat);
(2)對(duì)于連續(xù)動(dòng)作,不直接往CCSequence中加入CCRepeatForever,而是把CCRepeatForever放入瞬時(shí)動(dòng)作CCCallFunc中,再把CCCallFunc加入CCSequence中執(zhí)行。
-
close=CCSprite::create("CloseNormal.png");
-
CCBlink* blink=CCBlink::create(0.5f,10);
-
CCCallFunc* callFunc=CCCallFunc::create(this,callfunc_selector(TestScene::repeatFunc));//創(chuàng)建CCCallFunc對(duì)象
-
CCSequence* sequence=CCSequence::create(blink,callFunc,NULL);//把CCCallFunc對(duì)象加入CCSequence中
-
close->setPosition(ccp(240,160));
-
this->addChild(close);
-
close->runAction(sequence);
-
-
-
void TestScene::repeatFunc()
-
{
-
CCAnimation* animation=CCAnimation::create();
-
animation->addSpriteFrameWithFileName("CloseNormal.png");
-
animation->addSpriteFrameWithFileName("CloseSelected.png");
-
animation->setDelayPerUnit(1.0f);
-
CCAnimate* animate=CCAnimate::create(animation);
-
CCRepeatForever* repeat=CCRepeatForever::create(animate);
-
close->runAction(repeat);
-
}
(3)對(duì)于CCAnimation幀動(dòng)畫,可以設(shè)置循環(huán)屬性,而不使用CCRepeatForever。
5.總結(jié)
雖然CCRepeatForever也同樣繼承于CCActionInterval,理論上是延時(shí)動(dòng)作的子類,但是和一般的延時(shí)動(dòng)作又有很大的不同,所以平時(shí)在使用的時(shí)候必須很小心,不能當(dāng)成一般的CCActionInterval使用。而在cocos2d-x動(dòng)作的分類上是不是應(yīng)該把它從CCAction繼承出來會(huì)比較好一點(diǎn)?
|