扫掠检测
中级 程序员
扫掠检测(Sweepcast) 与射线检测类似,区别在于它使用形状而不是射线。它沿某个方向投射一个形状,并返回它碰撞到的内容。更通俗地说,就像发射一个看不见的物体,看看它击中了什么。
扫掠检测查询
要执行扫掠检测,请使用 Simulation.SweepCast。如果成功命中某些内容,该方法返回 true。在这种情况下,关于命中的所有信息都将包含在 HitInfo 中。
形状
扫掠检测需要一个形状,它可以是以下之一:Box、Capsule、ConvexHull、Sphere、Triangle。
姿态
姿态(pose) 描述了在扫掠检测开始时形状在世界中的位置方式。
创建 RigidPose 的新实例需要两个参数:位置和旋转。
如果你想使用场景中某个实体的位置和旋转,可以使用 Transform.GetWorldTransform。
Entity.Transform.GetWorldTransformation(out var position, out var rotation, out _);
var pose = new RigidPose(position, rotation);
速度
速度(velocity) 描述了形状在扫掠检测中将要跟随的路径。
创建 BodyVelocity 的新实例需要两个参数:线性速度(用于改变位置的速度)和角速度(用于改变旋转的速度)。
最大距离
最大距离控制扫掠检测的长度。然而,实际最大距离还受到线性速度向量大小的影响。
实际最大距离可以通过将 maxDistance 乘以 velocity.Linear 的长度来计算。
var maxDistance = 16f;
var velocity = new BodyVelocity(2f * Vector2.UnitY, Vector2.Zero);
var actualMaxDistance = maxDistance * velocity.Linear.Length();
Note
实际最大距离在大多数情况下都不需要担心。当使用 WorldMatrix.Forward 或类似值作为线性速度时,向量的长度将始终等于 1,这意味着 maxDistance 就是实际的最大距离。
穿透式扫掠检测查询
这种查询与普通查询的区别在于,当形状到达某个对象时,它不会停止,而是继续前进,直到达到最大长度。
要执行穿透式扫掠检测,请使用 Simulation.SweepCastPenetrating。
public void Shoot()
{
var hits = new List<HitInfo>();
simulation.SweepCastPenetrating(shape, pose, velocity, hits)
// 遍历所有结果
foreach (var hitInfo in hits)
{
// 处理成功命中
}
}
Note
命中结果的返回顺序不做任何保证。如果你想让它们按距离排序,必须自己对结果进行排序。
穿透式扫掠检测查询(使用 stackalloc)
在重复执行穿透式扫掠检测时,它必须不断为结果在堆上分配内存,这会给垃圾回收器(GC)带来更大压力(意味着更多的内存占用)。更优的解决方案是使用 stackalloc。
public void Shoot()
{
// 分配一个最多可容纳 16 个元素的缓冲区
Span<HitInfoStack> buffer = stackalloc HitInfoStack[16];
// 遍历所有结果
foreach (var hitInfo in simulation.SweepCastPenetrating(shape, pose, velocity, maxDistance, buffer))
{
// 处理成功命中
}
}
Note
span 能容纳的元素数量是有限的。如果命中数量超过缓冲区大小,则只会返回最近的那些命中。
处理命中
每个命中都由 HitInfo 表示,它包含关于命中的所有信息。
Point— 碰撞体在世界中被命中的位置。Normal— 被命中表面的法向量。Distance— 当Direction为单位长度时,原点与Point之间的距离。当它不是单位长度时,你可以预期actualDistance = Direction.Length * DistanceCollidable— 被命中的可碰撞体组件。ChildIndex— 如果是复合碰撞体,则为可碰撞体的碰撞体中所用形状的索引。
获取实体
因为 Collidable 是一个组件,所以你可以从它获取实体。
var entity = hit.Collidable.Entity;
获取与命中的距离
与命中的距离可以使用射线原点和 Point 通过 Vector3.Distance 计算得出。
var actualDistance = Vector3.Distance(origin, hit.Point);
使用碰撞层遮罩
每个查询都有一个可选参数,用于指定应在哪些碰撞层上执行该查询。
有关如何使用碰撞层遮罩的更多信息,请访问物理查询主页。
示例
以下是游戏中使用扫掠检测的几个示例。
粗射线检测
该方法将扫掠一个球体,这类似于具有一定粗细的射线检测。扫掠检测将在实体所面对的方向上执行。
public void Shoot()
{
var simulation = Entity.GetSimulation();
// 半径为 1 的球体形状
var shape = new Sphere(1f);
// 形状的起始姿态(起始位置和起始旋转)
// 我们可以忽略旋转,因为球体不受它影响
var pose = new RigidPose(Entity.Transform.WorldMatrix.TranslationVector, Quaternion.Identity);
// 形状的速度
// 在本例中,它将沿实体的前方方向前进
var velocity = new BodyVelocity(Entity.Transform.WorldMatrix.Forward, Vector3.Zero);
// 物体将要行进的最大距离或"时间量"
var maxDistance = 16f;
if (simulation.SweepCast(shape, pose, velocity, maxDistance, out HitInfo result))
{
// 处理成功命中
}
}
带穿透地扫掠盒体
该方法将在实体所面对的方向上使用盒体执行 SweepCastPenetrating。
public void SweepcastBox()
{
var simulation = Entity.GetSimulation();
Entity.Transform.GetWorldTransformation(out var position, out var rotation, out _);
var pose = new RigidPose(position, rotation);
var velocity = new BodyVelocity(Entity.Transform.WorldMatrix.Forward, Vector3.Zero);
// 分配一个最大大小为 16 的缓冲区
Span<HitInfo> buffer = stackalloc HitInfoSpan[16];
foreach (var hitInfo in simulation.SweepCastPenetrating(new Box(1f, 1f, 1f), pose, velocity, 16f, buffer))
{
// 处理成功命中
}
}