Unity 导览相机实现:键鼠控制自由漫游(WASD 移动 + 右键旋转)

简介


在虚拟展厅、景区导览、3D 模型预览等场景中,导览相机 是核心交互组件。本文将实现一款轻量、易扩展的导览相机脚本,支持 WASD 移动、QE 升降、右键视角旋转,还可通过 Shift 键加速,适配编辑器 / 运行时环境,新手也能快速上手!

核心功能说明


操作方式 功能效果
W/S/A/D 相机前后左右移动(基于自身朝向)
Q/E 相机垂直升降(世界坐标系 Y 轴)
鼠标右键按住 + 鼠标拖动 旋转相机视角(水平 / 垂直)
Left Shift 按住 移动速度翻倍(加速漫游)

完整代码实现


cs 复制代码
using UnityEngine;

/// <summary>
/// 导览相机核心脚本
/// 功能:WASD移动 + QE升降 + 右键旋转 + Shift加速
/// 适用:虚拟导览、展厅漫游、3D模型预览等场景
/// </summary>
[DisallowMultipleComponent] // 禁止重复挂载
public class TourCamera : MonoBehaviour
{
    [Header("移动配置")]
    [Tooltip("基础移动速度(单位:米/秒)")]
    public float moveSpeed = 5.0f; // 建议默认值5,更贴合实际漫游体验
    [Tooltip("视角旋转速度(单位:度/秒)")]
    public float rotateSpeed = 90.0f;
    [Tooltip("Shift加速倍率")]
    public float shiftRate = 2.0f;

    [Header("旋转限制(可选扩展)")]
    [Tooltip("垂直旋转最小角度(防止低头过度)")]
    [Range(-89, 0)] public float minVerticalAngle = -60f;
    [Tooltip("垂直旋转最大角度(防止抬头过度)")]
    [Range(0, 89)] public float maxVerticalAngle = 60f;

    private Transform cameraTrans; // 相机Transform缓存
    private float currentVerticalAngle; // 记录垂直旋转角度(用于限制)
    private Vector3 moveDirection; // 最终移动方向向量

    /// <summary>
    /// 初始化(缓存组件,避免重复GetComponent)
    /// </summary>
    private void Start()
    {
        // 优先使用挂载对象的Transform,也可指定外部相机
        cameraTrans = gameObject.transform;
        // 初始化垂直旋转角度(基于相机初始欧拉角)
        currentVerticalAngle = cameraTrans.eulerAngles.x;
    }

    /// <summary>
    /// 每帧更新(处理输入和相机变换)
    /// </summary>
    private void Update()
    {
        // 仅在编辑器/运行时响应(可选:可移除限制,适配打包后)
        if (Application.isEditor || Application.isPlaying)
        {
            HandleInput(); // 处理键鼠输入
            UpdateCameraMovement(); // 更新相机位置
            UpdateCameraRotation(); // 更新相机旋转
        }
    }

    /// <summary>
    /// 处理所有输入事件(输入逻辑单独封装,便于维护)
    /// </summary>
    private void HandleInput()
    {
        // 1. 重置移动方向
        moveDirection = Vector3.zero;

        // 2. Shift加速逻辑(修复原脚本Bug:重复按下Shift导致速度无限倍)
        float currentSpeedMultiplier = Input.GetKey(KeyCode.LeftShift) ? shiftRate : 1f;
        float finalMoveSpeed = moveSpeed * currentSpeedMultiplier;

        // 3. WASD前后左右移动(基于相机自身朝向)
        if (Input.GetKey(KeyCode.W)) moveDirection += cameraTrans.forward;
        if (Input.GetKey(KeyCode.S)) moveDirection -= cameraTrans.forward;
        if (Input.GetKey(KeyCode.A)) moveDirection -= cameraTrans.right;
        if (Input.GetKey(KeyCode.D)) moveDirection += cameraTrans.right;

        // 4. QE垂直升降(世界坐标系Y轴,避免旋转后升降方向偏移)
        if (Input.GetKey(KeyCode.Q)) moveDirection += Vector3.down;
        if (Input.GetKey(KeyCode.E)) moveDirection += Vector3.up;

        // 5. 归一化方向向量(避免斜向移动速度翻倍)
        if (moveDirection.magnitude > 1f)
        {
            moveDirection.Normalize();
        }

        // 6. 应用速度倍率
        moveDirection *= finalMoveSpeed;
    }

    /// <summary>
    /// 更新相机位置(移动逻辑)
    /// </summary>
    private void UpdateCameraMovement()
    {
        // 基于世界坐标系移动(保证升降方向始终为世界Y轴)
        cameraTrans.Translate(moveDirection * Time.deltaTime, Space.World);
    }

    /// <summary>
    /// 更新相机旋转(右键视角控制)
    /// </summary>
    private void UpdateCameraRotation()
    {
        if (Input.GetMouseButton(1)) // 按住鼠标右键
        {
            // 1. 水平旋转(绕世界Y轴,左右拖动鼠标)
            float horizontalRotate = Input.GetAxis("Mouse X") * rotateSpeed * Time.deltaTime;
            cameraTrans.RotateAround(cameraTrans.position, Vector3.up, horizontalRotate);

            // 2. 垂直旋转(绕相机自身X轴,上下拖动鼠标,带角度限制)
            float verticalRotate = -Input.GetAxis("Mouse Y") * rotateSpeed * Time.deltaTime;
            currentVerticalAngle += verticalRotate;
            // 限制垂直旋转角度,防止相机翻转
            currentVerticalAngle = Mathf.Clamp(currentVerticalAngle, minVerticalAngle, maxVerticalAngle);

            // 3. 应用垂直旋转(重置X轴欧拉角,避免万向节死锁)
            Vector3 currentEuler = cameraTrans.eulerAngles;
            cameraTrans.eulerAngles = new Vector3(currentVerticalAngle, currentEuler.y, currentEuler.z);
        }
    }

    /// <summary>
    /// 可选:Gizmos绘制相机移动方向(调试用)
    /// </summary>
    private void OnDrawGizmos()
    {
        Gizmos.color = Color.red;
        Gizmos.DrawRay(cameraTrans.position, moveDirection.normalized * 2f);
    }
}
相关推荐
leo__5201 小时前
单载波中继系统资源分配算法MATLAB仿真程序
算法·matlab·unity
努力长头发的程序猿2 小时前
Unity使用ScriptableObject序列化资源
unity·游戏引擎
mxwin3 小时前
Unity Shader 手写基于 PBR 的 URP Lit Shader 核心光照计算
unity·游戏引擎·shader
小贺儿开发3 小时前
Unity3D 智能云端数字标牌系统
unity·阿里云·人机交互·视频·oss·广告·互动
魔士于安3 小时前
Unity windows 同步 异步 打开文件文件夹工具
游戏·unity·游戏引擎·贴图·模型
笑虾4 小时前
cocos2d-x lua 加载 Cocos Studio 导出的 csb
游戏引擎·lua·cocos2d
魔士于安4 小时前
unity lowpoly 风格 城市 建筑 道路 交通标志
游戏·unity·游戏引擎·贴图·模型
mxwin4 小时前
Unity GPU Shader 性能优化指南
unity·游戏引擎·shader
steven_yzx4 小时前
自动驾驶相机坐标系转换
人工智能·数码相机·自动驾驶
steven_yzx5 小时前
自动驾驶相机坐标系转换2
人工智能·数码相机·自动驾驶