Unity 手机屏幕适配
手机屏幕适配主要分为
1、手机安全交互区域适配(例如需要处理刘海屏)
2、界面内部UI元素相对位置适配
准备工作
1、对Canvas设置

(1)设置Render Mode为Screen Space-Camera,并赋值相机的引用
(2)设置UI Scale Mode 为 Scale With Screen Size,并设置项目分辨率
2、安装设备模拟器
(Device Simulator)
该插件可以帮助我们模拟游戏在手机上运行时的适配效果
Unity 2021之前的版本都是预览包,需要手动下载,
打开package Manager,进行设置

勾选启用预览包

搜索下载 模拟器

在Game窗口切换使用

点击Safe Area 显示设备安全区域

切换设备型号

安全区域适配
需要处理三类屏幕适配情况:
1、只需要适配上端区域

| 型号 | 
| Apple iPad Air 2 | 
| Google Pixel 5 | 
| Huawei P40 Pro | 
| Motorola Moto G7 Power | 
| OnePlus 6T | 
| Samsung Galaxy Note10 | 
| Samsung Galaxy Note10+ 5G | 
| Samsung Galaxy Note20 Ultra 5G | 
| Samsung Galaxy S10 5G | 
| Samsung Galaxy S10+ | 
| Samsung Galaxy S10e | 
| Samsung Galaxy Z Fold2 5G_1 | 
| Samsung Galaxy Z Fold2 5G_2 | 
| Xiaomi Mi A3 | 
| Xiaomi Redmi 6 Pro | 
| Xiaomi Redmi Note7 | 
2、只需要适配下端区域

| 型号 | 
| Apple iPad Pro 11 | 
| Apple iPad Pro 12.9(2018) | 
2、上端和下端区域都适配

| 型号 | 
| Apple iPad Air (4th generation) | 
| Apple iPhone 11 | 
| Apple iPhone 12 | 
| Apple iPhone 12 Pro | 
| Apple iPhone 12 Pro Max | 
| Apple iPhone 12 Mini | 
| Apple iPhone X | 
| Apple iPhone XR | 
| Apple iPhone XS | 
| Apple iPhone XS Max | 
创建MatchManager
using UnityEngine;
using UnityEngine.UI;
public enum MatchDimension
{
    None = 1,
    Width = 2,
    Height = 3
}
public class MatchManager : MonoBehaviour
{
    private CanvasScaler _canvasScaler;
    private RectTransform _canvasRect;
    const float DesignW = 1080;
    const float DesignH = 1920;
    
    private float DesignRatio;   //标准参考比率
    private float screenWidth;   //手机屏幕宽度
    private float screenHeight;  //手机屏幕高度
    
    private float safeAreaX;
    private float safeAreaY;       
    private float safeAreaWidth;   //手机安全区域宽度
    private float safeAreaHeight;  //手机安全区域高度
    
    private float CanvasRectWidth => _canvasRect == null ? DesignW : _canvasRect.rect.width;
    private float CanvasRectHeight => _canvasRect == null ? DesignH : _canvasRect.rect.height;
    
    void Start()
    {
        _canvasScaler = GetComponent<CanvasScaler>();
        _canvasRect = GetComponent<RectTransform>();
        if (_canvasScaler == null)
        {
            var canvasObj = GameObject.Find("/GameRoot/UIRoot");
            if(canvasObj!=null)
            {
                _canvasScaler = canvasObj.GetComponent(typeof(CanvasScaler))as CanvasScaler;
                _canvasRect = canvasObj.GetComponent<RectTransform>();
            }
        }
        if (_canvasScaler.matchWidthOrHeight == 0)
        {
            if (CanvasRectHeight <= DesignH)
            {
                //需要改为高度适配
                ChangeReference((int)MatchDimension.Height);
            }
        }
        else if (NumberTool.IsSameFloat(_canvasScaler.matchWidthOrHeight,1))
        {
            if (CanvasRectWidth <= DesignH)
            {
                //需要改为宽度适配
                ChangeReference((int)MatchDimension.Width);
            }
        }
        InitConfigData();
    }
    
    /// <summary>
    /// 初始化适配时需要的配置数据
    /// </summary>
    private void InitConfigData()
    {
        DesignRatio = DesignW / DesignH;
        var factRatio = CanvasRectHeight / Screen.height;
        safeAreaX = Screen.safeArea.x*factRatio;
        safeAreaY = Screen.safeArea.y*factRatio;
        safeAreaWidth = Screen.safeArea.width*factRatio;
        safeAreaHeight = Screen.safeArea.height*factRatio;
        screenWidth = CanvasRectWidth;
        screenHeight = CanvasRectHeight;
    }
    /// <summary>
    /// 切换参考维度
    /// </summary>
    public void ChangeReference(int md)
    {
        if (_canvasScaler == null)
        {
            return;
        }
        if (md == (int)MatchDimension.Width)
        {
            _canvasScaler.matchWidthOrHeight = 0;
        }
        else if (md == (int)MatchDimension.Height)
        {
            _canvasScaler.matchWidthOrHeight = 1;
        }
    }
    
    /// <summary>
    /// 限制UI元素在安全区域
    /// </summary>
    public void LimitSafeArea(RectTransform rect)
    {
        var offset = screenHeight - safeAreaHeight;
        if (safeAreaY == 0)
        {
            if (offset > 0)
            {
                //仅需适配屏幕top
                rect.offsetMax = new Vector2(rect.offsetMax.x, rect.offsetMax.y-offset);
            }
        }
        else
        {
            if ((int)offset == (int)safeAreaY)
            {
                //仅需适配屏幕bottom
                rect.offsetMin = new Vector2(rect.offsetMin.x, rect.offsetMin.y + offset);
            }
            else if ((int)offset > (int)safeAreaY)
            {
                //需适配屏幕top和bottom
                var bottomOffset = safeAreaY;
                var topOffset = screenHeight - safeAreaHeight - bottomOffset;
                rect.offsetMin = new Vector2(rect.offsetMin.x, rect.offsetMin.y + bottomOffset);
                rect.offsetMax = new Vector2(rect.offsetMax.x, rect.offsetMax.y -topOffset);
            }
        }
    }
}
将该脚本附加到Canvas节点上

创建MatchUIContent
using UnityEngine;
public class MatchUIContent : MonoBehaviour
{
    void Start()
    {
        var rect = GetComponent<RectTransform>();
        MatchManager.Instance.LimitSafeArea(rect);
    }
}在界面预制体中创建一个单独的节点main,在该节点下进行界面拼接

把MatchUIContent 脚本附加在Main节点上
UI界面适配
主要是处理界面内UI节点间的对齐关系
主要有16种对齐方式

1、点对齐:长宽不变,位置移动,跟随对齐点适配

2、中心点对齐:长宽不变,位置不移动

3、拉伸对齐:长或宽会变,中心位置不移动,跟随界面拉伸

提示:拼接好界面后,多通过手机模拟器界面查看测试,如果遇到界面不适配的情况,就去进行调整。
