求解匀速贝塞尔曲线运动

写个捕鱼,想写个贝塞尔曲线运动的场景,发现速度不是匀速的,不知道怎么样才能匀速贝塞尔曲线运动啊,有没有大神指教一下

这个图中, 鱼的路径由几条贝塞尔曲线合成?

不知道你啥意思,这个就是标准的3阶贝塞尔曲线啊,官网API有

官方API有的

匀速就要自己拿公式写

試試TweenMax,印象中是均速的

您好,我也碰到了这问题,请问大哥你解决了吗

没解决

预处理贝塞尔曲线。将贝塞尔曲线切割成N段,由时间乘以速度得出当前所处线段,根据切割的线段做线性插值运动。
N越大运动轨迹越平滑。
本质上就是分割成n小段的直线,然后直线路径做匀速运动。

1赞

原理太复杂,我也讲不清楚, 贴代码就好。

以下代码:


var BezierImpl = function (p0, p1, p2) {
var ax = p0.x - 2 * p1.x + p2.x;
var ay = p0.y - 2 * p1.y + p2.y;
var bx = 2 * (p1.x - p0.x);
var by = 2 * (p1.y - p0.y);

var A = 4 * (ax * ax + ay * ay);
var B = 4 * (ax * bx + ay * by);
var C = bx * bx + by * by;

var t0 = Math.sqrt(C);
var t1 = 8 * Math.pow(A, 1.5);

var m0 = (B * B - 4 * A * C) / t1;
var m1 = 2 * Math.sqrt(A);
var m2 = m1 / t1;
var ttt = (B + m1 * t0);
var m3 = m0 * Math.log(ttt <= 0 ? 0.0000001 : ttt) - B * m2 * t0;

var f0 = A + B;
var f1 = A + f0;
var temp1 = C + f0;
var f2 = Math.sqrt(temp1 < 0 ? 0 : temp1);
temp1 = f1 + m1 * f2;
var f3 = Math.log(temp1 <= 0 ? 0.0000001 : temp1);

this.mLength = m3 - m0 * f3 + m2 * f1 * f2;
this.A = A;
this.B = B;
this.C = C;
this.m0 = m0;
this.m1 = m1;
this.m2 = m2;
this.m3 = m3;
this.p0 = p0;
this.p1 = p1;
this.p2 = p2;

};

BezierImpl.prototype.getLength = function () {
return this.mLength;
};
BezierImpl.prototype.getPoint = function (t) {
var ll = this.m3 - t * this.mLength;

for (var i = 0; i < 7; ++i) {

    var f0 = this.A * t;
    var f1 = this.B + f0;
    var f2 = f1 + f0;
    var temp1 = this.C + t * f1;
    var f3 = Math.sqrt(temp1 < 0 ? 0 : temp1);
    temp1 = f2 + this.m1 * f3;
    var f4 = Math.log(temp1 <= 0 ? 0.0000001 : temp1);
    var f = (ll - this.m0 * f4) / f3 + this.m2 * f2;
    t -= f;
    if (Math.abs(f) < 0.01) {
        break;
    }
}

var c = t * t;
var b = t + t;
var a = 1 - b + c;
b -= c + c;


return {x: (a * this.p0.x + b * this.p1.x + c * this.p2.x), y: (a * this.p0.y + b * this.p1.y + c * this.p2.y)};

};

var Bezier = function (pointCount, pArray) {

if (pointCount < 3) {
    throw 1;
}

var mIndex = 0;
var p0 = pArray[mIndex++];

var mLength = 0;

var mMap = [];

for (var i = 3; i < pointCount; ++i) {

    var p1 = {x: (pArray[mIndex].x + pArray[mIndex + 1].x) / 2, y: (pArray[mIndex].y + pArray[mIndex + 1].y) / 2};

    var bezierImpl = new BezierImpl(p0, pArray[mIndex], p1);

    mMap.push({first: mLength, second: bezierImpl});
    mLength += bezierImpl.getLength();

    p0 = p1;
    mIndex++;
}

var bezierImpl = new BezierImpl(p0, pArray[mIndex], pArray[mIndex + 1]);
mMap.push({first: mLength, second: bezierImpl});
mLength += bezierImpl.getLength();
mMap.sort(this.sortCmd);
this.mMap = mMap;
this.mLength = mLength;

};

Bezier.prototype.sortCmd = function (a, b) {

return a.first - b.first;

};
Bezier.prototype.getLength = function () {

return this.mLength;

};

Bezier.prototype.getPoint = function (t) {

t *= this.mLength;

var it = this.mMap[Math.max(0, this.upperBound(t) - 1)];

t = (t - it.first) / it.second.getLength();
return it.second.getPoint(t);

};

Bezier.prototype.upperBound = function (findKey) {

var index;
for (index = 0; index < this.mMap.length; ++index) {
    if (this.mMap[index].first > findKey) {
        break;
    }
}
return index;

};

var FishBezierBy = cc.DelayTime.extend({

_startPosition: null,
_endPosition: null,
_controlPoint1: null,
_controlPoint2: null,
_bezier: null,

ctor: function (speed, startPosition, endPosition, controlPoint1, controlPoint2) {

    this._startPosition = startPosition;
    this._endPosition = endPosition;
    this._controlPoint1 = controlPoint1;
    this._controlPoint2 = controlPoint2;

    this._bezier = new Bezier(4, [startPosition, controlPoint1, controlPoint2, endPosition]);


    this._super(this.getLength() / speed);
},


getLength: function () {
    return this._bezier.getLength();
},

update: function (dt) {

    if (this.getTarget()) {


        this.getTarget().setPosition(this._bezier.getPoint(dt));
    }

}

});


使用方式
var action = new FishBezierBy(speed, startPosition, endPosition, controlPoint1, controlPoint2);
action = cc.sequence(cc.place(startPosition), action);
fish.runAction(action);

只需要兩個算法一個類
類Bezier
算法一:設位於三次bezier曲線百分比處為t (0-1之間), 函數t2anchor(t) 返回t處座標
算法二:已知曲線上的點,求位於這一點的切線方程,並取其中一個方向

那麼這個創建這個動畫動畫過程為(這個運動只和時間有關和fps無關)
1建立對象v = {t:0}
2建立緩衝運動 createTween({t:1}, 運動事件,每次更新調用update);
3在update方法每次調用算法一求的:fish:的位置,然後調用算法二求:fish:的方向,更新到節點。
完事(理論上)

1赞

ps:三次被塞爾的切線算法我沒寫過。猜測不會太麻煩。
遇到三次被塞爾最麻煩的問題是求被塞爾曲線長度,基本除了拆分線段(類似微積分)以外基本無解
我基本上能用方程解決的問題不會考慮用循環解決。

1赞

這種簡單直接。常規做法
缺點是難以建立時間t和目標位置的關係。
在不同fps下運動速度也會不同。

1赞

动态去计算挺浪费cpu的,帧率的问题有线性插值计算没有任何问题。

时间和目标位置可以用二分查找也可以直接建立索引,查找很快

都进入胡同了。

  1. 想到等分当然是好,网上有办法,没验证过效率。
    2.为什么不能通过计算每两点距离,折算时间插值来做移动呢

怎么说?

很简单,匀速,是我们没有用对方法,如图所示,其实我们要传的并不是起点,中点和终点,而是轨道上的任意两个偏移点p1,p2,和终点,如果是多段,因为起点就是当前的位置,你也不需要考虑传第二段的起点,因为就是上一段的终点,这样,只要你的曲线是对称的,就能匀速运动了,亲测可用

你对称的话,那么基本上都是匀速的

匀速贝塞尔曲线路径在线绘制工具