引言
“距离”是游戏中判断互动的核心标准。 - 玩家是否进入了怪物的攻击范围? - 赛车离终点还有多远? - 摄像机是否穿墙了?
1. 距离公式
欧几里得距离 (Euclidean Distance)
这是最常用的直线距离。 \[ d = \sqrt{(x_2-x_1)^2 + (y_2-y_1)^2} \]
在代码中:
float dist = Vector3.Distance(a, b);
// 或者
float dist = (a - b).magnitude;曼哈顿距离 (Manhattan Distance)
在网格地图(如《火焰纹章》或《文明》)中,你不能斜着走,只能沿格子走。此时距离是 x 轴差值与 y 轴差值的和。 \[ d = |x_2-x_1| + |y_2-y_1| \]
性能优化:平方距离 (Squared Distance)
计算 sqrt (开平方) 是一个相对昂贵的操作。
如果你只是想比较距离(例如:谁离我更近?或者是否在攻击范围内?),你完全不需要开平方。
判断 dist < range 等价于判断
distSq < range * range。
float distSq = (target.position - player.position).sqrMagnitude;
float attackRangeSq = attackRange * attackRange;
if (distSq < attackRangeSq) {
// 在攻击范围内
}这在需要检测成百上千个物体时能显著提升性能。
2. 点与平面 (Point & Plane)
平面在游戏中通常用于墙壁、地面或触发区域。 一个平面由一个法线 (Normal) \(\vec{n}\) 和一个距离常数 \(d\) 定义。 方程:\(\vec{n} \cdot P + d = 0\)
有符号距离 (Signed Distance)
将任意点 \(P\) 代入平面方程,得到的结果不仅仅是 0。 \[ dist = \vec{n} \cdot P + d \]
- dist > 0: 点在平面的正面。
- dist = 0: 点在平面上。
- dist < 0: 点在平面的背面。
游戏应用
- 穿墙检测: 如果上一帧 dist > 0,这一帧 dist < 0,说明穿过了平面。
- 背面剔除: 渲染时,如果三角形法线指向屏幕内侧,就不渲染它。
3. 点与直线 (Point & Line)
如何计算点 \(P\) 到直线 \(AB\) 的最短距离? 这需要用到投影 (Projection)。
- 计算向量 \(\vec{AP} = P - A\)。
- 计算直线方向 \(\vec{AB} = B - A\) 并归一化为 \(\vec{u}\)。
- 计算投影长度 \(t = \vec{AP} \cdot \vec{u}\)。
- 最近点 \(C = A + \vec{u}
\times t\)。
- 注意:如果线段有限长,需要限制 \(t\) 在 \([0, length]\) 之间。
- 距离 = \(|P - C|\)。
这在赛车游戏中非常有用,用于计算车辆偏离赛道中心线的距离。