如何阻止2D游戏相机缩放超出地图边界?
2D相机缩放平移边界适配解决方案
你的核心问题是使用固定平移边界值,未考虑相机缩放(orthographicSize)变化带来的视野范围改变:缩小镜头时相机视野变大,固定边界会让视野超出地图;放大时视野变小,固定边界又限制相机移到地图边缘。
以下是适配《GardenScapes》式缩放平移逻辑的解决方案:
核心思路
- 动态获取地图的世界空间边界(基于SpriteRenderer的
bounds属性,无需硬编码1920x1080) - 根据当前相机的
orthographicSize和屏幕宽高比,实时计算相机可移动的边界:- 当相机视野宽/高于地图时,强制相机居中,避免显示空白
- 当相机视野窄/低于地图时,允许相机移动到边缘,让视野刚好贴合地图边缘
- 平移和缩放操作后,统一用动态计算的边界限制相机位置
修改后完整代码
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
相关产品推荐
相关产品推荐

