我正在制作一个涉及敌人AI路径的小游戏。我想使用使用Cubic Bezier曲线生成的路径,但需要一个公式,使得enemys以恒定速度在任何曲线上移动。我所知道的是直线路径上的不断运动,并产生贝塞尔曲线,但我不知道如何使两者协同工作。
答案 0 :(得分:1)
这里的问题是你选择了非线性曲线,你想要线性速度。
有一些选项,例如将曲线近似为线性几何(如线段或圆弧),然后以线性速度而不是实际曲线穿过它们。将曲线展平以相对直接行走:
lines = []
x=curve.getX(0), y=curve.getY(0), nx, ny
step=..., interval=1/step, t=step
while(t<=1) {
nx = curve.getX(t)
ny = curve.getY(t)
lines.push( new line(x,y,nx,ny)
x = nx
y = ny
t += interval
}
完成 - 我们现在有了曲线,表示为线性近似,我们可以开始沿着它移动。如果step
足够小,则没有人会注意到。
或者,您可以构建一个查找表作为绘图程序的一部分,并记录该点处曲线的距离,这样您就可以通过二进制搜索其余部分以线性速度遍历曲线。你需要的“下一个地方”的曲线。比选项1更多的初始工作,但一旦开始移动肯定会更快。
function arcLength(t) {
// true fact: computing the arc length of a bezier curve is not
// a thing you want to end up implementing yourself. It's not hard,
// but getting to a point where you undestand *why* it's not hard is
// is certainly time consuming, and depending on how much your brain
// is unwilling to just take maths at face value, definitely hard.
// Use someone else's implementation, like this one:
// https://github.com/Pomax/bezierjs/blob/gh-pages/lib/bezier.js#L87
}
Curve.draw = function() {
if (!this.curveLUT) {
// form a LUT however you like. The following demonstration
// code uses something similar to the above flattening:
for(i=0;i<LUT.length;i++) {
t = i / (LUT.length-1)
x = curve.getX(t)
y = curve.getY(t)
LUT.push({x:x, y:y, dist: arclength(t)})
}
}
this.curveLUT.foreach(point -> point.draw())
}
然后当我们需要以某种特定的速度走弯道时:
speed = ...
currentPos = SomeLUTindex
if (currentPos < LUT.length) {
currentDist = LUT[currentPos].dist
nextDist = currentDist + speed
nextPos = binarySearch(LUT.slice(currentPos), "dist", nextDist)
}
使用:
binarySearch(List, property, target) {
midIdx = (int) List.length/2
mid = List[midIdx]
curr = mid[property]
if(curr === target) return midIdx
if(curr > target) return binarySearch(List.slice(0,midIdx), property, target)
if(curr < target) return midIdx + binarySearch(List.slice(midIdx), property, target)
}
这看起来可能很多,但二进制搜索会在ceil(log₂(n))
中解决,最糟糕的情况是,所以即使在10000点的查找表上,这也会找到最多14个步骤的下一个点。对函数的优化也将它展开到一个直的for
/ while
循环,而不是对列表进行切片但是检查它的特定间隔 - 两者都是更多的代码,但相对容易实现谷歌和维基百科的帮助很少。