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);
    }
}
相关推荐
mxwin4 小时前
Unity Shader 顶点动画:在顶点着色器中实现风吹草动、河流波动、布料模拟
unity·游戏引擎·shader·着色器
DowneyJoy4 小时前
【Unity3D补充知识点】常用数据结构分析-集合(List<T>)
数据结构·unity·c#·list
格林威5 小时前
Baumer相机铝型材表面划伤长度测量:实现损伤量化评估的 5 个关键技术,附 OpenCV+Halcon 实战代码!
开发语言·人工智能·数码相机·opencv·计算机视觉·c#·工业相机
DowneyJoy5 小时前
【Unity3D补充知识点】常用数据结构分析-数组(Array)
数据结构·unity·c#
格林威5 小时前
Baumer相机铝箔表面针孔检测:提升包装阻隔性的 7 个核心策略,附 OpenCV+Halcon 实战代码!
开发语言·人工智能·数码相机·opencv·计算机视觉·c#·工业相机
w-白兰地6 小时前
配置Unity中的ADB环境变量
unity·adb·游戏引擎
mxwin6 小时前
Unity Shader 几何着色器:动态生成图元与顶点拓扑修改
unity·游戏引擎·着色器
呆呆敲代码的小Y7 小时前
【Unity-AI开发篇】| 游戏中接入DeepSeek实现AI对话,完整详细步骤
人工智能·游戏·unity·ai·游戏引擎·u3d·deepseek
相信神话20211 天前
第四章:Godot 4.6 核心概念与开发环境搭建
游戏引擎·godot·2d游戏编程·godot4·2d游戏开发
代数狂人1 天前
在Godot中应用面向对象原则:C#脚本实践
c#·游戏引擎·godot