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

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);
}

问题原因

  1. 相机可视区域计算错误:平板宽高比大于9:16,相机通过rect裁剪了左右两侧,但代码直接使用camera.pixelWidth/Height(屏幕原始像素尺寸)计算,而非相机裁剪后的实际可视尺寸,导致偏移量计算偏差。
  2. 地图尺寸未考虑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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 07:07:03