Unity屏幕适配——适配信息计算和安全区域适配

接上篇

Unity屏幕适配------立项时设置_unity 竖屏-CSDN博客文章浏览阅读1.3k次,点赞25次,收藏6次。其中:1334 是设计高2 是Camera(相机)的Size属性用于定义相机视图的垂直大小。这个值实际上是相机视图的一半高度。100 UI坐标系相对世界坐标系的缩放倍数。_unity 竖屏https://blog.csdn.net/NRatel/article/details/146253789意图:根据实际项目,传入屏幕基本信息的获取方法,然后得到以下信息:

Canvas大小

实际屏幕分辨率

实际屏幕宽高比

安全区域(屏幕空间)

安全区域宽高比

设计分辨率

设计宽高比

实际屏幕宽高比类型

安全区域宽高比类型

FitIn模式,Screen相对Design的缩放值

EnvelopeParent模式,Screen相对Design的缩放值

FitIn模式,Canvas相对Design的缩放值

EnvelopeParent模式,Canvas相对Design的缩放值

实际屏幕相对于设计的缩放值(与fitInScale相同)

屏幕空间下,四边安全区域距屏幕边缘的大小
UI空间下,四边安全区域距屏幕边缘的大小
UI空间下的安全区域

----------------------------------- NRatel 割 -----------------------------------

初始化时,需注入的屏幕基本信息的获取接口

cs 复制代码
using UnityEngine;

namespace NRFramework
{
    public interface IScreenInfoGetter
    {
        public Vector2 GetScreenSize();     //每帧检查,注意实现的性能

        public Rect GetSafeArea();

        public Vector2 GetCanvasSize();

        public Vector2 GetDesignSize();
    }
}

提供一个默认的获取方法

cs 复制代码
using UnityEngine;

namespace NRFramework
{
    public class ScreenInfoGetter_Default : IScreenInfoGetter
    {
        public Vector2 GetScreenSize()
        {
            return new Vector2(Screen.width, Screen.height);
        }

        public Rect GetSafeArea()
        {
            return Screen.safeArea;
        }

        public Vector2 GetCanvasSize()
        {
            Canvas uiCanvas = UIManager.GetInstance().uiCanvas;
            RectTransform uiCanvasRT = (RectTransform)uiCanvas.transform;
            return new Vector2(uiCanvasRT.rect.width, uiCanvasRT.rect.height);
        }

        public Vector2 GetDesignSize()
        {
            return UIManager.GetInstance().canvasScaler.referenceResolution;
        }
    }
}

计算类

cs 复制代码
using System;
using UnityEngine;

namespace NRFramework
{
    public enum AspectType
    {
        Wide,
        Tall,
        Standard,
    }

    //注意:
    // Rect 的坐标系(以屏幕左上为原点,向右为x正方向,向下为y正方向)
    // https://docs.unity3d.com/cn/2021.3/ScriptReference/Rect.html
    // 但 Rect safeArea 以屏幕左下为原点,向右为x正方向,向上为y正方向

    public class ScreenAdapter
    {
        static public bool inited { private set; get; }

        static public Vector2 canvasSize { private set; get; }                  //Canvas大小

        static public Vector2 screenSize { private set; get; }                  //实际屏幕分辨率

        static public float screenAspect { private set; get; }                  //实际屏幕宽高比

        static public Rect safeArea { private set; get; }                       //安全区域(屏幕空间)

        static public float safeAreaAspect { private set; get; }                //安全区域宽高比

        static public Vector2 designSize { private set; get; }                  //设计分辨率

        static public float designAspect { private set; get; }                  //设计宽高比

        static public AspectType screenAspectType { private set; get; }         //实际屏幕宽高比类型

        static public AspectType safeAreaAspectType { private set; get; }       //安全区域宽高比类型

        static public float screenFitInScale { private set; get; }              //FitIn模式,Screen相对Design的缩放值

        static public float screenEnvelopeScale { private set; get; }           //EnvelopeParent模式,Screen相对Design的缩放值

        static public float canvasFitInScale { private set; get; }              //FitIn模式,Canvas相对Design的缩放值

        static public float canvasEnvelopeScale { private set; get; }           //EnvelopeParent模式,Canvas相对Design的缩放值

        static public float screenScale { get { return screenFitInScale; } }    //实际屏幕相对于设计的缩放值(与fitInScale相同)

        static private IScreenInfoGetter sm_ScreenInfoGetter;

        static private Vector2 sm_LastScreenSize;

        static public event Action onScreenSizeChanged;                         //屏幕大小变化事件

        static public void Init(IScreenInfoGetter screenInfoGetter = null)
        {
            sm_ScreenInfoGetter = screenInfoGetter ?? new ScreenInfoGetter_Default();

            canvasSize = sm_ScreenInfoGetter.GetCanvasSize();
            designSize = sm_ScreenInfoGetter.GetDesignSize();
            screenSize = sm_ScreenInfoGetter.GetScreenSize();
            safeArea = sm_ScreenInfoGetter.GetSafeArea();

            screenAspect = screenSize.x / screenSize.y;
            safeAreaAspect = safeArea.width / safeArea.height;
            designAspect = designSize.x / designSize.y;

            if (screenAspect > designAspect)
            {
                // 宽类型(实际宽高比更大)
                screenAspectType = AspectType.Wide;

                // 宽类型时FitIn缩放比:Y方向正好填满,X方向不足(即Y方向方向缩放比)
                screenFitInScale = screenSize.x / designSize.y;
                canvasFitInScale = canvasSize.y / designSize.y;

                // 宽类型时Envelope缩放比:X方向正好填满,Y方向超出(即X方向方向缩放比)
                screenEnvelopeScale = screenSize.x / designSize.x;
                canvasEnvelopeScale = canvasSize.x / designSize.x;
            }
            else if (screenAspect < designAspect)
            {
                // 长类型(实际宽高比更小)
                screenAspectType = AspectType.Tall;

                // 长类型时FitIn缩放比:X方向正好填满,Y方向不足(即X方向方向缩放比)
                screenFitInScale = screenSize.x / designSize.x;
                canvasFitInScale = canvasSize.x / designSize.x;

                // 长类型时Envelope缩放比:Y方向正好填满,X方向超出(即Y方向方向缩放比)
                screenEnvelopeScale = screenSize.y / designSize.y;
                canvasEnvelopeScale = canvasSize.y / designSize.y;
            }
            else
            {
                //标准类型(同宽高比)
                screenAspectType = AspectType.Standard;
                screenFitInScale = 1;
                canvasFitInScale = 1;
                screenEnvelopeScale = 1;
                canvasEnvelopeScale = 1;
            }

            if (safeAreaAspect > designAspect)
            {
                // 宽类型(实际宽高比更大)
                safeAreaAspectType = AspectType.Wide;
            }
            else if (safeAreaAspect < designAspect)
            {
                // 长类型(实际宽高比更小)
                safeAreaAspectType = AspectType.Tall;
            }
            else
            {
                //标准类型(同宽高比)
                safeAreaAspectType = AspectType.Standard;
            }

            inited = true;
            sm_LastScreenSize = screenSize;

            //Debug.Log($"screenSize: {screenSize}");
            //Debug.Log($"safeArea: {safeArea}");
            //Debug.Log($"screenType: {screenAspectType}");
            //Debug.Log($"safeAspectType: {safeAreaAspectType}");
        }

        //按需,可在Imit后,由外部Update驱动检查
        static public void CheckScreenSizeChange()
        {
            Vector2 newScreenSize = sm_ScreenInfoGetter.GetScreenSize();
            if (newScreenSize.x == sm_LastScreenSize.x && newScreenSize.y == sm_LastScreenSize.y) { return; }
            sm_LastScreenSize = newScreenSize;

            Canvas.ForceUpdateCanvases();   //强刷Canvas
            Init();                         //重新初始化
            onScreenSizeChanged?.Invoke();  //回调变化
        }

        //屏幕空间下,四边安全区域距屏幕边缘的大小
        static public void GetSafeEdgeSize_InScreen(out float notchOffset, out float homeIndicatorOffset, out float side1Offset, out float side2Offset)
        {
            notchOffset = screenSize.y - safeArea.yMax;
            homeIndicatorOffset = safeArea.y;
            side1Offset = safeArea.x;
            side2Offset = screenSize.x - safeArea.xMax;
        }

        //UI空间下,四边安全区域距屏幕边缘的大小
        static public void GetSafeEdgeSize_InUI(out float notchOffset, out float homeIndicatorOffset, out float side1Offset, out float side2Offset)
        {
            GetSafeEdgeSize_InScreen(out float notchOffset_InScreen, out float homeIndicatorOffset_InScreen, out float side1Offset_InScreen, out float side2Offset_InScreen);

            notchOffset = notchOffset_InScreen / screenScale;
            homeIndicatorOffset = homeIndicatorOffset_InScreen / screenScale;
            side1Offset = side1Offset_InScreen / screenScale;
            side2Offset = side2Offset_InScreen / screenScale;
        }

        //UI空间下的安全区域
        static public Rect GetSafeArea_InUI()
        {
            GetSafeEdgeSize_InUI(out float notchOffset_InDesign, out float homeIndicatorOffset_InDesign, out float side1Offset_InDesign, out float side2Offset_InDesign);
            return new Rect()
            {
                x = (side1Offset_InDesign - side2Offset_InDesign) / 2,                                      //(左边-右边)/2
                y = (homeIndicatorOffset_InDesign - notchOffset_InDesign) / 2,                              //(下边-上边)/2
                width = designSize.x - (side1Offset_InDesign + side2Offset_InDesign),                 //设计宽-(左边+右边)
                height = designSize.y - (homeIndicatorOffset_InDesign + notchOffset_InDesign),        //设计高-(下边+上边)
            };
        }
    }
}

注意:

1、维护了一个 onScreenSizeChanged,供编辑器下/折叠屏分辨率切换时使用

2、屏幕分辨率切换时,需执行一次 Canvas.ForceUpdateCanvases();

3、从屏幕空间到UI空间转换大小时,核心为 / screenScale。(见 GetSafeEdgeSize_InUI 中的实现)。

----------------------------------- NRatel 割 -----------------------------------

经过适配信息计算后,安全区域的适配只需挂一个脚本(目前仅处理了竖屏项目)

此脚本将在运行时,将此节点的大小设到与安全区域相同。

需要显示到安全区域的物体,可以放到此节点下去(如下图的设置按钮,锚点为左上)。

iphoneX 下的效果

相关推荐
向宇it11 小时前
【unity游戏开发——编辑器扩展】EditorApplication公共类处理编辑器生命周期事件、播放模式控制以及各种编辑器状态查询
开发语言·ui·unity·编辑器·游戏引擎
向宇it11 小时前
【unity游戏开发——编辑器扩展】AssetDatabase公共类在编辑器环境中管理和操作项目中的资源
游戏·ui·unity·编辑器·游戏引擎
benben04412 小时前
Unity3D仿星露谷物语开发55之保存游戏到文件
游戏·ui·unity·游戏引擎
子燕若水16 小时前
Unity 快捷键
unity·游戏引擎
下次见咯!18 小时前
Unity-QFramework框架学习-MVC、Command、Event、Utility、System、BindableProperty
unity·mvc·框架·qframework
Tech Synapse20 小时前
开发AR导航助手:ARKit+Unity+Mapbox全流程实战教程
unity·游戏引擎·ar
向宇it2 天前
【unity游戏开发——编辑器扩展】EditorUtility编辑器工具类实现如文件操作、进度条、弹窗等操作
ui·unity·c#·编辑器·游戏引擎
benben0442 天前
Unity3D仿星露谷物语开发54之退出菜单及创建可执行文件
游戏·ui·unity·游戏引擎
小小数媒成员2 天前
Unity—lua基础语法
unity·junit·lua