坐标轴暂时没想好怎么处理,因此先限制y方向的旋转角度
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Cinemachine;
//todo:实现基本功能的自由相机
public class FreeCamera : MonoBehaviour
{
[Tooltip("相机模式")]
public FreeCameraType freeCameraType;
public ButtonState buttonState;
[Tooltip("这里是虚拟相机")]
public CinemachineVirtualCamera m_camera;
[Tooltip("目标旋转中心")]
public Transform lookAroundTrans;
[Tooltip("相机旋转轴偏移")]
public Vector3 offestRotate;
[Tooltip("旋转灵敏度")]
public float rotateSenstivity = 1;
[Tooltip("自动旋转速度,正值是顺时针,负值是逆时针")]
public float autoRotateSpeed = 10;
/// <summary>
/// 相机到目标的距离
/// </summary>
public float distance = 5;
[Tooltip("滚轮速度")]
public float zoomSpeed;
[Tooltip("最小距离")]
public float minDistance = 0;
[Tooltip("最大距离")]
public float maxDistance = 10;
/// <summary>
/// 是否自动旋转
/// </summary>
private bool isAutoRotate;
/// <summary>
/// 旋转中心的位置
/// </summary>
private Vector3 lookAroundPos;
private float angle_x;
private float angle_y;
private float angle_z;
/// <summary>
/// 目标距离
/// </summary>
private float targetDistance;
/// <summary>
/// 初始角度
/// </summary>
private Vector3 rootAngle;
/// <summary>
/// 初始默认距离
/// </summary>
private float rootDistance;
/// <summary>
/// 初始位置
/// </summary>
private Vector3 rootpos;
/// <summary>
/// 上一个鼠标位置
/// </summary>
private Vector3 lastMousePos;
private void Awake()
{
//设定初始默认距离
rootDistance = distance;
rootpos = m_camera.transform.position;
}
private void Start()
{
Init();
}
private void Update()
{
UpdateInput();
LookAround();
Zoom();
if (freeCameraType == FreeCameraType.自动旋转相机)
AutoRotate();
if (freeCameraType == FreeCameraType.自由相机)
Drag();
ResetlookAroundPos();
SetCameraPos();
}
private void UpdateInput()
{
buttonState.rightButtonDown = Input.GetMouseButtonDown(1);
buttonState.rightButton = Input.GetMouseButton(1);
buttonState.rightButtonUp = Input.GetMouseButtonUp(1);
buttonState.middlebuttonDown = Input.GetMouseButtonDown(2);
buttonState.middlebutton = Input.GetMouseButton(2);
buttonState.middleButtonUp = Input.GetMouseButtonUp(2);
}
/// <summary>
/// 初始化
/// </summary>
public void Init()
{
m_camera.transform.position = rootpos;
distance = rootDistance;
targetDistance = distance;
isAutoRotate = true;
ResetPos();
}
/// <summary>
/// 回到初始位置
/// </summary>
private void ResetPos()
{
if(lookAroundTrans!=null)
{
lookAroundPos = lookAroundTrans.position;
}
else
{
ResetlookAroundPos();
}
var forward = Vector3.Normalize(lookAroundPos - m_camera.transform.position);
var q = Quaternion.LookRotation(forward);
angle_x = q.eulerAngles.x;
angle_y = q.eulerAngles.y;
angle_x = Mathf.Clamp(angle_x, 10, 80);
angle_y = Mathf.Clamp(angle_y, -365, 365);
SetCameraPos();
}
/// <summary>
/// 重置中心点位置
/// </summary>
private void ResetlookAroundPos()
{
lookAroundPos = m_camera.transform.position + m_camera.transform.rotation * (Vector3.forward * distance);
}
/// <summary>
/// 相机位置变化应用
/// </summary>
public void SetCameraPos()
{
//距离差值
distance = Mathf.Lerp(distance, targetDistance, Time.deltaTime * 10);
m_camera.transform.rotation = Quaternion.Euler(angle_x, angle_y, angle_z);
m_camera.transform.position = lookAroundPos + m_camera.transform.rotation * (-distance * Vector3.forward);
m_camera.transform.rotation = Quaternion.Euler(angle_x, angle_y + offestRotate.y, angle_z);
}
/// <summary>
/// 旋转查看
/// </summary>
private void LookAround()
{
if (buttonState.rightButtonDown)
{
isAutoRotate = false;
angle_x = m_camera.transform.eulerAngles.x;
angle_y = m_camera.transform.eulerAngles.y - offestRotate.y;
angle_z = m_camera.transform.eulerAngles.z;
}
if (buttonState.rightButton)
{
float deltaY = Input.GetAxis("Mouse X");
float deltaX = Input.GetAxis("Mouse Y");
angle_x -= deltaX * rotateSenstivity;
angle_y += deltaY * rotateSenstivity;
angle_x = Mathf.Clamp(angle_x, 10, 80);
//不要给y轴限位
}
if (buttonState.rightButtonUp)
{
isAutoRotate = true;
}
}
/// <summary>
/// 滚轮滚动
/// </summary>
private void Zoom()
{
float scrollerValue = Input.GetAxis("Mouse ScrollWheel");
if (!IsEqual(scrollerValue, 0))
{
targetDistance -= scrollerValue * zoomSpeed;
targetDistance = Mathf.Clamp(targetDistance, minDistance, maxDistance);
}
}
/// <summary>
/// 自动旋转
/// </summary>
private void AutoRotate()
{
if (!isAutoRotate)
return;
angle_y += Time.deltaTime * autoRotateSpeed;
}
/// <summary>
/// 中键拖动相机移动
/// </summary>
private void Drag()
{
if (buttonState.middlebuttonDown)
{
lastMousePos = Input.mousePosition;
}
else if (buttonState.middlebutton)
{
Vector3 newMousePos = Input.mousePosition;
//获得一帧的相机移动
var delta = newMousePos - lastMousePos;
m_camera.transform.position = m_camera.transform.position - CalcuateDragNormal(m_camera, delta, distance);
lastMousePos = newMousePos;
}
}
/// <summary>
/// 计算拖动的位移
/// </summary>
/// <returns></returns>
private Vector3 CalcuateDragNormal(CinemachineVirtualCamera _camera, Vector3 _delta, float _distance)
{
//通过fov计算旋转中心在视锥截面上的高
var rect_height = 2 * _distance * Mathf.Tan(_camera.m_Lens.FieldOfView / 2 * Mathf.Deg2Rad);
//计算屏幕高与中心点截面高的比例,这样能计算出鼠标移动的距离对应在旋转点截面上移动的距离
var screenRate = rect_height / Screen.height;
var move_normal = screenRate * _delta.x * _camera.transform.right + screenRate * _delta.y * _camera.transform.up;
return move_normal;
}
/// <summary>
/// 是否约等于
/// </summary>
/// <returns></returns>
private bool IsEqual(float a, float b)
{
return Mathf.Abs(Mathf.Abs(a) - Mathf.Abs(b)) < 0.0001;
}
public struct ButtonState
{
public bool rightButtonDown;
public bool rightButton;
public bool rightButtonUp;
public bool middlebuttonDown;
public bool middlebutton;
public bool middleButtonUp;
}
public enum FreeCameraType
{
自由相机,
旋转相机,
自动旋转相机,
}
}