ict.xunfei/Assets/DigitalHuman/Scripts/Orbit/CameraOrbit.cs

640 lines
19 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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