微信扫一扫 分享朋友圈

已有 2405 人浏览分享

开启左侧

【转载】Unity UGUI 屏幕适配

[复制链接]
2405 0

本帖最后由 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都无需再考虑适配的逻辑。仅需要使用锚点等方法进行处理。 首先搭建一个测试场景如下: 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;
    }
}

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

0

关注

0

粉丝

12

主题
精彩推荐
热门资讯
网友晒图
图文推荐
  • iOS App

  • 安卓App

Archiver|手机版|小黑屋|技你太美101

GMT+8, 2024-11-21 17:09 , Processed in 0.106836 second(s), 31 queries .

Powered by 技你太美101

© 2024 JNTM101 Team