Unity 2D安卓游戏选关地图平板端居中偏移问题排查
Unity平板端世界地图关卡居中位置错误问题排查与修复
问题背景
开发2D安卓Unity游戏时,选关世界地图场景通过Awake调用相机适配代码锁定9:16显示比例,Start函数实现画布地图移动逻辑,让最后点击的关卡居中显示。该逻辑在编辑器和手机端正常,但平板端出现关卡居中位置错误,相机比例适配正常,怀疑与Canvas Scaler的Screen Match Mode相关,但无法定位具体原因。
相机适配代码
void Awake() { CamAspect(); } public void CamAspect() { float targetaspect = 9f / 16f; float windowaspect = (float)Screen.width / (float)Screen.height; float scaleheight = windowaspect / targetaspect; Camera camera = GetComponent<Camera>(); if (scaleheight < 1f) { Rect rect = camera.rect; rect.width = 1f; rect.height = scaleheight; rect.x = 0; rect.y = (1f - scaleheight) / 2f; camera.rect = rect; } else { float scalewidth = 1f / scaleheight; Rect rect = camera.rect; rect.width = scalewidth; rect.height = 1f; rect.x = (1f - scalewidth) / 2f; rect.y = 0; camera.rect = rect; } }
地图居中配置与代码
画布设置
- Render Mode: Screen Space - Camera
- UI Scale Mode: Scale with screen size
- Screen Match Mode: Shrink(Match width/height设为0.5时PC自由比例视图正常)
地图结构
mapHolder带有Scroll Rect组件,地图作为其子对象;lastLevelT为关卡相对于父级地图左下角的位置,居中逻辑基于此坐标计算。
地图居中代码
RectTransform RT; float[] lastLevelT; float cameraScreenWidth; float cameraScreenHeight; // map is 4096 wide by 3072 tall float shiftx; float shifty; float mapwidth; float mapheight; new Camera camera; void Start() { RT = GetComponent<RectTransform>(); camera = Camera.main; lastLevelT = SettingsManager.currentFocusLevelTransform; if (lastLevelT[0] == 0 && lastLevelT[1] == 0) { cameraScreenHeight = (float)camera.pixelHeight; cameraScreenWidth = (float)camera.pixelWidth; RT.localPosition = new Vector2(-cameraScreenWidth * 0.5f, -cameraScreenHeight * 0.5f); } else { Transform(); } } void Transform() { mapwidth = RT.rect.width; mapheight = RT.rect.height; cameraScreenHeight = (float)camera.pixelHeight; cameraScreenWidth = (float)camera.pixelWidth; shiftx = lastLevelT[0]; shifty = lastLevelT[1]; if ((mapwidth - lastLevelT[0]) < (cameraScreenWidth * 0.5f)) { shiftx = mapwidth - cameraScreenWidth * 0.5f; } if (lastLevelT[0] < (cameraScreenWidth * 0.5f)) { shiftx = cameraScreenWidth * 0.5f; } if ((mapheight - lastLevelT[1]) < (cameraScreenHeight * 0.5f)) { shifty = mapheight - cameraScreenHeight * 0.5f; } if (lastLevelT[1] < (cameraScreenHeight * 0.5f)) { shifty = cameraScreenHeight * 0.5f; } RT.localPosition = new Vector2(-shiftx, -shifty); }
问题原因
- 相机可视区域计算错误:平板宽高比大于9:16,相机通过
rect裁剪了左右两侧,但代码直接使用camera.pixelWidth/Height(屏幕原始像素尺寸)计算,而非相机裁剪后的实际可视尺寸,导致偏移量计算偏差。 - 地图尺寸未考虑Canvas缩放:Canvas Scaler的Shrink模式会根据屏幕比例缩放UI元素,代码直接使用
RT.rect.width的原始尺寸计算边界,未结合缩放系数,导致边界判断错误。
修复方案
1. 修正相机可视区域尺寸计算
替换原始的像素尺寸获取逻辑,使用相机裁剪后的实际可视尺寸:
// 替换cameraScreenWidth/Height的赋值 float cameraViewWidth = camera.pixelWidth * camera.rect.width; float cameraViewHeight = camera.pixelHeight * camera.rect.height;
2. 结合Canvas缩放系数计算实际尺寸
获取Canvas缩放比例,修正地图和关卡坐标的实际显示尺寸:
void Transform() { mapwidth = RT.rect.width; mapheight = RT.rect.height; // 获取相机实际可视尺寸 float cameraViewWidth = camera.pixelWidth * camera.rect.width; float cameraViewHeight = camera.pixelHeight * camera.rect.height; // 获取Canvas缩放系数 CanvasScaler canvasScaler = GetComponentInParent<CanvasScaler>(); float scaleFactor = canvasScaler.scaleFactor; // 计算地图和关卡的实际显示尺寸 float mapActualWidth = mapwidth * scaleFactor; float mapActualHeight = mapheight * scaleFactor; float levelActualX = lastLevelT[0] * scaleFactor; float levelActualY = lastLevelT[1] * scaleFactor; shiftx = levelActualX; shifty = levelActualY; // 修正边界判断逻辑 if ((mapActualWidth - levelActualX) < (cameraViewWidth * 0.5f)) { shiftx = mapActualWidth - cameraViewWidth * 0.5f; } if (levelActualX < (cameraViewWidth * 0.5f)) { shiftx = cameraViewWidth * 0.5f; } if ((mapActualHeight - levelActualY) < (cameraViewHeight * 0.5f)) { shifty = mapActualHeight - cameraViewHeight * 0.5f; } if (levelActualY < (cameraViewHeight * 0.5f)) { shifty = cameraViewHeight * 0.5f; } // 还原Scroll Rect局部坐标系的缩放影响 RT.localPosition = new Vector2(-shiftx / scaleFactor, -shifty / scaleFactor); }
3. 优化Canvas Scaler设置(可选)
将Screen Match Mode改为Match Width or Height,并将Match值设为1(优先匹配高度),让UI缩放更贴合9:16的竖屏设计,减少平板端的缩放偏差。
内容的提问来源于stack exchange,提问作者Poisht
相关产品推荐
相关产品推荐

