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

相关推荐
心前阳光7 小时前
Unity WebGL文本输入
unity·游戏引擎·webgl
天涯过客TYGK9 小时前
unity A星寻路
unity·游戏引擎
KhalilRuan9 小时前
Unity Demo——3D平台跳跃游戏笔记
笔记·游戏·unity·游戏引擎
ttod_qzstudio1 天前
Unity中使用EzySlice实现模型切割与UV控制完全指南
unity
南無忘码至尊1 天前
Unity 实现与 Ollama API 交互的实时流式响应处理
unity·游戏引擎·交互
平行云1 天前
如何实现UE程序大并发多集群的像素流部署
unity·ue5·图形渲染
向宇it2 天前
【unity小技巧】在 Unity 中将 2D 精灵添加到 3D 游戏中,并实现阴影投射效果,实现类《八分旅人》《饥荒》等等的2.5D游戏效果
游戏·3d·unity·编辑器·游戏引擎·材质
向宇it2 天前
Unity Universal Render Pipeline/Lit光照材质介绍
游戏·unity·c#·游戏引擎·材质
__water3 天前
RHA《Unity兼容AndroidStudio打Apk包》
android·unity·jdk·游戏引擎·sdk·打包·androidstudio
两水先木示3 天前
【Unity3D】微信小游戏适配安全区域或胶囊控件(圆圈按钮)水平高度一致方案
unity·微信小游戏·安全区域·ui适配·胶囊控件·safearea