我不明白 RaycastCommand

I don't understand RaycastCommand

文档 (https://docs.unity3d.com/2019.2/Documentation/ScriptReference/RaycastCommand.html) 说:

If maxHits is larger than the actual number of results for the command the result buffer will contain some invalid results which did not hit anything. The first invalid result is identified by the collider being null. The second and later invalid results are not written to by the raycast command so their colliders are not guaranteed to be null. When iterating over the results the loop should stop when the first invalid result is found.

苏,有

new RaycastCommand(from, direction, 4);
  1. 我如何区分没有命中和结果无效?两种情况都有 hit.collider == null.
  2. 如果与多个元素发生碰撞,而不是return最近的碰撞元素,它将return什么都没有??

RaycastHit 是一个 struct 类型,因此它本身永远不可能是 null。正如文档所说

The first invalid result is identified by the collider being null.


注意constructor中(可选)参数的顺序如果没有明确命名是

new RaycastCommand(Vector3 from, Vector3 direction, float distance, int layerMask, int maxHits);

它们的默认值是

  • distance = float.MaxValue
  • layerMask = -5
  • maxHits = 1

再说一遍:maxHits的默认值是1!

不幸的是,这在文档中不可见,但您的 IDE 应该会揭示这一点。


现在知道了这一点,您就可以看到您当前的示例

new RaycastCommand(from, direction, 4);

实际上说:射线的最大距离4但仍然允许1单次命中!

如果您想获得 4 个可能的点击率,您必须将其命名为

new RaycastCommand(from, direction, maxHits: 4);

现在,如果您这样调用它,将创建一个完全包含 4 RaycastHit 个条目的缓冲区,无论命中了多少对象或是否有任何对象。


如果

那么什么都没有被击中
results[0].collider == null

不幸的是

The second and later invalid results are not written to by the raycast command so their colliders are not guaranteed to be null.

您必须过滤它们,例如喜欢

var validResults = new List<RaycastHit>();
foreach(var r in results)
{
    if(r.collider == null) break;

    validResults.Add(r);
}

然后您还可以使用 Linq 将它们进一步排序为 "nearest first",例如

using System.Linq;

...

var orderedResults = validResults.OrderBy(r=>r.distance).ToList();

// Which is basically a shorthand for something like the following
// not sure though how Linq does it internally, I hope it does something more efficient ^^
var orderedResults = new List<RaycastHit>();
foreach(var r in validResults)
{
    for(int i = 0; i < orderedResults.Count; i ++)
    {
        if(orderedResults[i].distance <= r.distance)
        {
            orderesResult.Insert(i + 1, r);
            break;
        }
    }
}