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;
}
}
}