逻辑直线运动的曲线表现模拟2--基于控制点(基函数)的随机曲线

需求

设计师希望可以完全自定义地描述某些投射物的弹道曲线。该曲线需要支持跟踪和确定目标点。

实现

B样条

本曲线使用的基本理论是B样条,B样条是受多个控制点约束而绘制出的样条曲线。它相对于类似原理的贝赛尔曲线具有更好的局部性性质,用户调整其中部分控制点时曲线整体不会受到影响。

△ 表现上可以近似地理解为抛射物被控制点吸引地向正方向移动。

本实现中生成的B样条是一组曲线上的有序顶点。

B样条的数学推导相对复杂,本文档不会包含相关内容。但是可以参考下方链接:

https://www.qiujiawei.com/b-spline-1/

运动过程

每一帧都更新目标位置targetPos、逻辑直线运动总时间directFullTime、抛射物当前运动时间curLifeTime,计算过程中速度和加速度都参与计算。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
private void UpdateDirectInfo() {
fullDirectDis = Vector3.Distance(targetPos, startPos);

// 计算移动时间
directFullTime = 0;
float a = 0.5f * acceleration;
float b = moveSpeed;
float c = -fullDirectDis;
float check = b * b - 4 * a * c;
if (a != 0 && check >= 0) {
directFullTime = (-b + Mathf.Sqrt(check)) / (2 * a);
if (directFullTime < 0) {
directFullTime = (-b - Mathf.Sqrt(check)) / (2 * a);
}
} else {
directFullTime = fullDirectDis / moveSpeed;
}

if (directFullTime < minLifeTime) {
this.moveSpeed = fullDirectDis / minLifeTime;
directFullTime = minLifeTime;
}
}
  1. 每一帧上,直线上运动进度百分比为directRate = curLifeTime / directFullTime,因为曲线运动和直线运动抵达终点的时刻应相同,所以曲线上的运动进度百分比也应该等于directRate

  2. 若抵达最后顶点,说明抛射物已抵达终点,直接设置抛射物位置;若尚未抵达,则使用当前时间比例计算当帧的曲线位置。

    • if (directRate >= 1) {
          // 直接定位到最终位置
           RefreshTransform(this.targetPos);
      } else {
           Vector3 pos = bSpline.CreateBSpline(directRate);
           RefreshTransform(pos);
      }
      

曲线控制点的运行时定位方式

(咕)

跟踪情形

当抛射物目标点动态变化时,原有曲线将无法准确命中目标。因此在每一帧Tick时,需要更新曲线的最后一个顶点和曲线全长,以保证终点无误。

这会产生的问题是,抛射物运动轨迹的最后一部分将会是一条斜率可能发生突变的线段,但在绝大多数帧相关的情形中难以察觉。

为什么不实时生成新的曲线,然后重新对抛射物做当帧定位?

  1. 生成曲线的计算量不低,高频率的曲线生成会产生性能问题。
  2. 基于确定曲线的运动模拟不是一个增量过程,当目标点发生较大变化时,曲线也会剧烈变化,定位平滑效果较差的问题无法避免,这在复杂曲线上的表现更明显。

22.12.24更新

新做法,重新调整了样条生成方式,支持了直接计算b样条在某个直线比例上的曲线坐标Bspline.CreateCurvePointByDirectRate(float rate),以此去除了动态目标情况的无效计算消耗。

B样条优秀的局部采样性质使得曲线顶点的计算与控制点数量无关,相比于贝塞尔曲线,在大量曲线同时进行计算的情形下效率优化明显。