640 lines
19 KiB
C#
640 lines
19 KiB
C#
using System.Collections;
|
||
using UnityEngine;
|
||
using DG.Tweening;
|
||
using UnityEngine.EventSystems;
|
||
|
||
[RequireComponent(typeof(Camera))]
|
||
/// <summary>
|
||
/// 摄像机控制脚本
|
||
/// </summary>
|
||
public class CameraOrbit : CameraBase
|
||
{
|
||
[Header("开关")]
|
||
[Tooltip("是否可控.关闭则无法控制摄像机")]
|
||
public bool controlable = true;
|
||
|
||
[HideInInspector]
|
||
/// <summary>
|
||
/// 是否初始化
|
||
/// </summary>
|
||
public bool hasInit = false;
|
||
|
||
[Header("是否可以旋转")]
|
||
/// <summary>
|
||
/// 是否可以旋转
|
||
/// </summary>
|
||
public bool allowRotate = true;
|
||
|
||
[Header("是否可以平移")]
|
||
/// <summary>
|
||
/// 是否可以平移
|
||
/// </summary>
|
||
public bool allowPan = true;
|
||
|
||
[Header("是否双击定位")]
|
||
/// <summary>
|
||
/// 是否双击定位
|
||
/// </summary>
|
||
public bool allLocate = true;
|
||
|
||
[Header("是否自动旋转")]
|
||
/// <summary>
|
||
/// 是否自动旋转
|
||
/// </summary>
|
||
private bool autoRotate = false;
|
||
|
||
[Header("Movement")]
|
||
public CameraMovementType movementType = CameraMovementType.Normal;
|
||
|
||
[Header("旋转按键")]
|
||
public CameraMouseInputType rotateInputKey = CameraMouseInputType.LeftMouse;
|
||
|
||
[Header("平移按键")]
|
||
public CameraMouseInputType panInputKey = CameraMouseInputType.RightMouse;
|
||
|
||
[Header("观察的目标")]
|
||
public Vector3 target;
|
||
|
||
public Transform targetTrans;
|
||
|
||
[Header("摄像机与目标之间的距离")]
|
||
public float Distance = 150f;
|
||
|
||
[Header("限制")]
|
||
[Tooltip("摄像机与目标之间的距离限制")]
|
||
public Vector2 distanceClamp = new Vector2(1, 1000);
|
||
public Vector2 yLimitClamp = new Vector2(-20, 90);
|
||
public Vector2 xLimitClamp = new Vector2(360, 360);
|
||
public Vector2 yHeightLimitClamp = new Vector2(-Mathf.Infinity, Mathf.Infinity);
|
||
|
||
[Header("控制参数")]
|
||
[Header("鼠标滑动灵敏度")]
|
||
[Range(0.01f, 10)]
|
||
public float axisSensitivity = 3;
|
||
[Range(1, 100)]
|
||
public float axisLerp = 10;
|
||
[Header("鼠标滚轮灵敏度")]
|
||
[Range(0.001f, 10)]
|
||
float scrollSensitivity = 0.02f;
|
||
[Range(1, 100)]
|
||
float zoomLerp = 20;
|
||
[Header("相机平移灵敏度")]
|
||
[Range(0.01f, 10)]
|
||
public float panSensitivity = 1f;
|
||
[Header("相机自动旋转速度")]
|
||
[Range(0.01f, 10)]
|
||
public float autoRotateSpeed = 2f;
|
||
[Range(1, 100)]
|
||
public float panLerp = 15;
|
||
[Range(1, 100)]
|
||
private float lerpSpeed = 5;
|
||
|
||
[Header("切换目标时黑屏时间")]
|
||
[Range(0.01f, 10)]
|
||
public float swichtSpeed = 1;
|
||
|
||
public Ease ease = Ease.Linear;
|
||
|
||
private float x, y;
|
||
private float horizontal;
|
||
private float vertical;
|
||
private float distance = 0;
|
||
private Vector3 panOffset;
|
||
private Vector3 zoomVector;
|
||
private Vector3 currentPosition;
|
||
private Quaternion currentRotation;
|
||
private float initXRotation;
|
||
private bool isSwitchingTarget = false;
|
||
private float fadeAlpha = 0;
|
||
private bool control = true;
|
||
[SerializeField]
|
||
private Texture2D fadeTexture = null;
|
||
|
||
public bool Controlable
|
||
{
|
||
get { return controlable; }
|
||
set { controlable = value; }
|
||
}
|
||
|
||
private void Awake()
|
||
{
|
||
Init();
|
||
}
|
||
|
||
private void LateUpdate()
|
||
{
|
||
target = targetTrans.position;
|
||
Detection();
|
||
|
||
if (!controlable) return;
|
||
|
||
if (isSwitchingTarget)
|
||
return;
|
||
|
||
if (autoRotate)
|
||
{
|
||
AutoRotate();
|
||
}
|
||
|
||
if (control)
|
||
{
|
||
if (allowRotate)
|
||
{
|
||
ZoomControl(false);
|
||
OrbitControl();
|
||
}
|
||
else
|
||
{
|
||
ZoomControl(true);
|
||
}
|
||
|
||
if (allowPan)
|
||
{
|
||
PanControl();
|
||
}
|
||
}
|
||
|
||
if (allLocate)
|
||
{
|
||
//DclickLocate();
|
||
}
|
||
|
||
target = LimitHeight(target);
|
||
}
|
||
/// <summary>
|
||
/// 初始化
|
||
/// </summary>
|
||
private void Init()
|
||
{
|
||
distance = Distance;
|
||
Vector3 eulerAngles = Transform.eulerAngles;
|
||
x = eulerAngles.y;
|
||
y = eulerAngles.x;
|
||
initXRotation = eulerAngles.y;
|
||
horizontal = x;
|
||
vertical = y;
|
||
hasInit = true;
|
||
}
|
||
/// <summary>
|
||
/// 旋转控制
|
||
/// </summary>
|
||
private void OrbitControl()
|
||
{
|
||
if (controlable)
|
||
{
|
||
if (IsInputKeyRotate)
|
||
{
|
||
horizontal += (axisSensitivity) * AxisX;
|
||
vertical -= (axisSensitivity) * AxisY;
|
||
}
|
||
}
|
||
float delta = Time.deltaTime;
|
||
vertical = ClampAngle(vertical, yLimitClamp.x, yLimitClamp.y);
|
||
if (xLimitClamp.x < 360 && xLimitClamp.y < 360)
|
||
{
|
||
horizontal = ClampAngle(horizontal, (initXRotation - xLimitClamp.y), (xLimitClamp.x + initXRotation));
|
||
}
|
||
x = Mathf.Lerp(x, horizontal, Time.deltaTime * axisLerp);
|
||
y = Mathf.Lerp(y, vertical, Time.deltaTime * axisLerp);
|
||
y = ClampAngle(y, yLimitClamp.x, yLimitClamp.y);
|
||
currentRotation = Quaternion.Euler(y, x, 0f);
|
||
currentPosition = (currentRotation * zoomVector) + target;
|
||
switch (movementType)
|
||
{
|
||
case CameraMovementType.Dynamic:
|
||
Transform.position = Vector3.Lerp(Transform.position, currentPosition, (lerpSpeed) * delta);
|
||
Transform.rotation = Quaternion.Lerp(Transform.rotation, currentRotation, (lerpSpeed * 2) * delta);
|
||
break;
|
||
case CameraMovementType.Normal:
|
||
Transform.position = currentPosition;
|
||
Transform.rotation = currentRotation;
|
||
break;
|
||
case CameraMovementType.Towars:
|
||
Transform.position = Vector3.MoveTowards(Transform.position, currentPosition, lerpSpeed);
|
||
Transform.rotation = Quaternion.RotateTowards(Transform.rotation, currentRotation, lerpSpeed);
|
||
break;
|
||
}
|
||
}
|
||
/// <summary>
|
||
/// 距离控制
|
||
/// </summary>
|
||
/// <param name="autoApply"></param>
|
||
private void ZoomControl(bool autoApply)
|
||
{
|
||
float delta = Time.deltaTime;
|
||
distance = Mathf.Clamp(distance - (MouseScrollWheel * scrollSensitivity * 10), distanceClamp.x, distanceClamp.y);
|
||
distance = (distance < 1) ? 1 : distance;
|
||
Distance = Mathf.SmoothStep(Distance, distance, delta * zoomLerp);
|
||
zoomVector = new Vector3(0, 0, -Distance);
|
||
if (autoApply)
|
||
{
|
||
currentPosition = (currentRotation * zoomVector) + target;
|
||
switch (movementType)
|
||
{
|
||
case CameraMovementType.Dynamic:
|
||
Transform.position = Vector3.Lerp(Transform.position, currentPosition, (lerpSpeed) * delta);
|
||
Transform.rotation = Quaternion.Lerp(Transform.rotation, currentRotation, (lerpSpeed * 2) * delta);
|
||
break;
|
||
case CameraMovementType.Normal:
|
||
Transform.position = currentPosition;
|
||
Transform.rotation = currentRotation;
|
||
break;
|
||
case CameraMovementType.Towars:
|
||
Transform.position = Vector3.MoveTowards(Transform.position, currentPosition, lerpSpeed);
|
||
Transform.rotation = Quaternion.RotateTowards(Transform.rotation, currentRotation, lerpSpeed);
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
/// <summary>
|
||
/// 平移控制
|
||
/// </summary>
|
||
private void PanControl()
|
||
{
|
||
if (IsInputKeyPan)
|
||
{
|
||
Vector3 offset;
|
||
if (Input.GetKey(KeyCode.LeftControl))
|
||
{
|
||
offset = -transform.right.normalized * AxisX + transform.forward.normalized * AxisY;
|
||
}
|
||
else
|
||
{
|
||
offset = -transform.right.normalized * AxisX - transform.up.normalized * AxisY;
|
||
}
|
||
#if !UNITY_EDITOR
|
||
offset = new Vector3(offset.x, 0, offset.z);
|
||
#endif
|
||
panOffset = Vector3.Lerp(panOffset, offset, panLerp * Time.deltaTime);
|
||
}
|
||
else
|
||
{
|
||
panOffset = Vector3.Lerp(panOffset, Vector3.zero, panLerp * Time.deltaTime);
|
||
}
|
||
target += panOffset * panSensitivity * 10 * GetZoom();
|
||
}
|
||
|
||
/// <summary>
|
||
/// 控制检测,按下的一瞬间是否在UI上,是的话无法控制相机
|
||
/// </summary>
|
||
private void Detection()
|
||
{
|
||
if (Input.GetMouseButtonUp(0) || Input.GetMouseButtonUp(1) || Input.GetMouseButtonUp(2))
|
||
{
|
||
control = true;
|
||
}
|
||
if (Input.GetMouseButtonDown(0) || Input.GetMouseButtonDown(1) || Input.GetMouseButtonDown(2))
|
||
{
|
||
if (EventSystem.current.IsPointerOverGameObject())
|
||
{
|
||
control = false;
|
||
}
|
||
}
|
||
}
|
||
|
||
private bool IsInputKeyRotate
|
||
{
|
||
get
|
||
{
|
||
switch (rotateInputKey)
|
||
{
|
||
case CameraMouseInputType.All:
|
||
return (Input.GetKey(KeyCode.Mouse0) || Input.GetKey(KeyCode.Mouse1) || Input.GetKey(KeyCode.Mouse2) || Input.GetMouseButton(0));
|
||
case CameraMouseInputType.LeftAndRight:
|
||
return (Input.GetKey(KeyCode.Mouse0) || Input.GetKey(KeyCode.Mouse1));
|
||
case CameraMouseInputType.LeftMouse:
|
||
return (Input.GetKey(KeyCode.Mouse0));
|
||
case CameraMouseInputType.RightMouse:
|
||
return (Input.GetKey(KeyCode.Mouse1));
|
||
case CameraMouseInputType.MouseScroll:
|
||
return (Input.GetKey(KeyCode.Mouse2));
|
||
case CameraMouseInputType.MobileTouch:
|
||
return (Input.GetMouseButton(0) || Input.GetMouseButton(1));
|
||
default:
|
||
return (Input.GetKey(KeyCode.Mouse0));
|
||
}
|
||
}
|
||
}
|
||
private bool IsInputKeyPan
|
||
{
|
||
get
|
||
{
|
||
switch (panInputKey)
|
||
{
|
||
case CameraMouseInputType.All:
|
||
return (Input.GetKey(KeyCode.Mouse0) || Input.GetKey(KeyCode.Mouse1) || Input.GetKey(KeyCode.Mouse2) || Input.GetMouseButton(0));
|
||
case CameraMouseInputType.LeftAndRight:
|
||
return (Input.GetKey(KeyCode.Mouse0) || Input.GetKey(KeyCode.Mouse1));
|
||
case CameraMouseInputType.LeftMouse:
|
||
return (Input.GetKey(KeyCode.Mouse0));
|
||
case CameraMouseInputType.RightMouse:
|
||
return (Input.GetKey(KeyCode.Mouse1));
|
||
case CameraMouseInputType.MouseScroll:
|
||
return (Input.GetKey(KeyCode.Mouse2));
|
||
case CameraMouseInputType.MobileTouch:
|
||
return (Input.GetMouseButton(0) || Input.GetMouseButton(1));
|
||
default:
|
||
return (Input.GetKey(KeyCode.Mouse0));
|
||
}
|
||
}
|
||
}
|
||
public void SetTarget(Vector3 newTarget)
|
||
{
|
||
newTarget = LimitHeight(newTarget);
|
||
target = newTarget;
|
||
}
|
||
|
||
public void SetTargetLerp(Vector3 newTarget)
|
||
{
|
||
newTarget = LimitHeight(newTarget);
|
||
DOTween.To(() => target, x => target = x, newTarget, 0.2f).SetEase(ease);
|
||
}
|
||
public void SetTarget(Transform newTarget)
|
||
{
|
||
StopCoroutine("TranslateTarget");
|
||
StartCoroutine("TranslateTarget", newTarget);
|
||
}
|
||
private IEnumerator TranslateTarget(Transform newTarget)
|
||
{
|
||
isSwitchingTarget = true;
|
||
while (fadeAlpha < 1)
|
||
{
|
||
transform.position = Vector3.Lerp(transform.position, transform.position + new Vector3(0, 2, -2), Time.deltaTime);
|
||
fadeAlpha += Time.smoothDeltaTime * swichtSpeed;
|
||
yield return null;
|
||
}
|
||
target = newTarget.position;
|
||
isSwitchingTarget = false;
|
||
while (fadeAlpha > 0)
|
||
{
|
||
fadeAlpha -= Time.smoothDeltaTime * swichtSpeed;
|
||
yield return null;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 双击定位
|
||
/// </summary>
|
||
private void DclickLocate()
|
||
{
|
||
if (EventSystem.current.IsPointerOverGameObject()) return;
|
||
//if (Inputs.Key.DoubleClick(KeyCode.Mouse0, 0.2f))
|
||
//{
|
||
// Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
|
||
// if (Physics.Raycast(ray, out RaycastHit hit))
|
||
// {
|
||
// SetTargetLerp(hit.point);
|
||
// }
|
||
//}
|
||
}
|
||
|
||
/// <summary>
|
||
/// 限制目标点高度
|
||
/// </summary>
|
||
private Vector3 LimitHeight(Vector3 _target)
|
||
{
|
||
return new Vector3(_target.x, Mathf.Clamp(_target.y, yHeightLimitClamp.x, yHeightLimitClamp.y), _target.z);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 设置九个方向朝向
|
||
/// </summary>
|
||
/// <param name="Orient"></param>
|
||
public void SetViewPoint(CameraOrientation Orient = CameraOrientation.Forward)
|
||
{
|
||
switch (Orient)
|
||
{
|
||
case CameraOrientation.Forward:
|
||
vertical = 0; horizontal = 0;
|
||
break;
|
||
case CameraOrientation.Behind:
|
||
vertical = 0; horizontal = 180;
|
||
break;
|
||
case CameraOrientation.Left:
|
||
vertical = 0; horizontal = 90;
|
||
break;
|
||
case CameraOrientation.Right:
|
||
vertical = 0; horizontal = -90;
|
||
break;
|
||
case CameraOrientation.LeftForward:
|
||
vertical = 0; horizontal = -45;
|
||
break;
|
||
case CameraOrientation.RightForward:
|
||
vertical = 0; horizontal = -135;
|
||
break;
|
||
case CameraOrientation.LeftBehind:
|
||
vertical = 0; horizontal = 45;
|
||
break;
|
||
case CameraOrientation.RightBehind:
|
||
vertical = 0; horizontal = 135;
|
||
break;
|
||
case CameraOrientation.Top:
|
||
vertical = 90; horizontal = 0;
|
||
break;
|
||
}
|
||
}
|
||
/// <summary>
|
||
/// 镜头拉近拉远
|
||
/// </summary>
|
||
/// <param name="value"></param>
|
||
public void Zoom(float value, float timer = 0.5f)
|
||
{
|
||
float targetDis = distance + value;
|
||
DOTween.To(() => distance, x => distance = x, targetDis, timer).OnUpdate(() =>
|
||
{
|
||
distance = Mathf.Clamp(distance, distanceClamp.x, distanceClamp.y);
|
||
});
|
||
}
|
||
|
||
/// <summary>
|
||
///
|
||
/// </summary>
|
||
/// <param name="value"></param>
|
||
public void SetStaticZoom(float value)
|
||
{
|
||
distance += value;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 设置位置
|
||
/// </summary>
|
||
/// <param name="newTarget">注视点位置</param>
|
||
/// <param name="dis">与注视点距离</param>
|
||
/// <param name="angleX">竖直角度</param>
|
||
/// <param name="angleY">水平角度</param>
|
||
public void SetPosition(Vector3 newTarget, float dis, float angleX = 45, float angleY = 0, float timer = 0.2f)
|
||
{
|
||
newTarget = LimitHeight(newTarget);
|
||
target = newTarget;
|
||
distance += dis;
|
||
DOTween.To(() => distance, x => distance = x, dis, timer).OnUpdate(() =>
|
||
{
|
||
distance = Mathf.Clamp(distance, distanceClamp.x, distanceClamp.y);
|
||
});
|
||
horizontal = angleY;
|
||
vertical = angleX;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 定位摄像机到某个位置
|
||
/// </summary>
|
||
/// <param name="position"></param>
|
||
public void ResetPosition(Vector3 position, float timer = 0.2f)
|
||
{
|
||
float targetDis = Vector3.Distance(target, position);
|
||
DOTween.To(() => distance, x => distance = x, targetDis, timer).OnUpdate(() =>
|
||
{
|
||
distance = Mathf.Clamp(distance, distanceClamp.x, distanceClamp.y);
|
||
});
|
||
Vector3 tempPos = position - target;
|
||
|
||
double square = tempPos.x * tempPos.x + tempPos.z * tempPos.z;
|
||
double sqrt = System.Math.Sqrt(square);
|
||
float ratio = (float)sqrt / targetDis;
|
||
float deg = Mathf.Acos(ratio);
|
||
float angle = deg * Mathf.Rad2Deg;
|
||
vertical = angle * Mathf.Sign(tempPos.y);
|
||
|
||
square = targetDis * targetDis - tempPos.y * tempPos.y;
|
||
sqrt = System.Math.Sqrt(square);
|
||
if (sqrt == 0) sqrt = 0.001f;
|
||
ratio = tempPos.x / (float)sqrt;
|
||
deg = Mathf.Acos(ratio);
|
||
angle = 90 - deg * Mathf.Rad2Deg;
|
||
angle += 180;
|
||
if (tempPos.z < 0)
|
||
{
|
||
angle = 180 - angle;
|
||
}
|
||
horizontal = angle;
|
||
x %= 360;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 设置与目标之间的距离
|
||
/// </summary>
|
||
/// <param name="value"></param>
|
||
public void SetDistance(float value)
|
||
{
|
||
if (value <= 0)
|
||
{
|
||
value = 0;
|
||
}
|
||
distance = value;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 设置距离最大最小值
|
||
/// </summary>
|
||
/// <param name="min"></param>
|
||
/// <param name="max"></param>
|
||
public void SetDistanceClamp(float min = 1, float max = 10000)
|
||
{
|
||
distanceClamp = new Vector2(min, max);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 相机自动旋转
|
||
/// </summary>
|
||
private void AutoRotate()
|
||
{
|
||
horizontal -= autoRotateSpeed * Time.deltaTime;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 设置是否自动旋转
|
||
/// </summary>
|
||
/// <param name="rotate"></param>
|
||
public void SetAutoRotate(bool rotate, float speed = 2)
|
||
{
|
||
autoRotate = rotate;
|
||
autoRotateSpeed = speed;
|
||
}
|
||
|
||
/// <summary>
|
||
/// 获取整体缩放
|
||
/// </summary>
|
||
/// <returns></returns>
|
||
private float GetZoom()
|
||
{
|
||
return Distance / 100;
|
||
}
|
||
|
||
/// <summary>
|
||
///
|
||
/// </summary>
|
||
public float Horizontal
|
||
{
|
||
get
|
||
{
|
||
return horizontal;
|
||
}
|
||
set
|
||
{
|
||
horizontal += value;
|
||
}
|
||
}
|
||
|
||
/// <summary>
|
||
///
|
||
/// </summary>
|
||
public float Vertical
|
||
{
|
||
get
|
||
{
|
||
return vertical;
|
||
}
|
||
set
|
||
{
|
||
vertical += value;
|
||
}
|
||
}
|
||
private void OnGUI()
|
||
{
|
||
if (isSwitchingTarget)
|
||
{
|
||
GUI.color = new Color(1, 1, 1, fadeAlpha);
|
||
if (fadeTexture != null)
|
||
GUI.DrawTexture(new Rect(0, 0, Screen.width, Screen.height), fadeTexture, ScaleMode.StretchToFill);
|
||
return;
|
||
}
|
||
}
|
||
private void OnDestroy()
|
||
{
|
||
hasInit = false;
|
||
}
|
||
void OnDrawGizmosSelected()
|
||
{
|
||
Gizmos.color = new Color32(0, 221, 221, 255);
|
||
if (target != null)
|
||
{
|
||
Gizmos.DrawLine(transform.position, target);
|
||
Gizmos.matrix = Matrix4x4.TRS(target, transform.rotation, new Vector3(1f, 0, 1f));
|
||
Gizmos.DrawWireSphere(target, Distance);
|
||
Gizmos.matrix = Matrix4x4.identity;
|
||
}
|
||
Gizmos.DrawCube(transform.position, new Vector3(1, 0.2f, 0.2f));
|
||
Gizmos.DrawCube(transform.position, Vector3.one / 2);
|
||
}
|
||
|
||
/// <summary>
|
||
/// 限制角度
|
||
/// </summary>
|
||
/// <param name="angle"></param>
|
||
/// <param name="min"></param>
|
||
/// <param name="max"></param>
|
||
/// <returns></returns>
|
||
public static float ClampAngle(float angle, float min, float max)
|
||
{
|
||
if (angle < -360f)
|
||
{
|
||
angle += 360f;
|
||
}
|
||
if (angle > 360f)
|
||
{
|
||
angle -= 360f;
|
||
}
|
||
return Mathf.Clamp(angle, min, max);
|
||
}
|
||
}
|