pppboy 发表于 2023-4-22 11:03:30

【转载】Unity UGUI 屏幕适配

本帖最后由 pppboy 于 2023-4-22 11:11 编辑

> 本帖最后由 pppboy 于 2023-4-22 11:06 编辑

Unity UGUI 屏幕适配与黑边的处理
为了让项目能够在各种电子设备上都能正常运行,我们需要让UGUI在各种分辨率、各种比例下都能正常显示。为了达到这一目的我们需要做UGUI的屏幕适配。另外还有相机的适配在这篇文章中暂不讨论。

基础知识
首先我们需要了解手机屏幕分辨率的相关知识。
在屏幕适配的过程中我们主要关注:

屏幕的宽高比 (Aspect Ratio, 屏幕宽度/屏幕高度)。 可以在Unity的Game视图中进行调整。
游戏的设计分辨率。 既UI在设计界面的时候的分辨率。相对小的设计分辨率会减小安装包的大小,内存占用的大小等等。但是可能在大屏设备上变得模糊。
适配方案
由于UGUI是通过Canvas显示在界面上,所以Canvas的RenderMode属性需要调整为Screen Space - Camera.
当实际设备的屏幕分辨率与设计分辨路不一致的时候,通过CanvasScaler组件进行缩放。CanvasScaler组件在创建Canvas会自动创建。
CanvasScaler组件主要调整的参数就是UIScaleMode. 具体参数可以参考画布缩放器 (Canvas Scaler). 由于设计分辨率不可能和屏幕分辨率完美对应上我们需要选择Scale With Screen Size选项来保证UI会随着屏幕进行放大或缩小。

可选的适配方案:

按照短边等比缩放
按照短边进行等比缩放,长边进行自适应。自适应可以通过Anchors和Pivot来进行处理。
另外现在手机都有异性屏也需要进行处理。 处理方法为使用Screen.safeArea计算并设置Panel相关内容来避开安全区域。

实验处理
为了方便开发可以抽象一个Panel的概念。 Panel是一个界面的父节点,我们可以按照适配方案控制好panel的大小,这样这个节点下的所有UI都无需再考虑适配的逻辑。仅需要使用锚点等方法进行处理。
首先搭建一个测试场景如下:
!(data/attachment/forum/202304/22/110652b52t58riir9uumhm.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/300 "72a049b7860b4beeb3829478463a3a5c.png")

Canvas是我们的画布,挂载了AdaptiveCanvas脚本
Panel是我们的一个界面,挂载了AdaptivePanel脚本
Bg是没有进行适配的对照,其中的两个方块固定再左上和右下



注意: 代码在Update中一直计算比例并进行适配,实际项目中需要通过发事件或者其他方式自行处理。

```
public enum AdaptivePlan
{
    Match, // 通过自适应适配,尽量填充满屏幕
    Expand, // 比例实在夸张无法适配,采用固定尺寸,其他区域留白的方式处理
}
```

```
public class AdaptiveCanvas : MonoBehaviour
{
    public static AdaptiveCanvas Instance;
    public Vector2 referenceResolutoin = new Vector2(750, 1334);

    public AdaptivePlan adaptivePlan;
    private float lastwidth = 0f;
    private float lastheight = 0f;

    CanvasScaler canvasScaler;

    private void Awake()
    {
      Instance = this;
      canvasScaler = GetComponent<CanvasScaler>();
      canvasScaler.uiScaleMode = CanvasScaler.ScaleMode.ScaleWithScreenSize;
      canvasScaler.referenceResolution = referenceResolutoin;
    }
    void Update()
    {
      CheckResolution();
    }
    private void CheckResolution()
    {
      if (lastwidth != Screen.width || lastheight != Screen.height)
      {
            lastwidth = Screen.width;
            lastheight = Screen.height;
            Adaptive();
      }
    }
    private void Adaptive()
    {
      // 一般情况下竖屏游戏的短边是宽,横屏游戏的短边是高
      bool shortSideIsWidth = Screen.width < Screen.height ? true : false;

      float ratio = (float)Screen.width / Screen.height;
      // Debug.Log($"width: {Screen.width}height: {Screen.height} ratio: {ratio}");

      if (shortSideIsWidth)
      {
            if (ratio < 0.4f || ratio > 0.6f) // 比例实在太离谱,为保证游戏可用采取拓展画布区域
            {
                canvasScaler.screenMatchMode = CanvasScaler.ScreenMatchMode.Expand;
                adaptivePlan = AdaptivePlan.Expand;
                return;
            }
            canvasScaler.screenMatchMode = CanvasScaler.ScreenMatchMode.MatchWidthOrHeight;
            canvasScaler.matchWidthOrHeight = 0;
            adaptivePlan = AdaptivePlan.Match;
      }
      else
      {
            if (ratio < 1.6f || ratio > 2.5f) // 类型长方形比例
            {
                canvasScaler.screenMatchMode = CanvasScaler.ScreenMatchMode.Expand;
                adaptivePlan = AdaptivePlan.Expand;
                return;
            }
            // 比例在合适范围内使用短边适配的方案
            canvasScaler.screenMatchMode = CanvasScaler.ScreenMatchMode.MatchWidthOrHeight;
            canvasScaler.matchWidthOrHeight = 1;
            adaptivePlan = AdaptivePlan.Match;
      }
    }
}

```

```
public class AdaptivePanel : MonoBehaviour
{
    RectTransform rectTrans;
    // Start is called before the first frame update
    void Start()
    {
      rectTrans = transform as RectTransform;
    }

    private void Update()
    {
      if (AdaptiveCanvas.Instance.adaptivePlan == AdaptivePlan.Match)
      {
            rectTrans.localScale = Vector3.one;
            UpdateAdaptive();
            rectTrans.anchorMin = Vector2.zero;
            rectTrans.anchorMax = Vector2.one;
            rectTrans.offsetMin = Vector2.zero;
            rectTrans.offsetMax = Vector2.zero;
      }
      else if (AdaptiveCanvas.Instance.adaptivePlan == AdaptivePlan.Expand)
      {
            rectTrans.localScale = Vector3.one;
            UpdateAdaptive();
            rectTrans.anchorMin = Vector2.one * 0.5f;
            rectTrans.anchorMax = Vector2.one * 0.5f;
            rectTrans.offsetMin = Vector2.zero;
            rectTrans.offsetMax = Vector2.zero;

            rectTrans.sizeDelta = AdaptiveCanvas.Instance.referenceResolutoin;
      }
    }
        // 使用safeArea适配异性屏
    void UpdateAdaptive()
    {
      var r = Screen.safeArea;
      Vector2 anchorMin = r.position;
      Vector2 anchorMax = r.position + r.size;
      anchorMin.x /= Screen.width;
      anchorMin.y /= Screen.height;
      anchorMax.x /= Screen.width;
      anchorMax.y /= Screen.height;
      rectTrans.anchorMin = anchorMin;
      rectTrans.anchorMax = anchorMax;
    }
}
```
页: [1]
查看完整版本: 【转载】Unity UGUI 屏幕适配