cocos2d-x 贝塞尔曲线之游戏应用

一.贝赛尔曲线简介
贝塞尔曲线是应用于二维图形应用程序的数学曲线。曲线的定义有四个点:起始点、终止点(也称锚点)以及两个相互分离的中间点。滑动两个中间点,贝塞尔曲线的形状会发生变化  

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
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值