/// <summary> /// Returns true if a collider's bounds is within the camera FOV. /// Utilizes a cache to test if this collider has been seen before and returns current frame's calculated result. /// </summary> /// <param name="myCollider">The collider to test</param> public static bool IsInFOVConeCached(this Camera cam, Collider myCollider) { // if the collider's size is zero, it is not visible. Return false. if (myCollider.bounds.size == Vector3.zero || myCollider.transform.localScale == Vector3.zero) { return(false); } Tuple <Collider, Camera> cameraColliderPair = new Tuple <Collider, Camera>(myCollider, cam); bool result = false; if (inFOVConeLastCalculatedFrame != Time.frameCount) { inFOVConeColliderCache.Clear(); inFOVConeLastCalculatedFrame = Time.frameCount; } else if (inFOVConeColliderCache.TryGetValue(cameraColliderPair, out result)) { return(result); } inFOVConeBoundsCornerPoints.Clear(); BoundsExtensions.GetColliderBoundsPoints(myCollider, inFOVConeBoundsCornerPoints, 0); float xMin = float.MaxValue, yMin = float.MaxValue, zMin = float.MaxValue; float xMax = float.MinValue, yMax = float.MinValue, zMax = float.MinValue; for (int i = 0; i < inFOVConeBoundsCornerPoints.Count; i++) { var corner = inFOVConeBoundsCornerPoints[i]; if (cam.IsInFOVCone(corner, 0)) { inFOVConeColliderCache.Add(cameraColliderPair, true); return(true); } xMin = Mathf.Min(xMin, corner.x); yMin = Mathf.Min(yMin, corner.y); zMin = Mathf.Min(zMin, corner.z); xMax = Mathf.Max(xMax, corner.x); yMax = Mathf.Max(yMax, corner.y); zMax = Mathf.Max(zMax, corner.z); } var cameraPos = cam.transform.position; result = xMin <= cameraPos.x && cameraPos.x <= xMax && yMin <= cameraPos.y && cameraPos.y <= yMax && zMin <= cameraPos.z && cameraPos.z <= zMax; inFOVConeColliderCache.Add(cameraColliderPair, result); return(result); }
/// <summary> /// Returns true if a collider's bounds is within the camera FOV. /// Utilizes a cache to test if this collider has been seen before and returns current frame's calculated result. /// NOTE: This is a 'loose' FOV check -- it can return true in cases when the collider is actually not in the FOV /// because it does an axis-aligned check when testing for large colliders. So, if the axis aligned bounds are in the bounds of the camera, it will return true. /// </summary> /// <param name="myCollider">The collider to test</param> public static bool IsInFOVCached(this Camera cam, Collider myCollider) { // if the collider's size is zero, it is not visible. Return false. if (myCollider.bounds.size == Vector3.zero || myCollider.transform.localScale == Vector3.zero) { return(false); } Tuple <Collider, Camera> cameraColliderPair = new Tuple <Collider, Camera>(myCollider, cam); bool result = false; if (inFOVLastCalculatedFrame != Time.frameCount) { inFOVColliderCache.Clear(); inFOVLastCalculatedFrame = Time.frameCount; } else if (inFOVColliderCache.TryGetValue(cameraColliderPair, out result)) { return(result); } inFOVBoundsCornerPoints.Clear(); BoundsExtensions.GetColliderBoundsPoints(myCollider, inFOVBoundsCornerPoints, 0); float xMin = float.MaxValue, yMin = float.MaxValue, zMin = float.MaxValue; float xMax = float.MinValue, yMax = float.MinValue, zMax = float.MinValue; for (int i = 0; i < inFOVBoundsCornerPoints.Count; i++) { var corner = inFOVBoundsCornerPoints[i]; Vector3 screenPoint = cam.WorldToViewportPoint(corner); bool isInFOV = screenPoint.z >= 0 && screenPoint.z <= cam.farClipPlane && screenPoint.x >= 0 && screenPoint.x <= 1 && screenPoint.y >= 0 && screenPoint.y <= 1; if (isInFOV) { inFOVColliderCache.Add(cameraColliderPair, true); return(true); } // if the point is behind the camera, the x and y viewport positions are negated var zViewport = screenPoint.z; var xViewport = zViewport >= 0 ? screenPoint.x : -screenPoint.x; var yViewport = zViewport >= 0 ? screenPoint.y : -screenPoint.y; xMin = Mathf.Min(xMin, xViewport); yMin = Mathf.Min(yMin, yViewport); zMin = Mathf.Min(zMin, zViewport); xMax = Mathf.Max(xMax, xViewport); yMax = Mathf.Max(yMax, yViewport); zMax = Mathf.Max(zMax, zViewport); } // Check that collider is visible even if all corners are not visible // such as when having a large collider result = zMax > 0 && // Front of collider is in front of the camera. zMin < cam.farClipPlane && // Back of collider is not too far away. xMin < 1 && // Left edge is not too far to the right. xMax > 0 && // Right edge is not too far to the left. yMin < 1 && // Bottom edge is not too high. yMax > 0; // Top edge is not too low. inFOVColliderCache.Add(cameraColliderPair, result); return(result); }