高阶贝塞尔曲线

时间:2021-05-25 05:46:50

因为繁琐于写精灵的曲线运动,所以找相关解决的办法,最后找到了贝塞尔曲线。不过,只有cocos2d只支持3阶的贝塞尔动作怎么办,而且网上的算法也只有写3阶的。
于是,自己写了一个高阶的贝塞尔曲线,封装成CCActionInterval,调用的时候只要[sprite runAction:bezier]; 岂不爽哉!

第一步是找理论支持:
用到了下面这个公式:
高阶贝塞尔曲线阶贝塞尔曲线可如下推断。给定点P0、P1、…、Pn,其贝塞尔曲线即
高阶贝塞尔曲线高阶曲线为建构高阶曲线,便需要相应更多的中介点。对于三次曲线,可由线性贝塞尔曲线描述的中介点Q0、Q1、Q2,和由二次曲线描述的点R0、R1所建构:
高阶贝塞尔曲线   高阶贝塞尔曲线
三次贝塞尔曲线的结构   三次贝塞尔曲线演示动画,t in [0,1]
对于四次曲线,可由线性贝塞尔曲线描述的中介点Q0、Q1、Q2、Q3,由二次贝塞尔曲线描述的点R0、R1、R2,和由三次贝塞尔曲线描述的点S0、S1所建构:
高阶贝塞尔曲线   高阶贝塞尔曲线
四次贝塞尔曲线的结构   四次贝塞尔曲线演示动画,t in [0,1]


更详细的资料可以查看*——被赛尔曲线

然后是写算法:
typedef struct controlPos{
    float posX;
    float posY;
}controlPos;

//阶乘
static inline int factorial(int n){
    int result = 1;
    while (n) {
        result *=n;
        n--;
    }
    return result;
}

//杨辉三角
static inline int pascalTriangleRatio(int n,int i){
    int result = 0;
    result = factorial(n)/(factorial(i)*factorial(n-i));
    return result;
}


//在t点贝塞尔多项式的和
static inline controlPos bezieerat(controlPos *p,int n,float t){
    float px =0;
    float py = 0;
    int i=0;
    while (i<=n) {
        px += pascalTriangleRatio(n, i)*p.posX*powf(t, i)*powf(1-t, n-i);
        py += pascalTriangleRatio(n, i)*p.posY*powf(t, i)*powf(1-t, n-i);
        i++;
    }
    controlPos m;
    m.posX = px;
    m.posY = py;
    return m;
}


第三步的时候考虑到贝塞尔曲线在确定控制点的时候实在不太友好,于是写个这么个东西:

为了直观的定义曲线,所以写了个类名为TestBeszer的CCLayer,在这个层可以编辑曲线,
在项目里加载这个层,则可以直观的在地图上编辑出曲线,挺方便的是不是。
//        TestBeszer *testBezer = [[TestBeszer alloc] init];
//        [self addChild:testBezer];
我既可以画出爱心:
高阶贝塞尔曲线
也可以画出卫生巾:
高阶贝塞尔曲线
点击打印后就会打印出所有的坐标点了。


第四步封装:
我认为封装成CCActionInterval子类实在是太高明了,这让使用非常方便,测试过与原装的cocos2d动作都兼容。
封装的时候花了一些时间来弄明白cocos2d的动作编写机制。
类名是HCBezier,
调用方式如下,与原装动作可以组合使用。
     controlPos array[3]={
            {708,800},
            {590,346},
            {126,446}
        };
        id bezier = [HCBezier actionWithPoints:array pointNumber:3 Duration:4.f];
//      [sprite runAction:bezier];
        [sprite runAction:[CCRepeatForever actionWithAction:[CCSequence actions:bezier,[CCDelayTime actionWithDuration:3.f],[CCMoveBy actionWithDuration:1.f position:ccp(-200, 40)],[bezier reverse], nil]]];



最后,在HCBizer中有个不满意的地方
-(id) initWithPoints:(controlPos *)array pointNumber:(int)number Duration:(ccTime)t{
    if (self = [super initWithDuration:t]) {
        number_ = number;
        arrayOfControlPoints = [[NSMutableArray alloc] init];
        for (int i = 0; i<number_; i++) {
            CGPoint p = ccp(array.posX, array.posY);
            [arrayOfControlPoints addObject:[NSValue valueWithCGPoint:p]];
        }
    }
    return self;
}
我传入的是一个数组指针,我却没办法使用全局变量的方式取用这个数组,只能用nsarray转换,让人十分不爽,希望有人帮我解决这个问题。
//这里是让精灵移动,将t从0到1调用一遍是一个周期,这个周期会走完整个曲线,这个周期所经历的时间就是动作执行的时间。
-(void) update: (ccTime) t
{   
    controlPos _controlPos[number_];
    for (int i = 0; i<number_; i++) {
        _controlPos.posX = [[arrayOfControlPoints objectAtIndex:i] CGPointValue].x;
        _controlPos.posY = [[arrayOfControlPoints objectAtIndex:i] CGPointValue].y;
        
    }
    controlPos pos = bezieerat(_controlPos, number_-1, t);
    CGPoint p = ccp(pos.posX, pos.posY);
    [target_ setPosition:p];
   
}

高阶贝塞尔曲线高阶贝塞尔曲线.zip(7.26 KB, 下载次数: 62)

补充内容 (2012-9-11 16:57):
更新了附件在三楼

HAH3]R%9QIT~85~B)_X52GI.jpg(40.46 KB, 下载次数: 0)

高阶贝塞尔曲线

QQ20120906-1.png(31.7 KB, 下载次数: 0)

高阶贝塞尔曲线

来自:http://bbs.tairan.com/thread-3536-1-1.html