/// <summary>
        /// Checks whether <see cref="Character"/> is grounded.
        /// </summary>
        /// <remarks>
        /// <see cref="CharacterController.isGrounded"/> isn't accurate so this method does an additional check using <see cref="Physics"/>.
        /// </remarks>
        /// <returns>Whether <see cref="Character"/> is grounded.</returns>
        protected virtual bool CheckIfCharacterControllerIsGrounded()
        {
            if (Character.isGrounded)
            {
                return(true);
            }

            HeapAllocationFreeReadOnlyList <Collider> hitColliders = PhysicsCast.OverlapSphereAll(
                null,
                Character.transform.position + (Vector3.up * (Character.radius - Character.skinWidth - 0.001f)),
                Character.radius,
                1 << Character.gameObject.layer);

            foreach (Collider hitCollider in hitColliders)
            {
                if (hitCollider != Character &&
                    hitCollider != RigidbodyCollider &&
                    !ignoredColliders.Contains(hitCollider) &&
                    !Physics.GetIgnoreLayerCollision(
                        hitCollider.gameObject.layer,
                        Character.gameObject.layer) &&
                    !Physics.GetIgnoreLayerCollision(hitCollider.gameObject.layer, PhysicsBody.gameObject.layer))
                {
                    return(true);
                }
            }

            return(false);
        }
        /// <summary>
        /// Casts a ray from the <see cref="source"/> origin location towards the <see cref="target"/> destination location and determines whether the raycast is blocked by another <see cref="Collider"/>.
        /// </summary>
        public virtual void Process()
        {
            Vector3 difference = target.transform.position - source.transform.position;
            Ray     ray        = new Ray(source.transform.position, difference);

            RaycastHit[] raycastHits = PhysicsCast.RaycastAll(
                physicsCast,
                ray,
                difference.magnitude,
                Physics.IgnoreRaycastLayer);
            bool isObscured = raycastHits.Any(hit => hit.transform.gameObject != source && hit.collider != target);

            if (isObscured == wasPreviouslyObscured)
            {
                return;
            }

            wasPreviouslyObscured = isObscured;

            if (isObscured)
            {
                TargetObscured?.Invoke(raycastHits);
            }
            else
            {
                TargetUnobscured?.Invoke();
            }
        }
        /// <summary>
        /// Casts a ray to find the first collision.
        /// </summary>
        /// <param name="tracerRaycast">The ray to cast with.</param>
        /// <returns><see langword="true"/> if a valid surface is located.</returns>
        protected virtual bool FindFirstCollision(Ray tracerRaycast)
        {
            RaycastHit collision;

            if (PhysicsCast.Raycast(physicsCast, tracerRaycast, out collision, maximumDistance, Physics.IgnoreRaycastLayer))
            {
                SetSurfaceData(collision);
                return(true);
            }
            return(false);
        }
 /// <summary>
 /// Casts a ray to find all collisions.
 /// </summary>
 /// <param name="tracerRaycast">The ray to cast with.</param>
 /// <returns><see langword="true"/> if a valid surface is located.</returns>
 protected virtual bool FindAllCollisions(Ray tracerRaycast)
 {
     foreach (RaycastHit collision in PhysicsCast.RaycastAll(physicsCast, tracerRaycast, maximumDistance, Physics.IgnoreRaycastLayer).OrderBy(hit => hit.distance))
     {
         if (ValidSurface(collision))
         {
             SetSurfaceData(collision);
             return(true);
         }
     }
     return(false);
 }