Unity多功能相机控制器

支持:鼠标左键拖拽平移、右键拖拽环绕旋转、中键拖拽自转、滚轮拉近拉远、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);
    }
}

有需要会继续补充的

相关推荐
云边散步8 小时前
godot2D游戏教程系列二(21)
笔记·学习·游戏·游戏开发
fetasty1 天前
Godot游戏练习01-第18节-玩家死亡与复活,游戏失败检测
游戏开发
SmalBox1 天前
【节点】[SplitTextureTransform节点]原理解析与实际应用
unity3d·游戏开发·图形学
SmalBox2 天前
【节点】[SampleVirtualTexture节点]原理解析与实际应用
unity3d·游戏开发·图形学
fetasty2 天前
Godot游戏练习01-第17节-状态机管理的敌人
游戏开发
云边散步2 天前
godot2D游戏教程系列二(19)
笔记·学习·游戏·游戏开发
SmalBox4 天前
【节点】[SampleTexture3D节点]原理解析与实际应用
unity3d·游戏开发·图形学
JCHwa4 天前
UE5 GAS 源码深度解析 | 第2篇:AttributeSet 源码导读
游戏开发·unreal engine
SmalBox5 天前
【节点】[SampleTexture2DLOD节点]原理解析与实际应用
unity3d·游戏开发·图形学