using Core.Input; using UnityEngine; namespace Core.Camera { /// /// Class to control the camera's behaviour. Camera rig currently operates best on terrain that is mostly on /// a single plane /// ÔÚÕâ¸öÀàÀïÃæ£¬»á¸ù¾Ý /// public class CameraRig : MonoBehaviour { /// /// Look dampening factor /// public float lookDampFactor; /// /// Movement dampening factor /// public float movementDampFactor; /// /// Nearest zoom level - can go a bit further than this on touch, for springiness /// public float nearestZoom = 15; /// /// Furthest zoom level - can go a bit further than this on touch, for springiness /// public float furthestZoom = 40; /// /// True maximum zoom level /// public float maxZoom = 60; /// /// Logarithm used to decay zoom beyond furthest /// public float zoomLogFactor = 10; /// /// How fast zoom recovers to normal /// public float zoomRecoverSpeed = 20; /// /// Y-height of the floor the camera is assuming /// public float floorY; /// /// Camera angle when fully zoomed in /// public Transform zoomedCamAngle; /// /// Map size, edited through the CameraRigEditor script in edit mode /// [HideInInspector] public Rect mapSize = new Rect(-10, -10, 20, 20); /// /// Is the zoom able to exceed its normal zoom extents with a rubber banding effect /// public bool springyZoom = true; /// /// Current look velocity of camera /// Vector3 m_CurrentLookVelocity; /// /// Rotations of camera at various zoom levels /// Quaternion m_MinZoomRotation; Quaternion m_MaxZoomRotation; /// /// Current camera velocity /// Vector3 m_CurrentCamVelocity; /// /// Current reusable floor plane /// Plane m_FloorPlane; public Plane floorPlane { get { return m_FloorPlane; } } /// /// Target position on the grid that we're looking at /// public Vector3 lookPosition { get; private set; } /// /// Current look position of camera /// public Vector3 currentLookPosition { get; private set; } /// /// Target position of the camera /// public Vector3 cameraPosition { get; private set; } /// /// Bounds of our look area, related to map size, zoom level and aspect ratio/screen size /// public Rect lookBounds { get; private set; } /// /// Gets our current zoom distance /// public float zoomDist { get; private set; } /// /// Gets our current internal zoom distance, before clamping and scaling is applied /// public float rawZoomDist { get; private set; } /// /// Gets the unit we're tracking if any /// public GameObject trackingObject { get; private set; } /// /// Cached camera component /// public UnityEngine.Camera cachedCamera { get; private set; } /// /// Initialize references and floor plane /// protected virtual void Awake() { cachedCamera = GetComponent(); m_FloorPlane = new Plane(Vector3.up, new Vector3(0.0f, floorY, 0.0f)); // Set initial values var lookRay = new Ray(cachedCamera.transform.position, cachedCamera.transform.forward); float dist; if (m_FloorPlane.Raycast(lookRay, out dist)) { currentLookPosition = lookPosition = lookRay.GetPoint(dist); } cameraPosition = cachedCamera.transform.position; m_MinZoomRotation = Quaternion.FromToRotation(Vector3.up, -cachedCamera.transform.forward); m_MaxZoomRotation = Quaternion.FromToRotation(Vector3.up, -zoomedCamAngle.transform.forward); rawZoomDist = zoomDist = (currentLookPosition - cameraPosition).magnitude; } /// /// Setup initial zoom level and camera bounds /// protected virtual void Start() { RecalculateBoundingRect(); } /// /// Handle camera behaviour /// protected virtual void Update() { RecalculateBoundingRect(); // Tracking? if (trackingObject != null) { PanTo(trackingObject.transform.position); if (!trackingObject.activeInHierarchy) { StopTracking(); } } // Approach look position currentLookPosition = Vector3.SmoothDamp(currentLookPosition, lookPosition, ref m_CurrentLookVelocity, lookDampFactor); Vector3 worldPos = transform.position; worldPos = Vector3.SmoothDamp(worldPos, cameraPosition, ref m_CurrentCamVelocity, movementDampFactor); transform.position = worldPos; transform.LookAt(currentLookPosition); } #if UNITY_EDITOR /// /// Debug bounds area gizmo /// void OnDrawGizmosSelected() { // We dont want to display this in edit mode if (!Application.isPlaying) { return; } if (cachedCamera == null) { cachedCamera = GetComponent(); } RecalculateBoundingRect(); Gizmos.color = Color.red; Gizmos.DrawLine( new Vector3(lookBounds.xMin, 0.0f, lookBounds.yMin), new Vector3(lookBounds.xMax, 0.0f, lookBounds.yMin)); Gizmos.DrawLine( new Vector3(lookBounds.xMin, 0.0f, lookBounds.yMin), new Vector3(lookBounds.xMin, 0.0f, lookBounds.yMax)); Gizmos.DrawLine( new Vector3(lookBounds.xMax, 0.0f, lookBounds.yMax), new Vector3(lookBounds.xMin, 0.0f, lookBounds.yMax)); Gizmos.DrawLine( new Vector3(lookBounds.xMax, 0.0f, lookBounds.yMax), new Vector3(lookBounds.xMax, 0.0f, lookBounds.yMin)); Gizmos.color = Color.yellow; Gizmos.DrawLine(transform.position, currentLookPosition); } #endif /// /// Pans the camera to a specific position /// /// The look target public void PanTo(Vector3 position) { Vector3 pos = position; // Look position is floor height pos.y = floorY; // Clamp to look bounds pos.x = Mathf.Clamp(pos.x, lookBounds.xMin, lookBounds.xMax); pos.z = Mathf.Clamp(pos.z, lookBounds.yMin, lookBounds.yMax); lookPosition = pos; // Camera position calculated from look position with view vector and zoom dist cameraPosition = lookPosition + (GetToCamVector() * zoomDist); } /// /// Cause the camera to follow a unit /// /// public void TrackObject(GameObject objectToTrack) { trackingObject = objectToTrack; PanTo(trackingObject.transform.position); } /// /// Stop tracking a unit /// public void StopTracking() { trackingObject = null; } /// /// Pan the camera /// /// How far to pan the camera, in world space units public void PanCamera(Vector3 panDelta) { // River mod: ÓÃÓÚÏÞÖÆÊó±êÒÆ¶¯µ½ÆÁÄ»±ßÔµºóÏà»úµÄÒÆ¶¯¡£ return; /* Vector3 pos = lookPosition; pos += panDelta; // Clamp to look bounds pos.x = Mathf.Clamp(pos.x, lookBounds.xMin, lookBounds.xMax); pos.z = Mathf.Clamp(pos.z, lookBounds.yMin, lookBounds.yMax); lookPosition = pos; // Camera position calculated from look position with view vector and zoom dist cameraPosition = lookPosition + (GetToCamVector() * zoomDist); */ } /// /// Zoom the camera by a specified value /// /// How far to zoom the camera public void ZoomCameraRelative(float zoomDelta) { SetZoom(rawZoomDist + zoomDelta); } /// /// Zoom the camera to a specified value /// /// The absolute zoom value public void SetZoom(float newZoom) { if (springyZoom) { rawZoomDist = newZoom; if (newZoom > furthestZoom) { zoomDist = furthestZoom; zoomDist += Mathf.Log((Mathf.Min(rawZoomDist, maxZoom) - furthestZoom) + 1, zoomLogFactor); } else if (rawZoomDist < nearestZoom) { zoomDist = nearestZoom; zoomDist -= Mathf.Log((nearestZoom - rawZoomDist) + 1, zoomLogFactor); } else { zoomDist = rawZoomDist; } } else { zoomDist = rawZoomDist = Mathf.Clamp(newZoom, nearestZoom, furthestZoom); } // Update bounding rectangle, which is based on our zoom level RecalculateBoundingRect(); // Force recalculated CameraPosition PanCamera(Vector3.zero); } /// /// Calculates the ray for a specified pointer in 3d space /// /// The pointer info /// The ray representing a screen-space pointer in 3D space public Ray GetRayForPointer(PointerInfo pointer) { return cachedCamera.ScreenPointToRay(pointer.currentPosition); } /// /// Gets the screen position of a given world position /// /// The world position /// The screen position of that point public Vector3 GetScreenPos(Vector3 worldPos) { return cachedCamera.WorldToScreenPoint(worldPos); } /// /// Decay the zoom if it's beyond its zoom limits, for springiness /// public void ZoomDecay() { if (springyZoom) { if (rawZoomDist > furthestZoom) { float recover = rawZoomDist - furthestZoom; SetZoom(Mathf.Max(furthestZoom, rawZoomDist - (recover * zoomRecoverSpeed * Time.deltaTime))); } else if (rawZoomDist < nearestZoom) { float recover = nearestZoom - rawZoomDist; SetZoom(Mathf.Min(nearestZoom, rawZoomDist + (recover * zoomRecoverSpeed * Time.deltaTime))); } } } /// /// Returns our normalized zoom ratio /// public float CalculateZoomRatio() { return Mathf.Clamp01(Mathf.InverseLerp(nearestZoom, furthestZoom, zoomDist)); } /// /// Gets the to camera vector based on our current zoom level /// Vector3 GetToCamVector() { float t = Mathf.Clamp01((zoomDist - nearestZoom) / (furthestZoom - nearestZoom)); t = 1 - ((1 - t) * (1 - t)); Quaternion interpolatedRotation = Quaternion.Slerp( m_MaxZoomRotation, m_MinZoomRotation, t); return interpolatedRotation * Vector3.up; } /// /// Update the size of our camera's bounding rectangle /// void RecalculateBoundingRect() { Rect mapsize = mapSize; // Get some world space projections at this zoom level // Temporarily move camera to final look position Vector3 prevCameraPos = transform.position; transform.position = cameraPosition; transform.LookAt(lookPosition); // Project screen corners and center var bottomLeftScreen = new Vector3(0, 0); var topLeftScreen = new Vector3(0, Screen.height); var centerScreen = new Vector3(Screen.width * 0.5f, Screen.height * 0.5f); Vector3 bottomLeftWorld = Vector3.zero; Vector3 topLeftWorld = Vector3.zero; Vector3 centerWorld = Vector3.zero; float dist; Ray ray = cachedCamera.ScreenPointToRay(bottomLeftScreen); if (m_FloorPlane.Raycast(ray, out dist)) { bottomLeftWorld = ray.GetPoint(dist); } ray = cachedCamera.ScreenPointToRay(topLeftScreen); if (m_FloorPlane.Raycast(ray, out dist)) { topLeftWorld = ray.GetPoint(dist); } ray = cachedCamera.ScreenPointToRay(centerScreen); if (m_FloorPlane.Raycast(ray, out dist)) { centerWorld = ray.GetPoint(dist); } Vector3 toTopLeft = topLeftWorld - centerWorld; Vector3 toBottomLeft = bottomLeftWorld - centerWorld; lookBounds = new Rect( mapsize.xMin - toBottomLeft.x, mapsize.yMin - toBottomLeft.z, Mathf.Max(mapsize.width + (toBottomLeft.x * 2), 0), Mathf.Max((mapsize.height - toTopLeft.z) + toBottomLeft.z, 0)); // Restore camera position transform.position = prevCameraPos; transform.LookAt(currentLookPosition); // River: ¶¯Ì¬µÄ´¦ÀíViewPortRect. float vpscale = 1920f / Screen.height; Rect vpRect = new Rect(); vpRect.x = 0.0f; vpRect.y = (1.0f-vpscale)/2.0f; vpRect.width = 1f; vpRect.height = vpscale; cachedCamera.rect = vpRect; } } }