一.貝賽爾曲線簡介
貝塞爾曲線是應用于二維圖形應用程序的數學曲線。曲線的定義有四個點:起始點、終止點(也稱錨點)以及兩個相互分離的中間點。滑動兩個中間點,貝塞爾曲線的形狀會發生變化 
p0起點,p3是終點,p1,p2是控制點
http://en.wikipedia.org/wiki/B%C3%A9zier_curve 二.游戲應用
我們可能需要在游戲中模擬導彈或箭的移動軌跡,用才cocos2d-x下的bezier可以輕松的模擬出來
cocos2d-x下為我們提供了兩個action CCBezierBy和CCBezierTo,使用也很簡單,只需要填充結構體:
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);
我們只需要提供兩個控制點和一個終點位置就可以了,這里要注意的是
CCBezier這個action是以當前位置為起始點的,兩個控制點和終點都是相對于起始點的偏移值如:tr0.endPosition = ccp(280,240); 是相對于起始點的偏移三. 游戲實例
我們來模擬一個射箭游戲,
屏幕中心點是一個人,點擊屏幕任何位置,會朝這個點擊位置射一支箭,箭到達指定點后抖動一會(播放一個抖動動畫),然后消失

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

第一步:存貯箭的飛行動畫和抖動動畫到CCAnimationCatch中,后面播放動畫時通過名字來獲得動畫
// 存貯箭的飛行動畫,箭有多少個方向,就存貯多少個動畫
for (
int i = 0; i < mDirections; ++i)
{
// 箭飛行動畫
CCArray* spriteFramesArray = CCArray::create(flyFrames);
// 從圖片中每一幀的位置來生成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);
}
//以鍵值形式存貯動畫到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();
}
// 同理存貯箭的抖動動畫

.
第二步:獲得射箭目標點,用貝塞爾曲線模擬箭的飛行軌跡
首先,要求出箭的射擊方向,從CCAnimationCatche中取出對應的飛行動畫,我們有16個方向的動畫,下面是寫的一個簡單的求射箭角度和方向的函數:
#define EPSION 0.0001f
#define IS_EQUAL(val1, val2) (fabs((val1) - (val2)) <= EPSION)
const int mDirections = 16;
/** 更新方向,傳入起始點和終止點,利用actan來獲得射箭的弧度,然后轉換為角度
*/
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
{
// 弧度轉角度
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;
}
有了角度和動畫就好辦了,我們已經知道了目標點,哈哈,可以讓箭一邊播放飛行動畫一邊沿著貝塞爾曲線移動就OK了
// 播放箭飛行動作
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;
// 沿著貝塞爾曲線移動
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);
我們為action序列添加了回調函數
ArrowDirection::arrowFlyOverCallBack 箭飛行完畢后進入下一階段
第三步:箭到達目標點,播放抖動動畫
在上一階段的回調函數中先停止所有動畫
sprite->stopAllActions();
然后播放抖動動畫,抖動動畫再加一個回調函數
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);
第四步:播放完畢,清除箭
在上一階段回調中刪除自己
removeFromParentAndCleanup(true);
-------------------------------------------------------------------------------
調試幫助:
1.光看是不夠的,要看箭的飛行軌跡,還是要畫出來,在CCNode的派生類中重載draw()函數,在里面畫貝塞爾曲線
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);
// 畫控制點
ccDrawLine(mBezierStartPoint, control1);
ccDrawLine(control2, end);
// 畫貝塞爾曲線
ccDrawCubicBezier(mBezierStartPoint, mBezierConfig.controlPoint_1, mBezierConfig.controlPoint_2, mBezierConfig.endPosition, 100);
}
}
更多畫法參考cocos2d-x粒子DrawPrimitivesTest
posted on 2012-08-24 18:04
風輕云淡 閱讀(15372)
評論(6) 編輯 收藏 引用 所屬分類:
cocos2d