You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何阻止2D游戏相机缩放超出地图边界?

2D相机缩放平移边界适配解决方案

你的核心问题是使用固定平移边界值,未考虑相机缩放(orthographicSize)变化带来的视野范围改变:缩小镜头时相机视野变大,固定边界会让视野超出地图;放大时视野变小,固定边界又限制相机移到地图边缘。

以下是适配《GardenScapes》式缩放平移逻辑的解决方案:

核心思路

  1. 动态获取地图的世界空间边界(基于SpriteRenderer的bounds属性,无需硬编码1920x1080)
  2. 根据当前相机的orthographicSize和屏幕宽高比,实时计算相机可移动的边界:
    • 当相机视野宽/高于地图时,强制相机居中,避免显示空白
    • 当相机视野窄/低于地图时,允许相机移动到边缘,让视野刚好贴合地图边缘
  3. 平移和缩放操作后,统一用动态计算的边界限制相机位置

修改后完整代码

using UnityEngine;

public class CameraController : MonoBehaviour
{
    public Camera cam;
    public SpriteRenderer mapSprite;
    public float zoomPinchSpeed = 0.01f;
    public float zoomMouseSpeed = 5f;
    public float zoomOutMin = 1f;
    public float zoomOutMax = 10f;

    private Vector3 touchStart;
    private bool multiTouch;
    private Bounds mapBounds;
    private float screenRatio;

    void Start()
    {
        mapBounds = mapSprite.bounds;
        screenRatio = (float)Screen.width / Screen.height;
    }

    void Update()
    {
        if (Input.GetMouseButtonDown(0))
        {
            touchStart = cam.ScreenToWorldPoint(Input.mousePosition);
            multiTouch = false;
        }

        if (Input.touchCount == 2)
        {
            multiTouch = true;
            Touch touchZero = Input.GetTouch(0);
            Touch touchOne = Input.GetTouch(1);

            Vector2 touchZeroPrevPos = touchZero.position - touchZero.deltaPosition;
            Vector2 touchOnePrevPos = touchOne.position - touchOne.deltaPosition;

            float prevMagnitude = (touchZeroPrevPos - touchOnePrevPos).magnitude;
            float currentMagnitude = (touchZero.position - touchOne.position).magnitude;
            float difference = currentMagnitude - prevMagnitude;

            zoom(difference * zoomPinchSpeed);
            ClampCameraPosition();
        }
        else if (Input.GetMouseButton(0) && !multiTouch)
        {
            Vector3 direction = touchStart - cam.ScreenToWorldPoint(Input.mousePosition);
            cam.transform.position += direction;
            ClampCameraPosition();
        }

        float scrollInput = Input.GetAxis("Mouse ScrollWheel") * zoomMouseSpeed;
        if (scrollInput != 0)
        {
            zoom(scrollInput);
            ClampCameraPosition();
        }
    }

    void zoom(float increment)
    {
        cam.orthographicSize = Mathf.Clamp(cam.orthographicSize - increment, zoomOutMin, zoomOutMax);
    }

    private void ClampCameraPosition()
    {
        Vector2 xBounds = GetCameraXBounds();
        Vector2 yBounds = GetCameraYBounds();

        Vector3 targetPosition = cam.transform.position;
        targetPosition.x = Mathf.Clamp(targetPosition.x, xBounds.x, xBounds.y);
        targetPosition.y = Mathf.Clamp(targetPosition.y, yBounds.x, yBounds.y);
        targetPosition.z = -0.01f;

        cam.transform.position = targetPosition;
    }

    private Vector2 GetCameraXBounds()
    {
        float cameraHalfWidth = cam.orthographicSize * screenRatio;
        float mapHalfWidth = mapBounds.extents.x;

        float minX = mapBounds.center.x - (mapHalfWidth - cameraHalfWidth);
        float maxX = mapBounds.center.x + (mapHalfWidth - cameraHalfWidth);

        // 相机视野宽于地图时,强制居中避免空白
        if (cameraHalfWidth >= mapHalfWidth)
        {
            minX = mapBounds.center.x;
            maxX = mapBounds.center.x;
        }

        return new Vector2(minX, maxX);
    }

    private Vector2 GetCameraYBounds()
    {
        float cameraHalfHeight = cam.orthographicSize;
        float mapHalfHeight = mapBounds.extents.y;

        float minY = mapBounds.center.y - (mapHalfHeight - cameraHalfHeight);
        float maxY = mapBounds.center.y + (mapHalfHeight - cameraHalfHeight);

        // 相机视野高于地图时,强制居中避免空白
        if (cameraHalfHeight >= mapHalfHeight)
        {
            minY = mapBounds.center.y;
            maxY = mapBounds.center.y;
        }

        return new Vector2(minY, maxY);
    }
}

关键逻辑说明

  • mapBounds:直接从SpriteRenderer获取地图的世界空间边界,适配任何地图缩放或位置调整
  • screenRatio:提前计算屏幕宽高比,避免重复计算浪费性能
  • GetCameraXBounds/GetCameraYBounds:实时计算相机可移动范围,兼顾“缩小不显示空白”和“放大可触达边缘”的需求
  • ClampCameraPosition:统一处理相机位置限制,平移和缩放后都调用,确保边界始终正确

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.20 22:43:18