一.貝賽爾曲線簡(jiǎn)介
貝塞爾曲線是應(yīng)用于二維圖形應(yīng)用程序的數(shù)學(xué)曲線。曲線的定義有四個(gè)點(diǎn):起始點(diǎn)、終止點(diǎn)(也稱錨點(diǎn))以及兩個(gè)相互分離的中間點(diǎn)。滑動(dòng)兩個(gè)中間點(diǎn),貝塞爾曲線的形狀會(huì)發(fā)生變化 
p0起點(diǎn),p3是終點(diǎn),p1,p2是控制點(diǎn)
http://en.wikipedia.org/wiki/B%C3%A9zier_curve 二.游戲應(yīng)用
我們可能需要在游戲中模擬導(dǎo)彈或箭的移動(dòng)軌跡,用才cocos2d-x下的bezier可以輕松的模擬出來(lái)
cocos2d-x下為我們提供了兩個(gè)action CCBezierBy和CCBezierTo,使用也很簡(jiǎn)單,只需要填充結(jié)構(gòu)體:
ccBezierConfig tr0;
tr0.endPosition=ccp(280, 240);
tr0.controlPoint_1=ccp(40, 400);
tr0.controlPoint_2=ccp(280, 80);
CCActionInterval* bezierForward = CCBezierBy::create(1.f, tr0);
我們只需要提供兩個(gè)控制點(diǎn)和一個(gè)終點(diǎn)位置就可以了,這里要注意的是
CCBezier這個(gè)action是以當(dāng)前位置為起始點(diǎn)的,兩個(gè)控制點(diǎn)和終點(diǎn)都是相對(duì)于起始點(diǎn)的偏移值如:tr0.endPosition = ccp(280,240); 是相對(duì)于起始點(diǎn)的偏移三. 游戲?qū)嵗?br />我們來(lái)模擬一個(gè)射箭游戲,
屏幕中心點(diǎn)是一個(gè)人,點(diǎn)擊屏幕任何位置,會(huì)朝這個(gè)點(diǎn)擊位置射一支箭,箭到達(dá)指定點(diǎn)后抖動(dòng)一會(huì)(播放一個(gè)抖動(dòng)動(dòng)畫(huà)),然后消失

p0是箭的發(fā)射位置,p3是箭的目標(biāo)點(diǎn),p1,p2是控制飛行軌跡的控制點(diǎn),p1p0p3組成了箭的角度,假設(shè)我們有十六個(gè)方向的箭,即360.f/16= 22.5度一個(gè)方向

第一步:存貯箭的飛行動(dòng)畫(huà)和抖動(dòng)動(dòng)畫(huà)到CCAnimationCatch中,后面播放動(dòng)畫(huà)時(shí)通過(guò)名字來(lái)獲得動(dòng)畫(huà)
// 存貯箭的飛行動(dòng)畫(huà),箭有多少個(gè)方向,就存貯多少個(gè)動(dòng)畫(huà)
for (
int i = 0; i < mDirections; ++i)
{
// 箭飛行動(dòng)畫(huà)
CCArray* spriteFramesArray = CCArray::create(flyFrames);
// 從圖片中每一幀的位置來(lái)生成CCSpriteFrame
for (
int j = 0; j < flyFrames; ++j )
{
CCSpriteFrame* frame = CCSpriteFrame::create(pTexture,
CCRectMake(j * frame_width, i * frame_height, frame_width, frame_height));
spriteFramesArray->addObject(frame);
}
//以鍵值形式存貯動(dòng)畫(huà)到CCAnimationCatch中
float frameTime = 1.f / (mSpeed * flyFrames);
CCAnimation* animation = CCAnimation::create(spriteFramesArray, frameTime);
String name = getArrowAnimateName(i);
CCAnimationCache::sharedAnimationCache()->addAnimation(animation, name.c_str());
spriteFramesArray->removeAllObjects();
spriteFramesArray->release();
}
// 同理存貯箭的抖動(dòng)動(dòng)畫(huà)

.
第二步:獲得射箭目標(biāo)點(diǎn),用貝塞爾曲線模擬箭的飛行軌跡
首先,要求出箭的射擊方向,從CCAnimationCatche中取出對(duì)應(yīng)的飛行動(dòng)畫(huà),我們有16個(gè)方向的動(dòng)畫(huà),下面是寫(xiě)的一個(gè)簡(jiǎn)單的求射箭角度和方向的函數(shù):
#define EPSION 0.0001f
#define IS_EQUAL(val1, val2) (fabs((val1) - (val2)) <= EPSION)
const int mDirections = 16;
/** 更新方向,傳入起始點(diǎn)和終止點(diǎn),利用actan來(lái)獲得射箭的弧度,然后轉(zhuǎn)換為角度
*/
int ArrowDirection::updateDirection(const cocos2d::CCPoint& ptRole, const cocos2d::CCPoint& ptTarget)
{
CCPoint sub = ccpSub(ptTarget, ptRole);
if (IS_EQUAL(sub.x, 0.f) && IS_EQUAL(sub.y, 0.f))
return -1;
if (IS_EQUAL(sub.y, 0.f) && sub.x > 0)
{
mDegree = 90.f;
}
else if (IS_EQUAL(sub.y, 0.f) && sub.x < 0)
{
mDegree = 180.f;
}
else
{
// 弧度轉(zhuǎn)角度
float radians = atanf(sub.x/sub.y);
mDegree = CC_RADIANS_TO_DEGREES(radians);
if (sub.x >= 0 && sub.y >= 0 ) // 第一象限
{
}
else if (sub.x >= 0 && sub.y <= 0) // 第二象限
{
mDegree += 180.f;
}
else if (sub.x <= 0 && sub.y <= 0) // 第三象限
{
mDegree += 180.f;
}
else // 第四象限
{
mDegree += 360.f;
}
}
if (mDegree < 0.f)
mDegree = 0.f;
if (mDegree > 360.f)
mDegree = 0.f;
float single = (float)360 / 16;
for (int i = 0; i < mDirections; ++i)
{
if (mDegree >= i * single && mDegree <= (i+1) * single)
return mDirections;
}
return mDirections - 1;
}
有了角度和動(dòng)畫(huà)就好辦了,我們已經(jīng)知道了目標(biāo)點(diǎn),哈哈,可以讓箭一邊播放飛行動(dòng)畫(huà)一邊沿著貝塞爾曲線移動(dòng)就OK了
// 播放箭飛行動(dòng)作
String name = getArrowAnimateName(dir);
cocos2d::CCAnimation* animation = CCAnimationCache::sharedAnimationCache()->animationByName(name.c_str());
CCAnimate* animate = CCAnimate::actionWithAnimation(animation);
mSprite->runAction(animate);
// 填充bezier
ccBezierConfig cfg;
cfg.controlPoint_1 = ccp(0, control_height);
cfg.controlPoint_2 = ccp(ptRelativeTarget.x, ptRelativeTarget.y + control_height);
cfg.endPosition = ptRelativeTarget;
// 沿著貝塞爾曲線移動(dòng)
CCActionInterval* bezierForward = CCBezierBy::create(2.f, cfg);
CCActionInterval* seq = (CCActionInterval*)CCSequence::create(bezierForward,
CCCallFuncND::create(this, callfuncND_selector(ArrowDirection::arrowFlyOverCallBack), this),
NULL);
mSprite->runAction(seq);
我們?yōu)閍ction序列添加了回調(diào)函數(shù)
ArrowDirection::arrowFlyOverCallBack 箭飛行完畢后進(jìn)入下一階段
第三步:箭到達(dá)目標(biāo)點(diǎn),播放抖動(dòng)動(dòng)畫(huà)
在上一階段的回調(diào)函數(shù)中先停止所有動(dòng)畫(huà)
sprite->stopAllActions();
然后播放抖動(dòng)動(dòng)畫(huà),抖動(dòng)動(dòng)畫(huà)再加一個(gè)回調(diào)函數(shù)
CCAnimate* animate = CCAnimate::actionWithAnimation(animation);
sprite->runAction(CCRepeatForever::create(animate));
CCActionInterval* delay = CCDelayTime::create(pArrowDir->getArrowShakeTime());
CCActionInterval* seq = (CCActionInterval*)CCSequence::create(delay,
CCCallFuncND::create(pArrowDir, callfuncND_selector(ArrowDirection::arrowDisappearedCallBack), pArrowDir),
NULL);
sprite->runAction(seq);
第四步:播放完畢,清除箭
在上一階段回調(diào)中刪除自己
removeFromParentAndCleanup(true);
-------------------------------------------------------------------------------
調(diào)試幫助:
1.光看是不夠的,要看箭的飛行軌跡,還是要畫(huà)出來(lái),在CCNode的派生類中重載draw()函數(shù),在里面畫(huà)貝塞爾曲線
void ArrowDirection::draw()
{
if (mDrawBezier)
{
CCPoint control1 = ccpAdd(mBezierStartPoint, mBezierConfig.controlPoint_1);
CCPoint control2 = ccpAdd(mBezierStartPoint, mBezierConfig.controlPoint_2);
CCPoint end = ccpAdd(mBezierStartPoint, mBezierConfig.endPosition);
// 畫(huà)控制點(diǎn)
ccDrawLine(mBezierStartPoint, control1);
ccDrawLine(control2, end);
// 畫(huà)貝塞爾曲線
ccDrawCubicBezier(mBezierStartPoint, mBezierConfig.controlPoint_1, mBezierConfig.controlPoint_2, mBezierConfig.endPosition, 100);
}
}
更多畫(huà)法參考cocos2d-x粒子DrawPrimitivesTest
posted on 2012-08-24 18:04
風(fēng)輕云淡 閱讀(15373)
評(píng)論(6) 編輯 收藏 引用 所屬分類:
cocos2d