| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518 |
- using UnityEngine;
- using System;
- using Cinemachine.Utility;
- using UnityEngine.Serialization;
- namespace Cinemachine
- {
- /// <summary>
- /// This is a CinemachineComponent in the Aim section of the component pipeline.
- /// Its job is to aim the camera at the vcam's LookAt target object, with
- /// configurable offsets, damping, and composition rules.
- ///
- /// The composer does not change the camera's position. It will only pan and tilt the
- /// camera where it is, in order to get the desired framing. To move the camera, you have
- /// to use the virtual camera's Body section.
- /// </summary>
- [DocumentationSorting(DocumentationSortingAttribute.Level.UserRef)]
- [AddComponentMenu("")] // Don't display in add component menu
- [SaveDuringPlay]
- public class CinemachineComposer : CinemachineComponentBase
- {
- /// <summary>Target offset from the object's center in LOCAL space which
- /// the Composer tracks. Use this to fine-tune the tracking target position
- /// when the desired area is not in the tracked object's center</summary>
- [Tooltip("Target offset from the target object's center in target-local space. Use this to "
- + "fine-tune the tracking target position when the desired area is not the tracked object's center.")]
- public Vector3 m_TrackedObjectOffset = Vector3.zero;
- /// <summary>This setting will instruct the composer to adjust its target offset based
- /// on the motion of the target. The composer will look at a point where it estimates
- /// the target will be this many seconds into the future. Note that this setting is sensitive
- /// to noisy animation, and can amplify the noise, resulting in undesirable camera jitter.
- /// If the camera jitters unacceptably when the target is in motion, turn down this setting,
- /// or animate the target more smoothly.</summary>
- [Space]
- [Tooltip("This setting will instruct the composer to adjust its target offset based on the motion "
- + "of the target. The composer will look at a point where it estimates the target will be this "
- + "many seconds into the future. Note that this setting is sensitive to noisy animation, and "
- + "can amplify the noise, resulting in undesirable camera jitter. If the camera jitters "
- + "unacceptably when the target is in motion, turn down this setting, or animate the target more smoothly.")]
- [Range(0f, 1f)]
- public float m_LookaheadTime = 0;
- /// <summary>Controls the smoothness of the lookahead algorithm. Larger values smooth out
- /// jittery predictions and also increase prediction lag</summary>
- [Tooltip("Controls the smoothness of the lookahead algorithm. Larger values smooth "
- + "out jittery predictions and also increase prediction lag")]
- [Range(0, 30)]
- public float m_LookaheadSmoothing = 0;
- /// <summary>If checked, movement along the Y axis will be ignored for lookahead calculations</summary>
- [Tooltip("If checked, movement along the Y axis will be ignored for lookahead calculations")]
- public bool m_LookaheadIgnoreY;
- /// <summary>How aggressively the camera tries to follow the target in the screen-horizontal direction.
- /// Small numbers are more responsive, rapidly orienting the camera to keep the target in
- /// the dead zone. Larger numbers give a more heavy slowly responding camera.
- /// Using different vertical and horizontal settings can yield a wide range of camera behaviors.</summary>
- [Space]
- [Range(0f, 20)]
- [Tooltip("How aggressively the camera tries to follow the target in the screen-horizontal direction. "
- + "Small numbers are more responsive, rapidly orienting the camera to keep the target in "
- + "the dead zone. Larger numbers give a more heavy slowly responding camera. Using different "
- + "vertical and horizontal settings can yield a wide range of camera behaviors.")]
- public float m_HorizontalDamping = 0.5f;
- /// <summary>How aggressively the camera tries to follow the target in the screen-vertical direction.
- /// Small numbers are more responsive, rapidly orienting the camera to keep the target in
- /// the dead zone. Larger numbers give a more heavy slowly responding camera. Using different vertical
- /// and horizontal settings can yield a wide range of camera behaviors.</summary>
- [Range(0f, 20)]
- [Tooltip("How aggressively the camera tries to follow the target in the screen-vertical direction. "
- + "Small numbers are more responsive, rapidly orienting the camera to keep the target in "
- + "the dead zone. Larger numbers give a more heavy slowly responding camera. Using different "
- + "vertical and horizontal settings can yield a wide range of camera behaviors.")]
- public float m_VerticalDamping = 0.5f;
- /// <summary>Horizontal screen position for target. The camera will rotate to the position the tracked object here</summary>
- [Space]
- [Range(-0.5f, 1.5f)]
- [Tooltip("Horizontal screen position for target. The camera will rotate to position the tracked object here.")]
- public float m_ScreenX = 0.5f;
- /// <summary>Vertical screen position for target, The camera will rotate to to position the tracked object here</summary>
- [Range(-0.5f, 1.5f)]
- [Tooltip("Vertical screen position for target, The camera will rotate to position the tracked object here.")]
- public float m_ScreenY = 0.5f;
- /// <summary>Camera will not rotate horizontally if the target is within this range of the position</summary>
- [Range(0f, 2f)]
- [Tooltip("Camera will not rotate horizontally if the target is within this range of the position.")]
- public float m_DeadZoneWidth = 0f;
- /// <summary>Camera will not rotate vertically if the target is within this range of the position</summary>
- [Range(0f, 2f)]
- [Tooltip("Camera will not rotate vertically if the target is within this range of the position.")]
- public float m_DeadZoneHeight = 0f;
- /// <summary>When target is within this region, camera will gradually move to re-align
- /// towards the desired position, depending onm the damping speed</summary>
- [Range(0f, 2f)]
- [Tooltip("When target is within this region, camera will gradually rotate horizontally to re-align "
- + "towards the desired position, depending on the damping speed.")]
- public float m_SoftZoneWidth = 0.8f;
- /// <summary>When target is within this region, camera will gradually move to re-align
- /// towards the desired position, depending onm the damping speed</summary>
- [Range(0f, 2f)]
- [Tooltip("When target is within this region, camera will gradually rotate vertically to re-align "
- + "towards the desired position, depending on the damping speed.")]
- public float m_SoftZoneHeight = 0.8f;
- /// <summary>A non-zero bias will move the targt position away from the center of the soft zone</summary>
- [Range(-0.5f, 0.5f)]
- [Tooltip("A non-zero bias will move the target position horizontally away from the center of the soft zone.")]
- public float m_BiasX = 0f;
- /// <summary>A non-zero bias will move the targt position away from the center of the soft zone</summary>
- [Range(-0.5f, 0.5f)]
- [Tooltip("A non-zero bias will move the target position vertically away from the center of the soft zone.")]
- public float m_BiasY = 0f;
- /// <summary>Force target to center of screen when this camera activates.
- /// If false, will clamp target to the edges of the dead zone</summary>
- [Tooltip("Force target to center of screen when this camera activates. If false, will "
- + "clamp target to the edges of the dead zone")]
- public bool m_CenterOnActivate = true;
- /// <summary>True if component is enabled and has a LookAt defined</summary>
- public override bool IsValid { get { return enabled && LookAtTarget != null; } }
- /// <summary>Get the Cinemachine Pipeline stage that this component implements.
- /// Always returns the Aim stage</summary>
- public override CinemachineCore.Stage Stage { get { return CinemachineCore.Stage.Aim; } }
- /// <summary>Internal API for inspector</summary>
- public Vector3 TrackedPoint { get; private set; }
- /// <summary>Apply the target offsets to the target location.
- /// Also set the TrackedPoint property, taking lookahead into account.</summary>
- /// <param name="lookAt">The unoffset LookAt point</param>
- /// <param name="up">Currest effective world up</param>
- /// <param name="deltaTime">Current effective deltaTime</param>
- /// <returns>The LookAt point with the offset applied</returns>
- protected virtual Vector3 GetLookAtPointAndSetTrackedPoint(
- Vector3 lookAt, Vector3 up, float deltaTime)
- {
- Vector3 pos = lookAt;
- if (LookAtTarget != null)
- pos += LookAtTargetRotation * m_TrackedObjectOffset;
- if (m_LookaheadTime < Epsilon)
- TrackedPoint = pos;
- else
- {
- m_Predictor.Smoothing = m_LookaheadSmoothing;
- m_Predictor.AddPosition(pos, VirtualCamera.PreviousStateIsValid ? deltaTime : -1, m_LookaheadTime);
- var delta = m_Predictor.PredictPositionDelta(m_LookaheadTime);
- if (m_LookaheadIgnoreY)
- delta = delta.ProjectOntoPlane(up);
- TrackedPoint = pos + delta;
- }
- return pos;
- }
- /// <summary>State information for damping</summary>
- Vector3 m_CameraPosPrevFrame = Vector3.zero;
- Vector3 m_LookAtPrevFrame = Vector3.zero;
- Vector2 m_ScreenOffsetPrevFrame = Vector2.zero;
- Quaternion m_CameraOrientationPrevFrame = Quaternion.identity;
- internal PositionPredictor m_Predictor = new PositionPredictor();
- /// <summary>This is called to notify the us that a target got warped,
- /// so that we can update its internal state to make the camera
- /// also warp seamlessy.</summary>
- /// <param name="target">The object that was warped</param>
- /// <param name="positionDelta">The amount the target's position changed</param>
- public override void OnTargetObjectWarped(Transform target, Vector3 positionDelta)
- {
- base.OnTargetObjectWarped(target, positionDelta);
- if (target == LookAtTarget)
- {
- m_CameraPosPrevFrame += positionDelta;
- m_LookAtPrevFrame += positionDelta;
- m_Predictor.ApplyTransformDelta(positionDelta);
- }
- }
- /// <summary>
- /// Force the virtual camera to assume a given position and orientation
- /// </summary>
- /// <param name="pos">Worldspace pposition to take</param>
- /// <param name="rot">Worldspace orientation to take</param>
- public override void ForceCameraPosition(Vector3 pos, Quaternion rot)
- {
- base.ForceCameraPosition(pos, rot);
- m_CameraPosPrevFrame = pos;
- m_CameraOrientationPrevFrame = rot;
- }
-
- /// <summary>
- /// Report maximum damping time needed for this component.
- /// </summary>
- /// <returns>Highest damping setting in this component</returns>
- public override float GetMaxDampTime()
- {
- return Mathf.Max(m_HorizontalDamping, m_VerticalDamping);
- }
- /// <summary>Sets the state's ReferenceLookAt, applying the offset.</summary>
- /// <param name="curState">Input state that must be mutated</param>
- /// <param name="deltaTime">Current effective deltaTime</param>
- public override void PrePipelineMutateCameraState(ref CameraState curState, float deltaTime)
- {
- if (IsValid && curState.HasLookAt)
- curState.ReferenceLookAt = GetLookAtPointAndSetTrackedPoint(
- curState.ReferenceLookAt, curState.ReferenceUp, deltaTime);
- }
- /// <summary>Applies the composer rules and orients the camera accordingly</summary>
- /// <param name="curState">The current camera state</param>
- /// <param name="deltaTime">Used for calculating damping. If less than
- /// zero, then target will snap to the center of the dead zone.</param>
- public override void MutateCameraState(ref CameraState curState, float deltaTime)
- {
- if (!IsValid || !curState.HasLookAt)
- return;
- // Correct the tracked point in the event that it's behind the camera
- // while the real target is in front
- if (!(TrackedPoint - curState.ReferenceLookAt).AlmostZero())
- {
- Vector3 mid = Vector3.Lerp(curState.CorrectedPosition, curState.ReferenceLookAt, 0.5f);
- Vector3 toLookAt = curState.ReferenceLookAt - mid;
- Vector3 toTracked = TrackedPoint - mid;
- if (Vector3.Dot(toLookAt, toTracked) < 0)
- {
- float t = Vector3.Distance(curState.ReferenceLookAt, mid)
- / Vector3.Distance(curState.ReferenceLookAt, TrackedPoint);
- TrackedPoint = Vector3.Lerp(curState.ReferenceLookAt, TrackedPoint, t);
- }
- }
- float targetDistance = (TrackedPoint - curState.CorrectedPosition).magnitude;
- if (targetDistance < Epsilon)
- {
- if (deltaTime >= 0 && VirtualCamera.PreviousStateIsValid)
- curState.RawOrientation = m_CameraOrientationPrevFrame;
- return; // navel-gazing, get outa here
- }
- // Expensive FOV calculations
- mCache.UpdateCache(curState.Lens, SoftGuideRect, HardGuideRect, targetDistance);
- Quaternion rigOrientation = curState.RawOrientation;
- if (deltaTime < 0 || !VirtualCamera.PreviousStateIsValid)
- {
- // No damping, just snap to central bounds, skipping the soft zone
- rigOrientation = Quaternion.LookRotation(
- rigOrientation * Vector3.forward, curState.ReferenceUp);
- Rect rect = mCache.mFovSoftGuideRect;
- if (m_CenterOnActivate)
- rect = new Rect(rect.center, Vector2.zero); // Force to center
- RotateToScreenBounds(
- ref curState, rect, curState.ReferenceLookAt,
- ref rigOrientation, mCache.mFov, mCache.mFovH, -1);
- }
- else
- {
- // Start with previous frame's orientation (but with current up)
- Vector3 dir = m_LookAtPrevFrame - m_CameraPosPrevFrame;
- if (dir.AlmostZero())
- rigOrientation = Quaternion.LookRotation(
- m_CameraOrientationPrevFrame * Vector3.forward, curState.ReferenceUp);
- else
- {
- dir = Quaternion.Euler(curState.PositionDampingBypass) * dir;
- rigOrientation = Quaternion.LookRotation(dir, curState.ReferenceUp);
- rigOrientation = rigOrientation.ApplyCameraRotation(
- -m_ScreenOffsetPrevFrame, curState.ReferenceUp);
- }
- // Move target through the soft zone, with damping
- RotateToScreenBounds(
- ref curState, mCache.mFovSoftGuideRect, TrackedPoint,
- ref rigOrientation, mCache.mFov, mCache.mFovH, deltaTime);
- // Force the actual target (not the lookahead one) into the hard bounds, no damping
- if (deltaTime < 0 || VirtualCamera.LookAtTargetAttachment > 1 - Epsilon)
- RotateToScreenBounds(
- ref curState, mCache.mFovHardGuideRect, curState.ReferenceLookAt,
- ref rigOrientation, mCache.mFov, mCache.mFovH, -1);
- }
- m_CameraPosPrevFrame = curState.CorrectedPosition;
- m_LookAtPrevFrame = TrackedPoint;
- m_CameraOrientationPrevFrame = UnityQuaternionExtensions.Normalized(rigOrientation);
- m_ScreenOffsetPrevFrame = m_CameraOrientationPrevFrame.GetCameraRotationToTarget(
- m_LookAtPrevFrame - curState.CorrectedPosition, curState.ReferenceUp);
- curState.RawOrientation = m_CameraOrientationPrevFrame;
- }
- /// <summary>Internal API for the inspector editor</summary>
- internal Rect SoftGuideRect
- {
- get
- {
- return new Rect(
- m_ScreenX - m_DeadZoneWidth / 2, m_ScreenY - m_DeadZoneHeight / 2,
- m_DeadZoneWidth, m_DeadZoneHeight);
- }
- set
- {
- m_DeadZoneWidth = Mathf.Clamp(value.width, 0, 2);
- m_DeadZoneHeight = Mathf.Clamp(value.height, 0, 2);
- m_ScreenX = Mathf.Clamp(value.x + m_DeadZoneWidth / 2, -0.5f, 1.5f);
- m_ScreenY = Mathf.Clamp(value.y + m_DeadZoneHeight / 2, -0.5f, 1.5f);
- m_SoftZoneWidth = Mathf.Max(m_SoftZoneWidth, m_DeadZoneWidth);
- m_SoftZoneHeight = Mathf.Max(m_SoftZoneHeight, m_DeadZoneHeight);
- }
- }
- /// <summary>Internal API for the inspector editor</summary>
- internal Rect HardGuideRect
- {
- get
- {
- Rect r = new Rect(
- m_ScreenX - m_SoftZoneWidth / 2, m_ScreenY - m_SoftZoneHeight / 2,
- m_SoftZoneWidth, m_SoftZoneHeight);
- r.position += new Vector2(
- m_BiasX * (m_SoftZoneWidth - m_DeadZoneWidth),
- m_BiasY * (m_SoftZoneHeight - m_DeadZoneHeight));
- return r;
- }
- set
- {
- m_SoftZoneWidth = Mathf.Clamp(value.width, 0, 2f);
- m_SoftZoneHeight = Mathf.Clamp(value.height, 0, 2f);
- m_DeadZoneWidth = Mathf.Min(m_DeadZoneWidth, m_SoftZoneWidth);
- m_DeadZoneHeight = Mathf.Min(m_DeadZoneHeight, m_SoftZoneHeight);
- Vector2 center = value.center;
- Vector2 bias = center - new Vector2(m_ScreenX, m_ScreenY);
- float biasWidth = Mathf.Max(0, m_SoftZoneWidth - m_DeadZoneWidth);
- float biasHeight = Mathf.Max(0, m_SoftZoneHeight - m_DeadZoneHeight);
- m_BiasX = biasWidth < Epsilon ? 0 : Mathf.Clamp(bias.x / biasWidth, -0.5f, 0.5f);
- m_BiasY = biasHeight < Epsilon ? 0 : Mathf.Clamp(bias.y / biasHeight, -0.5f, 0.5f);
- }
- }
- // Cache for some expensive calculations
- struct FovCache
- {
- public Rect mFovSoftGuideRect;
- public Rect mFovHardGuideRect;
- public float mFovH;
- public float mFov;
- float mOrthoSizeOverDistance;
- float mAspect;
- Rect mSoftGuideRect;
- Rect mHardGuideRect;
- public void UpdateCache(
- LensSettings lens, Rect softGuide, Rect hardGuide, float targetDistance)
- {
- bool recalculate = mAspect != lens.Aspect
- || softGuide != mSoftGuideRect || hardGuide != mHardGuideRect;
- if (lens.Orthographic)
- {
- float orthoOverDistance = Mathf.Abs(lens.OrthographicSize / targetDistance);
- if (mOrthoSizeOverDistance == 0
- || Mathf.Abs(orthoOverDistance - mOrthoSizeOverDistance) / mOrthoSizeOverDistance
- > mOrthoSizeOverDistance * 0.01f)
- recalculate = true;
- if (recalculate)
- {
- // Calculate effective fov - fake it for ortho based on target distance
- mFov = Mathf.Rad2Deg * 2 * Mathf.Atan(orthoOverDistance);
- mFovH = Mathf.Rad2Deg * 2 * Mathf.Atan(lens.Aspect * orthoOverDistance);
- mOrthoSizeOverDistance = orthoOverDistance;
- }
- }
- else
- {
- if (mFov != lens.FieldOfView)
- recalculate = true;
- if (recalculate)
- {
- mFov = lens.FieldOfView;
- double radHFOV = 2 * Math.Atan(Math.Tan(mFov * Mathf.Deg2Rad / 2) * lens.Aspect);
- mFovH = (float)(Mathf.Rad2Deg * radHFOV);
- mOrthoSizeOverDistance = 0;
- }
- }
- if (recalculate)
- {
- mFovSoftGuideRect = ScreenToFOV(softGuide, mFov, mFovH, lens.Aspect);
- mSoftGuideRect = softGuide;
- mFovHardGuideRect = ScreenToFOV(hardGuide, mFov, mFovH, lens.Aspect);
- mHardGuideRect = hardGuide;
- mAspect = lens.Aspect;
- }
- }
- // Convert from screen coords to normalized FOV angular coords
- private Rect ScreenToFOV(Rect rScreen, float fov, float fovH, float aspect)
- {
- Rect r = new Rect(rScreen);
- Matrix4x4 persp = Matrix4x4.Perspective(fov, aspect, 0.0001f, 2f).inverse;
- Vector3 p = persp.MultiplyPoint(new Vector3(0, (r.yMin * 2f) - 1f, 0.5f)); p.z = -p.z;
- float angle = UnityVectorExtensions.SignedAngle(Vector3.forward, p, Vector3.left);
- r.yMin = ((fov / 2) + angle) / fov;
- p = persp.MultiplyPoint(new Vector3(0, (r.yMax * 2f) - 1f, 0.5f)); p.z = -p.z;
- angle = UnityVectorExtensions.SignedAngle(Vector3.forward, p, Vector3.left);
- r.yMax = ((fov / 2) + angle) / fov;
- p = persp.MultiplyPoint(new Vector3((r.xMin * 2f) - 1f, 0, 0.5f)); p.z = -p.z;
- angle = UnityVectorExtensions.SignedAngle(Vector3.forward, p, Vector3.up);
- r.xMin = ((fovH / 2) + angle) / fovH;
- p = persp.MultiplyPoint(new Vector3((r.xMax * 2f) - 1f, 0, 0.5f)); p.z = -p.z;
- angle = UnityVectorExtensions.SignedAngle(Vector3.forward, p, Vector3.up);
- r.xMax = ((fovH / 2) + angle) / fovH;
- return r;
- }
- }
- FovCache mCache;
- /// <summary>
- /// Adjust the rigOrientation to put the camera within the screen bounds.
- /// If deltaTime >= 0 then damping will be applied.
- /// Assumes that currentOrientation fwd is such that input rigOrientation's
- /// local up is NEVER NEVER NEVER pointing downwards, relative to
- /// state.ReferenceUp. If this condition is violated
- /// then you will see crazy spinning. That's the symptom.
- /// </summary>
- private void RotateToScreenBounds(
- ref CameraState state, Rect screenRect, Vector3 trackedPoint,
- ref Quaternion rigOrientation, float fov, float fovH, float deltaTime)
- {
- Vector3 targetDir = trackedPoint - state.CorrectedPosition;
- Vector2 rotToRect = rigOrientation.GetCameraRotationToTarget(targetDir, state.ReferenceUp);
- // Bring it to the edge of screenRect, if outside. Leave it alone if inside.
- ClampVerticalBounds(ref screenRect, targetDir, state.ReferenceUp, fov);
- float min = (screenRect.yMin - 0.5f) * fov;
- float max = (screenRect.yMax - 0.5f) * fov;
- if (rotToRect.x < min)
- rotToRect.x -= min;
- else if (rotToRect.x > max)
- rotToRect.x -= max;
- else
- rotToRect.x = 0;
- min = (screenRect.xMin - 0.5f) * fovH;
- max = (screenRect.xMax - 0.5f) * fovH;
- if (rotToRect.y < min)
- rotToRect.y -= min;
- else if (rotToRect.y > max)
- rotToRect.y -= max;
- else
- rotToRect.y = 0;
- // Apply damping
- if (deltaTime >= 0 && VirtualCamera.PreviousStateIsValid)
- {
- rotToRect.x = VirtualCamera.DetachedLookAtTargetDamp(
- rotToRect.x, m_VerticalDamping, deltaTime);
- rotToRect.y = VirtualCamera.DetachedLookAtTargetDamp(
- rotToRect.y, m_HorizontalDamping, deltaTime);
- }
- // Rotate
- rigOrientation = rigOrientation.ApplyCameraRotation(rotToRect, state.ReferenceUp);
- }
- /// <summary>
- /// Prevent upside-down camera situation. This can happen if we have a high
- /// camera pitch combined with composer settings that cause the camera to tilt
- /// beyond the vertical in order to produce the desired framing. We prevent this by
- /// clamping the composer's vertical settings so that this situation can't happen.
- /// </summary>
- private bool ClampVerticalBounds(ref Rect r, Vector3 dir, Vector3 up, float fov)
- {
- float angle = UnityVectorExtensions.Angle(dir, up);
- float halfFov = (fov / 2f) + 1; // give it a little extra to accommodate precision errors
- if (angle < halfFov)
- {
- // looking up
- float maxY = 1f - (halfFov - angle) / fov;
- if (r.yMax > maxY)
- {
- r.yMin = Mathf.Min(r.yMin, maxY);
- r.yMax = Mathf.Min(r.yMax, maxY);
- return true;
- }
- }
- if (angle > (180 - halfFov))
- {
- // looking down
- float minY = (angle - (180 - halfFov)) / fov;
- if (minY > r.yMin)
- {
- r.yMin = Mathf.Max(r.yMin, minY);
- r.yMax = Mathf.Max(r.yMax, minY);
- return true;
- }
- }
- return false;
- }
- }
- }
|