Unity 摄像机控制器

Unity 摄像机控制器

一个功能完整的Unity摄像机控制脚本,支持鼠标拖动移动、旋转、缩放,并惯性效果。

功能特点

鼠标左键拖动:摄像机上下左右移动鼠标右键拖动:摄像机围绕目标点旋转鼠标滚轮:摄像机缩放平滑旋转:使用SmoothDamp实现流畅的旋转过渡惯性旋转:支持旋转惯性,提升操作手感视角重置:一键返回初始视角参数可调:所有操作速度、限制和效果都可通过Inspector调整

目标设置


Target
:摄像机围绕旋转的目标点(可选)Use Camera Position As Target`:如果没有指定目标,是否使用摄像机初始位置作为参考点

移动设置


Move Speed
:鼠标左键拖动时的移动速度
Move Smooth Time
:移动时的平滑时间

旋转设置


Rotation Speed
:鼠标右键拖动时的旋转速度
Min Vertical Angle
:垂直旋转的最小角度限制
Max Vertical Angle
:垂直旋转的最大角度限制
Rotation Smooth Time
:旋转时的平滑时间

惯性设置


Enable Inertia
:是否启用惯性旋转
Inertia Damping
:惯性衰减系数(值越小,惯性持续时间越长)
Inertia Threshold
:惯性阈值(低于此值时停止惯性)

缩放设置


Zoom Speed
:鼠标滚轮缩放速度
Min Zoom Distance
:最小缩放距离
Max Zoom Distance
:最大缩放距离
Zoom Smooth Time
:缩放时的平滑时间

视角重置


Reset View Key
:重置视角的快捷键
Reset Smooth Time
:重置视角时的平滑时间

注意事项

如果没有指定目标点,摄像机会围绕自身旋转建议在场景中创建一个空物体作为目标点,这样可以更好地控制旋转中心调整平滑时间和惯性参数可以获得不同的操作手感垂直旋转角度限制可以防止摄像机翻转过头

以下为示例代码,亲测可用


using UnityEngine;

/// <summary>
/// 摄像机控制器 - 实现鼠标控制摄像机移动、旋转、缩放和平滑惯性效果
/// </summary>
[RequireComponent(typeof(Camera))]
public class CameraController : MonoBehaviour
{
    [Header("目标设置")]
    [Tooltip("摄像机围绕旋转的目标点")]
    public Transform target;
    
    [Tooltip("如果没有指定目标,是否使用摄像机初始位置作为参考点")]
    public bool useCameraPositionAsTarget = true;

    [Header("移动设置")]
    [Tooltip("鼠标左键拖动时的移动速度")]
    public float moveSpeed = 5f;
    
    [Tooltip("移动时的平滑时间")]
    public float moveSmoothTime = 0.3f;

    [Header("旋转设置")]
    [Tooltip("鼠标右键拖动时的旋转速度")]
    public float rotationSpeed = 100f;
    
    [Tooltip("垂直旋转的最小角度限制")]
    public float minVerticalAngle = -80f;
    
    [Tooltip("垂直旋转的最大角度限制")]
    public float maxVerticalAngle = 80f;
    
    [Tooltip("旋转时的平滑时间")]
    public float rotationSmoothTime = 0.3f;

    [Header("惯性设置")]
    [Tooltip("是否启用惯性旋转")]
    public bool enableInertia = true;
    
    [Tooltip("惯性衰减系数 (值越小,惯性持续时间越长)")]
    public float inertiaDamping = 5f;
    
    [Tooltip("惯性阈值 (低于此值时停止惯性)")]
    public float inertiaThreshold = 0.1f;

    [Header("缩放设置")]
    [Tooltip("鼠标滚轮缩放速度")]
    public float zoomSpeed = 2f;
    
    [Tooltip("最小缩放距离")]
    public float minZoomDistance = 1f;
    
    [Tooltip("最大缩放距离")]
    public float maxZoomDistance = 20f;
    
    [Tooltip("缩放时的平滑时间")]
    public float zoomSmoothTime = 0.2f;

    [Header("视角重置")]
    [Tooltip("重置视角的快捷键")]
    public KeyCode resetViewKey = KeyCode.R;
    
    [Tooltip("重置视角时的平滑时间")]
    public float resetSmoothTime = 0.5f;

    // 私有变量
    private Vector3 _targetPosition;
    private Vector3 _currentPosition;
    private Vector3 _positionVelocity;
    
    private float _currentXAngle;
    private float _currentYAngle;
    private float _targetXAngle;
    private float _targetYAngle;
    private float _xAngleVelocity;
    private float _yAngleVelocity;
    
    private float _currentDistance;
    private float _targetDistance;
    private float _distanceVelocity;
    
    private Vector3 _initialPosition;
    private Quaternion _initialRotation;
    private float _initialDistance;
    
    private Vector2 _lastMousePosition;
    private Vector2 _mouseDelta;
    private Vector2 _rotationInertia;

    private void Start()
    {
        // 初始化目标点
        if (target == null && useCameraPositionAsTarget)
        {
            target = new GameObject("CameraTarget").transform;
            target.position = transform.position + transform.forward * 5f; // 默认在摄像机前方5单位
        }

        // 记录初始状态用于重置
        _initialPosition = transform.position;
        _initialRotation = transform.rotation;
        
        // 计算初始距离
        if (target != null)
        {
            _initialDistance = Vector3.Distance(transform.position, target.position);
            _currentDistance = _initialDistance;
            _targetDistance = _initialDistance;
            
            // 计算初始角度
            Vector3 direction = transform.position - target.position;
            Quaternion rotation = Quaternion.LookRotation(-direction);
            _currentXAngle = rotation.eulerAngles.x;
            _currentYAngle = rotation.eulerAngles.y;
            _targetXAngle = _currentXAngle;
            _targetYAngle = _currentYAngle;
        }
        else
        {
            _initialDistance = 0;
            _currentDistance = 0;
            _targetDistance = 0;
            
            _currentXAngle = transform.eulerAngles.x;
            _currentYAngle = transform.eulerAngles.y;
            _targetXAngle = _currentXAngle;
            _targetYAngle = _currentYAngle;
        }

        _currentPosition = transform.position;
        _targetPosition = transform.position;
    }

    private void Update()
    {
        HandleInput();
        UpdateCameraPosition();
        UpdateCameraRotation();
        UpdateCameraZoom();
        HandleViewReset();
    }

    /// <summary>
    /// 处理用户输入
    /// </summary>
    private void HandleInput()
    {
        // 记录鼠标位置和 delta
        _lastMousePosition = Input.mousePosition;
        
        // 处理鼠标左键拖动 - 移动
        if (Input.GetMouseButton(0))
        {
            _mouseDelta = new Vector2(Input.GetAxis("Mouse X"), Input.GetAxis("Mouse Y"));
            HandleCameraMovement();
        }
        
        // 处理鼠标右键拖动 - 旋转
        if (Input.GetMouseButton(1))
        {
            _mouseDelta = new Vector2(Input.GetAxis("Mouse X"), Input.GetAxis("Mouse Y"));
            HandleCameraRotationInput();
        }
        else if (enableInertia && _rotationInertia.magnitude > inertiaThreshold)
        {
            // 应用惯性旋转
            ApplyInertiaRotation();
        }
        else if (!Input.GetMouseButton(1))
        {
            // 重置惯性
            _rotationInertia = Vector2.zero;
        }
        
        // 处理鼠标滚轮 - 缩放
        float scrollInput = Input.GetAxis("Mouse ScrollWheel");
        if (Mathf.Abs(scrollInput) > 0.01f)
        {
            HandleCameraZoomInput(scrollInput);
        }
    }

    /// <summary>
    /// 处理摄像机移动
    /// </summary>
    private void HandleCameraMovement()
    {
        if (target == null) return;
        
        // 计算移动方向
        Vector3 moveDirection = (-transform.right * _mouseDelta.x - transform.up * _mouseDelta.y) * moveSpeed;
        
        // 更新目标位置
        _targetPosition += moveDirection;
        
        // 如果有目标,也移动目标点
        target.position += moveDirection;
    }

    /// <summary>
    /// 处理摄像机旋转输入
    /// </summary>
    private void HandleCameraRotationInput()
    {
        // 计算旋转增量
        float xDelta = _mouseDelta.x * rotationSpeed * Time.deltaTime;
        float yDelta = _mouseDelta.y * rotationSpeed * Time.deltaTime;
        
        // 更新目标角度
        _targetYAngle += xDelta;
        _targetXAngle -= yDelta;
        
        // 限制垂直旋转角度
        _targetXAngle = Mathf.Clamp(_targetXAngle, minVerticalAngle, maxVerticalAngle);
        
        // 记录惯性
        if (enableInertia)
        {
            _rotationInertia = new Vector2(xDelta, yDelta) * 10f; // 放大惯性效果
        }
    }

    /// <summary>
    /// 应用惯性旋转
    /// </summary>
    private void ApplyInertiaRotation()
    {
        // 衰减惯性
        _rotationInertia *= (1f - inertiaDamping * Time.deltaTime);
        
        // 应用惯性旋转
        _targetYAngle += _rotationInertia.x;
        _targetXAngle -= _rotationInertia.y;
        
        // 限制垂直旋转角度
        _targetXAngle = Mathf.Clamp(_targetXAngle, minVerticalAngle, maxVerticalAngle);
    }

    /// <summary>
    /// 处理摄像机缩放输入
    /// </summary>
    private void HandleCameraZoomInput(float scrollInput)
    {
        if (target == null) return;
        
        // 更新目标距离
        _targetDistance -= scrollInput * zoomSpeed;
        _targetDistance = Mathf.Clamp(_targetDistance, minZoomDistance, maxZoomDistance);
    }

    /// <summary>
    /// 更新摄像机位置
    /// </summary>
    private void UpdateCameraPosition()
    {
        if (target == null) return;
        
        // 平滑更新位置
        _currentPosition = Vector3.SmoothDamp(_currentPosition, _targetPosition, ref _positionVelocity, moveSmoothTime);
        transform.position = _currentPosition;
    }

    /// <summary>
    /// 更新摄像机旋转
    /// </summary>
    private void UpdateCameraRotation()
    {
        if (target == null) return;
        
        // 平滑更新角度
        _currentXAngle = Mathf.SmoothDampAngle(_currentXAngle, _targetXAngle, ref _xAngleVelocity, rotationSmoothTime);
        _currentYAngle = Mathf.SmoothDampAngle(_currentYAngle, _targetYAngle, ref _yAngleVelocity, rotationSmoothTime);
        
        // 应用旋转
        Quaternion rotation = Quaternion.Euler(_currentXAngle, _currentYAngle, 0f);
        transform.rotation = rotation;
        
        // 根据距离更新位置
        _currentDistance = Mathf.SmoothDamp(_currentDistance, _targetDistance, ref _distanceVelocity, zoomSmoothTime);
        transform.position = target.position - rotation * Vector3.forward * _currentDistance;
    }

    /// <summary>
    /// 更新摄像机缩放
    /// </summary>
    private void UpdateCameraZoom()
    {
        // 在UpdateCameraRotation中已经处理了缩放
    }

    /// <summary>
    /// 处理视角重置
    /// </summary>
    private void HandleViewReset()
    {
        if (Input.GetKeyDown(resetViewKey))
        {
            ResetView();
        }
    }

    /// <summary>
    /// 重置视角到初始状态
    /// </summary>
    public void ResetView()
    {
        _targetPosition = _initialPosition;
        _targetXAngle = _initialRotation.eulerAngles.x;
        _targetYAngle = _initialRotation.eulerAngles.y;
        _targetDistance = _initialDistance;
        
        // 如果有目标,也重置目标位置
        if (target != null && useCameraPositionAsTarget)
        {
            target.position = _initialPosition + _initialRotation * Vector3.forward * _initialDistance;
        }
        
        // 重置惯性
        _rotationInertia = Vector2.zero;
    }

    /// <summary>
    /// 绘制Gizmos
    /// </summary>
    private void OnDrawGizmosSelected()
    {
        if (target != null)
        {
            Gizmos.color = Color.yellow;
            Gizmos.DrawWireSphere(target.position, 0.2f);
            Gizmos.DrawLine(transform.position, target.position);
        }
    }
}

© 版权声明
THE END
如果内容对您有所帮助,就支持一下吧!
点赞0 分享
评论 抢沙发

请登录后发表评论

    暂无评论内容