Table of Contents

扫掠检测

中级 程序员

扫掠检测(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 表示,它包含关于命中的所有信息。

获取实体

因为 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))
    {
        // 处理成功命中
    }
}