这个图中, 鱼的路径由几条贝塞尔曲线合成?
不知道你啥意思,这个就是标准的3阶贝塞尔曲线啊,官网API有
官方API有的
匀速就要自己拿公式写
試試TweenMax,印象中是均速的
您好,我也碰到了这问题,请问大哥你解决了吗
没解决
预处理贝塞尔曲线。将贝塞尔曲线切割成N段,由时间乘以速度得出当前所处线段,根据切割的线段做线性插值运动。
N越大运动轨迹越平滑。
本质上就是分割成n小段的直线,然后直线路径做匀速运动。
原理太复杂,我也讲不清楚, 贴代码就好。
以下代码:
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方法每次調用算法一求的的位置,然後調用算法二求的方向,更新到節點。
完事(理論上)
ps:三次被塞爾的切線算法我沒寫過。猜測不會太麻煩。
遇到三次被塞爾最麻煩的問題是求被塞爾曲線長度,基本除了拆分線段(類似微積分)以外基本無解
我基本上能用方程解決的問題不會考慮用循環解決。
這種簡單直接。常規做法
缺點是難以建立時間t和目標位置的關係。
在不同fps下運動速度也會不同。
动态去计算挺浪费cpu的,帧率的问题有线性插值计算没有任何问题。
时间和目标位置可以用二分查找也可以直接建立索引,查找很快
都进入胡同了。
- 想到等分当然是好,网上有办法,没验证过效率。
2.为什么不能通过计算每两点距离,折算时间插值来做移动呢
怎么说?
很简单,匀速,是我们没有用对方法,如图所示,其实我们要传的并不是起点,中点和终点,而是轨道上的任意两个偏移点p1,p2,和终点,如果是多段,因为起点就是当前的位置,你也不需要考虑传第二段的起点,因为就是上一段的终点,这样,只要你的曲线是对称的,就能匀速运动了,亲测可用
你对称的话,那么基本上都是匀速的
感谢大佬