支持:鼠标左键拖拽平移、右键拖拽环绕旋转、中键拖拽自转、滚轮拉近拉远、WASD移动、QE升降 直接挂载相机即可使用
代码如下:
c#
using UnityEngine;
using UnityEngine.EventSystems;
/// <summary>
/// 多功能相机控制器
/// 支持:鼠标左键拖拽平移、右键拖拽环绕旋转、中键拖拽自转、滚轮拉近拉远、WASD移动、QE升降
/// </summary>
public class CameraController : MonoBehaviour
{
[Header("移动速度")]
[SerializeField] private float moveSpeed = 200f; // WASD移动速度
[SerializeField] private float mouseMoveSpeed = 100f; // 左键拖拽移动速度系数
[SerializeField] private float qeSpeed = 3f; // QE升降速度
[Header("旋转速度")]
[SerializeField] private float rotateSpeed = 4f; // 右键环绕旋转速度
[SerializeField] private float selfRotateSpeed = 200f; // 中键自转速度
[Header("环绕参数")]
[SerializeField] private float focusDistance = 15f; // 右键旋转时的环绕半径(焦点距离)
[SerializeField] private float minFocusDistance = 1f; // 最小环绕距离
[SerializeField] private float maxFocusDistance = 20f; // 最大环绕距离
[Header("滚轮参数")]
[SerializeField] private float zoomSpeed = 600f; // 滚轮拉近拉远速度
[SerializeField] private float minZoomDistance = 0.5f; // 非环绕模式下最小距离(避免穿过物体)
[SerializeField] private float maxZoomDistance = 50f; // 非环绕模式下最大距离
// 右键拖拽状态
private bool isRightDragging = false;
private Vector3 dragFocusPoint; // 拖拽过程中的固定焦点
private float dragDistance; // 相机到焦点的距离
private float dragAngleX; // 水平角度(绕Y轴)
private float dragAngleY; // 垂直角度(俯仰)
void Update()
{
//判断如果鼠标在UI上不可以操作
if (EventSystem.current.IsPointerOverGameObject() == true) return;
// 右键拖拽(优先级最高,避免与其他操作冲突)
if (Input.GetMouseButtonDown(1))
{
StartRightDrag();
}
else if (Input.GetMouseButton(1))
{
UpdateRightDrag();
}
else if (Input.GetMouseButtonUp(1))
{
EndRightDrag();
}
// 当未进行右键拖拽时,处理其他输入
if (!isRightDragging)
{
// 左键拖拽平移
if (Input.GetMouseButton(0))
{
HandleLeftDrag();
}
// 中键自转(第一人称式旋转)
if (Input.GetMouseButton(2))
{
HandleMiddleDrag();
}
// 键盘移动(WASD + QE)
HandleKeyboardMovement();
// 滚轮拉近拉远(非环绕模式)
HandleMouseWheel();
}
else
{
// 右键拖拽中,滚轮用于改变环绕半径
HandleMouseWheelInOrbit();
}
}
/// <summary>
/// 开始右键拖拽:记录初始焦点和角度
/// </summary>
private void StartRightDrag()
{
isRightDragging = true;
// 计算当前焦点(相机前方 focusDistance 米处的点)
dragFocusPoint = transform.position + transform.forward * focusDistance;
dragDistance = Vector3.Distance(transform.position, dragFocusPoint);
// 计算相机相对于焦点的方向向量,并转换为球坐标角度
Vector3 direction = (transform.position - dragFocusPoint).normalized;
dragAngleX = Mathf.Atan2(direction.x, direction.z) * Mathf.Rad2Deg; // 水平角(-180 到 180)
dragAngleY = Mathf.Asin(direction.y) * Mathf.Rad2Deg; // 俯仰角(-90 到 90)
}
/// <summary>
/// 更新右键拖拽:根据鼠标移动调整角度,重新计算相机位置
/// </summary>
private void UpdateRightDrag()
{
// 获取鼠标增量
float mouseX = Input.GetAxis("Mouse X") * rotateSpeed;
float mouseY = Input.GetAxis("Mouse Y") * rotateSpeed;
// 更新角度
dragAngleX += mouseX;
dragAngleY -= mouseY;
dragAngleY = Mathf.Clamp(dragAngleY, -89f, 89f); // 限制俯仰角,避免翻转
// 根据球坐标计算新方向
float radX = dragAngleX * Mathf.Deg2Rad;
float radY = dragAngleY * Mathf.Deg2Rad;
Vector3 direction = new Vector3(
Mathf.Sin(radX) * Mathf.Cos(radY),
Mathf.Sin(radY),
Mathf.Cos(radX) * Mathf.Cos(radY)
);
// 更新相机位置和朝向
transform.position = dragFocusPoint + direction * dragDistance;
transform.LookAt(dragFocusPoint);
}
/// <summary>
/// 结束右键拖拽:清理状态
/// </summary>
private void EndRightDrag()
{
isRightDragging = false;
}
/// <summary>
/// 左键拖拽平移:沿水平面移动相机
/// </summary>
private void HandleLeftDrag()
{
// 获取鼠标增量
float mouseX = Input.GetAxis("Mouse X") * mouseMoveSpeed * Time.deltaTime;
float mouseY = Input.GetAxis("Mouse Y") * mouseMoveSpeed * Time.deltaTime;
// 计算移动方向:相机右向和水平前向
Vector3 right = transform.right;
Vector3 forward = Vector3.ProjectOnPlane(transform.forward, Vector3.up).normalized;
// 如果前向接近垂直,使用世界前向作为后备
if (forward.magnitude < 0.01f)
forward = Vector3.forward;
// 应用移动
Vector3 delta = (right * -mouseX + forward * -mouseY);
transform.position += delta;
}
/// <summary>
/// 中键拖拽自转:控制相机的上下左右旋转(俯仰和偏航),位置不变
/// </summary>
private void HandleMiddleDrag()
{
float mouseX = Input.GetAxis("Mouse X") * selfRotateSpeed * Time.deltaTime;
float mouseY = Input.GetAxis("Mouse Y") * selfRotateSpeed * Time.deltaTime;
// 获取当前欧拉角
Vector3 currentEuler = transform.eulerAngles;
// 更新偏航(Yaw)和俯仰(Pitch)
currentEuler.y += mouseX;
currentEuler.x -= mouseY;
// 限制俯仰角在 -89 到 89 度之间,避免翻转
currentEuler.x = ClampAngle(currentEuler.x, -89f, 89f);
// 保持Z轴为0,避免滚转
currentEuler.z = 0f;
transform.eulerAngles = currentEuler;
}
/// <summary>
/// 键盘移动:WASD水平移动,QE升降
/// </summary>
private void HandleKeyboardMovement()
{
// 获取WASD输入
float horizontal = Input.GetAxis("Horizontal")*0.1f;
float vertical = Input.GetAxis("Vertical")*0.1f;
// QE升降输入
float upDown = 0f;
if (Input.GetKey(KeyCode.Q))
upDown = -1f;
else if (Input.GetKey(KeyCode.E))
upDown = 1f;
// 如果没有输入,直接返回
if (Mathf.Approximately(horizontal, 0f) && Mathf.Approximately(vertical, 0f) && Mathf.Approximately(upDown, 0f))
return;
// 计算移动方向(保持水平移动,不影响高度)
Vector3 right = transform.right;
Vector3 forward = Vector3.ProjectOnPlane(transform.forward, Vector3.up).normalized;
if (forward.magnitude < 0.01f)
forward = Vector3.forward;
// 应用水平移动
Vector3 moveDelta = (right * horizontal + forward * vertical) * moveSpeed * Time.deltaTime;
transform.position += moveDelta;
// 应用升降移动(沿世界Y轴)
if (!Mathf.Approximately(upDown, 0f))
{
transform.position += Vector3.up * upDown * qeSpeed * Time.deltaTime;
}
}
/// <summary>
/// 非环绕模式下的滚轮拉近拉远:沿相机前向移动
/// </summary>
private void HandleMouseWheel()
{
float scroll = Input.GetAxis("Mouse ScrollWheel");
if (Mathf.Approximately(scroll, 0f))
return;
// 沿前向移动的距离
float delta = scroll * zoomSpeed * Time.deltaTime;
Vector3 newPos = transform.position + transform.forward * delta;
// 可选:限制相机与场景中心或地面的距离,这里简单限制一下高度范围(示例)
// 根据实际需要可调整或移除
// 限制 Y 轴范围(避免埋入地下或飞太高)
float clampedY = Mathf.Clamp(newPos.y, 0.5f, 100f);
newPos.y = clampedY;
transform.position = newPos;
}
/// <summary>
/// 环绕模式下的滚轮:改变相机到焦点的距离
/// </summary>
private void HandleMouseWheelInOrbit()
{
float scroll = Input.GetAxis("Mouse ScrollWheel");
if (Mathf.Approximately(scroll, 0f))
return;
// 更新距离
dragDistance -= scroll * zoomSpeed; // 滚轮向上为正,拉近(减少距离)
dragDistance = Mathf.Clamp(dragDistance, minFocusDistance, maxFocusDistance);
// 根据当前角度重新计算方向
float radX = dragAngleX * Mathf.Deg2Rad;
float radY = dragAngleY * Mathf.Deg2Rad;
Vector3 direction = new Vector3(
Mathf.Sin(radX) * Mathf.Cos(radY),
Mathf.Sin(radY),
Mathf.Cos(radX) * Mathf.Cos(radY)
);
// 更新相机位置
transform.position = dragFocusPoint + direction * dragDistance;
transform.LookAt(dragFocusPoint);
}
/// <summary>
/// 将角度限制在 [-180,180] 范围内并 clamp 到指定区间
/// </summary>
private float ClampAngle(float angle, float min, float max)
{
if (angle < -180f) angle += 360f;
if (angle > 180f) angle -= 360f;
return Mathf.Clamp(angle, min, max);
}
}
有需要会继续补充的