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 下的效果

相关推荐
凡情17 小时前
android隐私合规检测
android·unity
小贺儿开发17 小时前
Unity3D 本地 Stable Diffusion 文生图效果演示
人工智能·unity·stable diffusion·文生图·ai绘画·本地化
mxwin1 天前
Unity Shader 半透明物体为什么不能写入深度缓冲?
unity·游戏引擎·shader
晚枫歌F1 天前
三层时间轮的实现
网络·unity·游戏引擎
咸鱼永不翻身1 天前
Lua脚本事件检查工具
unity·lua·工具
leo__5202 天前
单载波中继系统资源分配算法MATLAB仿真程序
算法·matlab·unity
努力长头发的程序猿2 天前
Unity使用ScriptableObject序列化资源
unity·游戏引擎
mxwin2 天前
Unity Shader 手写基于 PBR 的 URP Lit Shader 核心光照计算
unity·游戏引擎·shader
小贺儿开发2 天前
Unity3D 智能云端数字标牌系统
unity·阿里云·人机交互·视频·oss·广告·互动
魔士于安2 天前
Unity windows 同步 异步 打开文件文件夹工具
游戏·unity·游戏引擎·贴图·模型