Assets/MRTK/Core/Utilities/Physics/ConeCastUtility.cs
/// <summary>
/// Function casts a sphere along a ray and checks if the hitpoint is within the angle of the cone and returns the best target determined by the weights provided.
/// </summary>
/// <param name="origin">The vertex of the cone and the at the start of the sweep.</param>
/// <param name="direction">The direction into which to sweep the sphere..</param>
/// <param name="maxRadius">The radius of the sweep.</param>
/// <param name="maxDistance">The max length of the cast.</param>
/// <param name="coneAngle">The angle used to define the cone.</param>
/// <param name="layerMask">A Layer mask that is used to selectively ignore colliders when casting a capsule.</param>
/// <param name="distanceWeight">The importance of distance between the hitpoint and the origin in selecting the best target.</param>
/// <param name="angleWeight">The importance of angle between the hitpoint and the origin in selecting the best target.</param>
/// <param name="distanceToCenterWeight">The importance of distance between the hitpoint and the center of the object in selecting the best target.</param>
/// <param name="angleToCenterWeight">The importance of angle between the hitpoint and the center of the object in selecting the best target.</param>
/// <returns>The RaycastHit of the best object.</returns>
Function casts a sphere along a ray and checks if the hitpoint is within the angle of the cone and returns the best target determined by the weights provided.
Function casts a sphere along a ray and checks if the hitpoint is within the angle of the cone and returns the best target determined by the weights provided .
public static RaycastHit ConeCastBest(Vector3 origin, Vector3 direction, float maxRadius, float maxDistance, float coneAngle, LayerMask layerMask, float distanceWeight, float angleWeight, float distanceToCenterWeight, float angleToCenterWeight)
        {
            if (sphereCastHits == null || sphereCastHits.Length < sphereCastMaxHitCount)
            {
                sphereCastHits = new RaycastHit[sphereCastMaxHitCount];
            }
            var hitCount = UnityEngine.Physics.SphereCastNonAlloc(origin - (direction * maxRadius), maxRadius, direction, sphereCastHits, maxDistance, layerMask, QueryTriggerInteraction.Ignore);
            if (hitCount >= sphereCastMaxHitCount && sphereCastMaxHitCount < sphereCastLimit)
            {
                sphereCastMaxHitCount = Math.Min(sphereCastLimit, sphereCastMaxHitCount * 2);
            }
            RaycastHit hitGameobject = new RaycastHit();
            float score = float.MaxValue;
            for (int i = 0; i < hitCount; i++)
            {
                RaycastHit hit = sphereCastHits[i];
                Vector3 hitPoint = hit.point;
                Vector3 directionToHit = hitPoint - origin;
                float angleToHit = Vector3.Angle(direction, directionToHit);
                Vector3 hitDistance = hit.collider.transform.position - hitPoint;
                Vector3 directionToCenter = hit.collider.transform.position - origin;
                float angleToCenter = Vector3.Angle(direction, directionToCenter);
                if (hit.collider.GetType() != typeof(MeshCollider))
                {
                    Vector3 pointFurtherAlongGazePath = (maxRadius * 0.5f * direction.normalized) + FindNearestPointOnLine(origin, direction, hitPoint);
                    Vector3 closestPointToPointFurtherAlongGazePath = hit.collider.ClosestPoint(pointFurtherAlongGazePath);
                    Vector3 directionToSecondaryPoint = closestPointToPointFurtherAlongGazePath - origin;
                    float angleToSecondaryPoint = Vector3.Angle(direction, directionToSecondaryPoint);
                    if (angleToSecondaryPoint < angleToHit)
                    {
                        hitPoint = closestPointToPointFurtherAlongGazePath;
                        directionToHit = directionToSecondaryPoint;
                        angleToHit = angleToSecondaryPoint;
                        hitDistance = hit.collider.transform.position - hitPoint;
                    }
                }
                if (angleToHit < coneAngle)
                {
                    float distanceScore = distanceWeight == 0 ? 0.0f : (distanceWeight * directionToHit.magnitude);
                    float angleScore = angleWeight == 0 ? 0.0f : (angleWeight * angleToHit);
                    float centerScore = distanceToCenterWeight == 0 ? 0.0f : (distanceToCenterWeight * hitDistance.magnitude);
                    float centerAngleScore = angleToCenterWeight == 0 ? 0.0f : (angleToCenterWeight * angleToCenter);
                    float newScore = distanceScore + angleScore + centerScore + centerAngleScore;
                    if (newScore < score)
                    {
                        score = newScore;
                        hitGameobject = hit;
                    }
                }
            }
            return hitGameobject;
        }
public static RaycastHit ConeCastBest ( Vector3 origin , Vector3 direction , float maxRadius , float maxDistance , float coneAngle , LayerMask layerMask , float distanceWeight , float angleWeight , float distanceToCenterWeight , float angleToCenterWeight ) { if ( sphereCastHits == null || sphereCastHits . Length < sphereCastMaxHitCount ) { sphereCastHits = new RaycastHit [ sphereCastMaxHitCount ] ; } var hitCount = UnityEngine . Physics . SphereCastNonAlloc ( origin - ( direction * maxRadius ) , maxRadius , direction , sphereCastHits , maxDistance , layerMask , QueryTriggerInteraction . Ignore ) ; if ( hitCount >= sphereCastMaxHitCount && sphereCastMaxHitCount < sphereCastLimit ) { sphereCastMaxHitCount = Math . Min ( sphereCastLimit , sphereCastMaxHitCount * 2 ) ; } RaycastHit hitGameobject = new RaycastHit ( ) ; float score = float . MaxValue ; for ( int i = 0 ; i < hitCount ; i ++ ) { RaycastHit hit = sphereCastHits [ i ] ; Vector3 hitPoint = hit . point ; Vector3 directionToHit = hitPoint - origin ; float angleToHit = Vector3 . Angle ( direction , directionToHit ) ; Vector3 hitDistance = hit . collider . transform . position - hitPoint ; Vector3 directionToCenter = hit . collider . transform . position - origin ; float angleToCenter = Vector3 . Angle ( direction , directionToCenter ) ; if ( hit . collider . GetType ( ) != typeof ( MeshCollider ) ) { Vector3 pointFurtherAlongGazePath = ( maxRadius * 0.5f * direction . normalized ) + FindNearestPointOnLine ( origin , direction , hitPoint ) ; Vector3 closestPointToPointFurtherAlongGazePath = hit . collider . ClosestPoint ( pointFurtherAlongGazePath ) ; Vector3 directionToSecondaryPoint = closestPointToPointFurtherAlongGazePath - origin ; float angleToSecondaryPoint = Vector3 . Angle ( direction , directionToSecondaryPoint ) ; if ( angleToSecondaryPoint < angleToHit ) { hitPoint = closestPointToPointFurtherAlongGazePath ; directionToHit = directionToSecondaryPoint ; angleToHit = angleToSecondaryPoint ; hitDistance = hit . collider . transform . position - hitPoint ; } } if ( angleToHit < coneAngle ) { float distanceScore = distanceWeight == 0 ? 0.0f : ( distanceWeight * directionToHit . magnitude ) ; float angleScore = angleWeight == 0 ? 0.0f : ( angleWeight * angleToHit ) ; float centerScore = distanceToCenterWeight == 0 ? 0.0f : ( distanceToCenterWeight * hitDistance . magnitude ) ; float centerAngleScore = angleToCenterWeight == 0 ? 0.0f : ( angleToCenterWeight * angleToCenter ) ; float newScore = distanceScore + angleScore + centerScore + centerAngleScore ; if ( newScore < score ) { score = newScore ; hitGameobject = hit ; } } } return hitGameobject ; }
Function casts a sphere along a ray and checks if the hitpoint is within the angle of the cone and returns the best target determined by the weights provided.
Function casts a sphere along a ray and checks if the hitpoint is within the angle of the cone and returns the best target determined by the weights provided .
// Algorithm: double the max hit count if there are too many results, up to a certain limit // There might be more hits we didn't get, grow the array and try again next time // Note that this frame, the results might be imprecise. // Additional work to see if there is a better point slightly further ahead on the direction line. This is only allowed if the collider isn't a mesh collider.
The RaycastHit of the best object.
The RaycastHit of the best object .
origin
Vector3
The vertex of the cone and the at the start of the sweep.
The vertex of the cone and the at the start of the sweep .
direction
Vector3
The direction into which to sweep the sphere
The direction into which to sweep the sphere
maxRadius
float
The radius of the sweep.
The radius of the sweep .
maxDistance
float
The max length of the cast.
The max length of the cast .
coneAngle
float
The angle used to define the cone.
The angle used to define the cone .
layerMask
LayerMask
A Layer mask that is used to selectively ignore colliders when casting a capsule.
A Layer mask that is used to selectively ignore colliders when casting a capsule .
distanceWeight
float
The importance of distance between the hitpoint and the origin in selecting the best target.
The importance of distance between the hitpoint and the origin in selecting the best target .
angleWeight
float
The importance of angle between the hitpoint and the origin in selecting the best target.
The importance of angle between the hitpoint and the origin in selecting the best target .
distanceToCenterWeight
float
The importance of distance between the hitpoint and the center of the object in selecting the best target.
The importance of distance between the hitpoint and the center of the object in selecting the best target .
angleToCenterWeight
float
The importance of angle between the hitpoint and the center of the object in selecting the best target.
The importance of angle between the hitpoint and the center of the object in selecting the best target .