0%

圆弧路径

(3D小游戏相关) 在update中计算出沿着圆移动的路径

问题:从点A移动到点B,要求移动的路径为圆弧形式

思路:做一个圆,使AB都处于这个圆上,沿着圆移动即可走出圆弧路径。AB相连做直线,取直线AB的垂线C,所需圆的圆心位于直线C上。然后根据具体需求,取出所需要的圆即可

举例:已知AB坐标,获取A到B的圆弧路径。要求AB路线的弧度所对应的角度为45度

注:以下算法仅针对游戏中的具体需求,提出一种思路和解,算法步骤并不完美,可根据实际问题优化

Laya + TypeScript 代码实现:

已知点A点B坐标和圆弧路径角度

1
2
3
const A = new Laya.Vector3(0, 0, 0)
const B = new Laya.Vector3(9, 9, 9)
const Angle = 45

计算出AB所走圆的圆心与半径

注意:
由于AB点处于三维空间,AB两点无法确定一个平面,而计算出路径是在平面之中,所以需要第3个点,或着一个方向,来帮助确定路径所在的平面。具体确定平面的方法,根据具体需求来

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
let center = null
let radius = null

// 点A、AB中点、圆心所成直角三角形的b边长度
let b = Laya.Vector3.distance(A, B) / 2

// 点A、AB中点、圆心所成直角三角形的c边长度
let c = b / Math.sin(Angle)

// 点A、AB中点、圆心所成直角三角形的a边长度
let a = Math.sqrt(c * c - b * b)

// 计算点A点B的中点C
let C = new Laya.Vector3(
(A.x + B.x) / 2,
(A.y + B.y) / 2,
(A.z + B.z) / 2
)

/**
* 注意,这里需要引入第3个点,或者一个方向来帮助确定路径所在的平面。
* 假设A向B的运动是一个简单的从原点向上跃起的动作,路径位于A点B点Y轴所形成的平面中,
* 则可引入一个向上的方向向量(0, 1, 0)
*/

// 引入一个沿着Y轴向上的方向向量
let up = new Laya.Vector3(0, 1, 0)

// 得到圆心位置和半径
center = new Laya.Vector3
Laya.Vector3.scale(up, -a, up) // 因为求向上跃起的路径,则圆心应该在AB下方,所以取-a
Laya.Vector3.add(C, up, center)
radius = c

取得路径圆的圆心和半径之后,可以开始计算圆的切线方向。在每一帧移动中,使移动方向为切线方向,即可走出圆弧路径。

以下代码计算圆的切线方向

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
let cur = sp3.transform.position // 当前位置

// 计算AB的方向向量 ab
let dir_a_b = new Laya.Vector3
Laya.Vector3.subtract(B, A, dir_a_b)
Laya.Vector3.normalize(dir_a_b, dir_a_b)

// 计算当前位置到圆心的方向向量 cc
let dir_cur_c = new Laya.Vector3
Laya.Vector3.subtract(c, cur, dir_cur_c)
Laya.Vector3.normalize(dir_cur_c, dir_cur_c)

// 求出ab方向向量和圆心方向向量cc的垂向量v1
let dir_v1 = new Laya.Vector3
Laya.Vector3.cross(dir_a_b, dir_cur_c, dir_v1)
Laya.Vector3.normalize(dir_v1, dir_v1)

// 求出圆心方向向量cc和垂向量v1的垂向量v2,v2即使圆切线的方向向量
let dir_v2 = new Laya.Vector3
Laya.Vector3.cross(dir_cur_c, dir_v1, dir_v2)
Laya.Vector3.normalize(dir_v2, dir_v2)

每一帧的移动都沿着圆的切线方向进行

1
2
3
4
let dis = speed * Laya.timer.delta // 每一帧要移动的距离
let tag = new Laya.Vector3 // 每一帧移动到的目标位置
Laya.Vector3.scale(dir_v2, dis, tag)
Laya.Vector3.add(cur, tag, tag)

取得每一帧的目标位置后,还需要对目标位置做一个修正。防止跳帧或移动距离过大的情况下,走出的路径偏离圆弧

1
2
3
4
5
6
7
8
9
10
11
// 目标点与圆心距离大与半径了
if (Laya.Vector3.distance(tag, center) > radius) {
// 重新计算tag的位置
let dir = new Laya.Vector3
Laya.Vector3.subtract(tag, center, dir)
Laya.Vector3.normalize(dir, dir)
Laya.Vector3.scale(dir, radius, dir)
Laya.Vector3.add(center, dir, tag)
// 这里只是简单的将圆心与tag相连,然后得出连线上位于圆上的点
// 可以保证每次移动都在圆上,但不能保证每次移动的距离都是dis
}

以上算法思路在游戏中实现了:

  1. 人物荡秋千路径
  2. 向前跳跃落地路径
  3. 45度圆角转弯寻路