Search code examples
unity-game-engineraycasting

I don't understand RaycastCommand


Documentation (https://docs.unity3d.com/2019.2/Documentation/ScriptReference/RaycastCommand.html) says:

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.

Soo, having

new RaycastCommand(from, direction, 4);
  1. How can i distinguish if i had no hits vs when result is invalid? Both cases have hit.collider == null.
  2. If collides with multiple elements, rather than returning the nearest colliding one, it will return nothing??

Solution

  • RaycastHit is a struct type thus itself can never be null. So as the docs says

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


    NOTE: The order of the (optional) parameters in the constructor if not explicitly named is

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

    and their default values are

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

    Again in words: The default value for maxHits is 1!

    Unfortunately this is not visible in the docs but your IDE should reveal that.


    Knowing this now you can see that your current example

    new RaycastCommand(from, direction, 4);
    

    actually says: The maximum distance of the ray is 4 but still allows only 1 single hit!

    If you rather wanted to have 4 possible hits you have to call it as

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

    now if you call it like this a buffer with exactly 4 RaycastHit entries will be created, no matter how many objects are hit or if any at all.


    Then nothing was hit at all if

    results[0].collider == null
    

    and since unfortunately

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

    you would have to filter them e.g. like

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

    Then you could further also sort them to "nearest first" using Linq like e.g.

    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;
            }
        }
    }