You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

Unity正交相机(Orthographic Camera)边界内鼠标平移异常问题求助

解决正交相机鼠标平移边界卡住的问题

这个问题我之前做项目时也碰到过,核心原因是你的代码在相机被边界限制后,没有同步更新拖动的基准点,导致后续的偏移计算始终基于超出边界时的错误数值,所以往回拖的时候相机会卡住不动。

问题根源分析

原代码里,你用Origin(拖动开始的世界点)和Diference(当前鼠标世界点与相机位置的差值)来计算新相机位置:

Vector3 newCameraPosition = Origin - Diference;

当相机被Mathf.Clamp限制在边界后,相机位置不再跟随鼠标移动,但Origin还是最初的拖动起点,Diference的计算会基于当前鼠标的世界点和被限制后的相机位置,这就导致newCameraPosition一直被卡在边界值,直到鼠标回到边界范围内,Diference的变化才会让计算结果脱离边界。

修复方案:基于屏幕偏移的拖动逻辑

更好的做法是用屏幕坐标的位移来计算相机移动,正交相机下屏幕位移和世界空间移动是线性对应的,而且不需要依赖Raycast的世界点(当然你可以保留Raycast判断是否点击地形)。同时,当相机被边界限制时,及时更新拖动的基准点,确保后续计算始终正确。

修改后的代码如下:

public class CameraDragMouse : MonoBehaviour {
    private Vector3 _dragStartScreenPos;
    private Vector3 _dragStartCameraPos;
    private bool _isDragging;
    private float _minPanningBoundaryX;
    private float _maxPanningBoundaryX;
    private float _minPanningBoundaryZ;
    private float _maxPanningBoundaryZ;
    private Camera _mainCamera;

    private void Start() {
        _mainCamera = GetComponentInChildren<Camera>();
        CameraCenter.CenterCameraOnPosition(new Vector3(24, 0, 24));
        var cameraStartPos = transform.position;
        _minPanningBoundaryX = cameraStartPos.x - 40;
        _maxPanningBoundaryX = cameraStartPos.x + 40;
        _minPanningBoundaryZ = cameraStartPos.z - 40;
        _maxPanningBoundaryZ = cameraStartPos.z + 40;
    }

    void LateUpdate() {
        if (Input.GetMouseButtonDown(0)) {
            // 仅在点击地形时允许拖动
            Ray ray = _mainCamera.ScreenPointToRay(Input.mousePosition);
            if (Physics.Raycast(ray, out RaycastHit hit, Mathf.Infinity, Constants.LAYER_MASK_TERRAIN)) {
                _isDragging = true;
                // 记录拖动起始的屏幕位置和相机位置
                _dragStartScreenPos = Input.mousePosition;
                _dragStartCameraPos = transform.position;
            }
        }
        else if (Input.GetMouseButtonUp(0)) {
            _isDragging = false;
        }

        if (_isDragging) {
            // 计算鼠标屏幕位置的偏移量
            Vector3 screenDelta = Input.mousePosition - _dragStartScreenPos;
            // 将屏幕偏移转换为世界空间单位(正交相机专属计算)
            float worldUnitsPerPixel = _mainCamera.orthographicSize * 2 / Screen.height;
            Vector3 worldDelta = new Vector3(screenDelta.x, 0, screenDelta.y) * worldUnitsPerPixel;
            
            // 计算新的相机位置(鼠标向右拖,相机向左移,所以取反)
            Vector3 targetPos = _dragStartCameraPos - worldDelta;
            // 限制在边界内
            Vector3 clampedPos = new Vector3(
                Mathf.Clamp(targetPos.x, _minPanningBoundaryX, _maxPanningBoundaryX),
                transform.position.y,
                Mathf.Clamp(targetPos.z, _minPanningBoundaryZ, _maxPanningBoundaryZ)
            );

            // 关键:如果相机被边界限制,更新拖动基准点
            if (clampedPos != targetPos) {
                _dragStartCameraPos = clampedPos;
                _dragStartScreenPos = Input.mousePosition;
            }

            transform.position = clampedPos;
        }
    }
}

修复要点说明

  1. 屏幕转世界空间的计算:利用正交相机的orthographicSize(相机视口半高的世界单位)和屏幕高度,算出每个像素对应的世界单位,确保屏幕位移和相机移动的比例正确。
  2. 边界限制时更新基准点:当相机被Clamp后,立即把当前相机位置和鼠标屏幕位置设为新的拖动起点,这样后续的偏移计算就会基于边界位置重新开始,鼠标往回拖时相机能立即响应。
  3. 更清晰的状态管理:用_isDragging替代原有的Drag,变量名更具语义,逻辑也更清晰。

这样修改后,不管你拖到边界后怎么反向拖动,相机都会实时跟随鼠标移动,不会再出现卡住的情况。

内容的提问来源于stack exchange,提问作者eek

火山引擎 最新活动