37.分辨率自适应

37.FGUI基础-Unity中的使用必备-分辨率自适应


37.1 知识点

FairyGUI中的分辨率自适应

  • FairyGUI为手游开发提供了自动适应各种设备分辨率的UI适配策略
  • 意味着开发者只需制作一套UI,即可适配所有分辨率的设备。

分辨率自适应具体步骤

制作UI时应使用一套设计分辨率

  • 应使用一套设计分辨率,以最大的全屏UI界面的大小为标准,如 1136x640、1280x720、1920x1080 等等。
  • 确定设计分辨率时,主要按照宽高比例设计,考虑市面上主流设备的分辨率比例,比如 16:9、19.5:9 等等。目前 iPhone 主流机型的分辨率比例为 19.5:9(2436x1125),建议采用该比例设计。
  • FairyGUI会在 2436x1125 屏幕上自动放大图片,只要差距不是太大,呈现出的画面是完全可以接受的。

在Unity中设置全局缩放方式和设计分辨率

  • 可以通过代码或者UIContentScaler组件设置全局缩放方式和设计分辨率。

    • 适配模式有:
      • MatchWidthOrHeight:取宽和高比例较小的进行缩放,保证内容缩放后始终在屏幕内,可能会留白,但不会超出屏幕范围。
      • MatchWidth:固定取宽的比例进行缩放,高边可能会超出屏幕(适用于竖屏游戏)。
      • MatchHeight:固定取高的比例进行缩放,宽边可能会超出屏幕(适用于横屏游戏)。
  • 使用以下代码将全局缩放方式和设计分辨率设置为1365x768,并应用到GRoot实例:

    GRoot.inst.SetContentScaleFactor(1365, 768, UIContentScaler.ScreenMatchMode.MatchHeight);
    

在FGUI中拼凑UI时设定关联和对齐方式

对于全屏UI,通过代码将组件调整为填满屏幕大小。如果屏幕大小会发生变化,需要设置相关关联。

// 如果屏幕大小会发生变化,需要给组件加上关联设置
var package = UIPackage.AddPackage("UI/FGUI教程");
foreach (var item in package.dependencies)
{
    UIPackage.AddPackage("UI/" + item["name"]);
}

//绑定拓展类
FGUIJiaoChengBinder.BindAll();

//CreateInstance创建面板
UI_Lesson37_FGUIJiChu_UnityZhongDeShiYongBiBei_FenBianLvZiShiYing panel = UI_Lesson37_FGUIJiChu_UnityZhongDeShiYongBiBei_FenBianLvZiShiYing.CreateInstance();

//MakeFullScreen方法 把我们组件的尺寸设置为何我们屏幕的逻辑分辨率一致
panel.MakeFullScreen();

//添加到根
GRoot.inst.AddChild(panel);

//AddRelation方法 将面板组件和GRoot容器进行了宽高关联 容器变化 那么组件也会变化
//我们需要在把它添加到了Groot之中之后 再去设置它
panel.AddRelation(GRoot.inst, RelationType.Size);

37.2 知识点代码

using FairyGUI;
using FGUIJiaoCheng;
using System.Collections;
using System.Collections.Generic;
using Teach;
using UnityEngine;

public class Lesson37_FGUI基础_Unity中的使用必备_分辨率自适应 : MonoBehaviour
{
    void Start()
    {
        #region 知识点一 FairyGUI中的分辨率自适应
        //FairyGUI为手游开发提供了自动适应各种设备分辨率的UI适配策略
        //这意味着开发者只需要制作一套UI,就可以适配所有分辨率的设备
        #endregion

        #region 知识点二 分辨率自适应具体步骤
        //1.制作UI时使用一套设计分辨率(以最大的全屏UI界面的大小为标准)
        //  比如:1136*640、1280*720、1920*1080等等 

        //  如何确定设计分辨率
        //  设计分辨率的确定主要按宽高比例设计 一般考虑市面上主流设备的分辨率比例
        //  比如 16:9、19.5:9等等 目前iphone主流机型分辨率比例为 19.5:9(2436*1125) 建议采用该比例设计(选择较大比值)
        //  采用该比例不是说就用2436*1125来做,因为分辨率越大,意味着图片越大,那么在使用时所占的内存就大
        //  你可以根据自己游戏的实际情况来进行设计  比如 1950*900 也是19.5:9 但是分辨率明显就小了 内存也就小了
        //  当这个UI真正在2436*1125的屏幕上显示时,FairyGUI会自动帮助我们把这个图片进行放大处理
        //  只要差距不是太大,呈现出的画面是完全可以接受的

        //2.在Unity中通过代码或者UIContentScaler组件设置 全局缩放方式和设计分辨率
        //  适配模式
        //  2-1 MatchWidthOrHeight 取宽和高比例较小的进行缩放
        //      例如,设计分辨率是960x640,设备分辨率是1280×720
        //      那么可以得到宽边的比例是1280/960=1.33,高边的比例是720/640=1.125
        //      最后取较小的1.125作为全局缩放系数
        //      这种缩放方式保证内容缩放后始终在屏幕内,可能会留边,但不会超出屏幕看不到
        //  2-2 MatchWidth 固定取宽的比例进行缩放。高边可能会超出屏幕(竖屏游戏建议使用)
        //  2-3 MatchHeight 固定取高的比例进行缩放。宽边可能会超出屏幕(横屏游戏建议使用)
        GRoot.inst.SetContentScaleFactor(1365, 768, UIContentScaler.ScreenMatchMode.MatchHeight);

        //3.在FGUI中拼凑UI时,设定每个元件的关联系统对齐方式

        //4.对于全屏UI来说,通过代码将组件打大小填满屏幕

        //  如果屏幕大小会发生变化,需要给组件加上关联设置
       var package = UIPackage.AddPackage("UI/FGUI教程");
        foreach (var item in package.dependencies)
        {
            UIPackage.AddPackage("UI/" + item["name"]);
        }

        //绑定拓展类
        FGUIJiaoChengBinder.BindAll();

        //CreateInstance创建面板
        UI_Lesson37_FGUIJiChu_UnityZhongDeShiYongBiBei_FenBianLvZiShiYing panel = UI_Lesson37_FGUIJiChu_UnityZhongDeShiYongBiBei_FenBianLvZiShiYing.CreateInstance();

        //MakeFullScreen方法 把我们组件的尺寸设置为何我们屏幕的逻辑分辨率一致
        panel.MakeFullScreen();

        //添加到根
        GRoot.inst.AddChild(panel);

        //AddRelation方法 将面板组件和GRoot容器进行了宽高关联 容器变化 那么组件也会变化
        //我们需要在把它添加到了Groot之中之后 再去设置它
        panel.AddRelation(GRoot.inst, RelationType.Size);

        #endregion
    }
}

37.3 练习题

尝试自己实现一个UI管理器,UI管理器中通过字典存储已经创建的面板,提供显示、隐藏、获取面板的功能

创建UIManager 搞成单例

public class UIManager
{
    private static UIManager instance = new UIManager();
    public static UIManager Instance => instance;
      
    private UIManager()
    {
     
    }
}

定义面板存储字典

private Dictionary<string, GComponent> panelDic = new Dictionary<string, GComponent>();

在构造函数初始化项目的基础设置和注册拖拽类

private UIManager()
{
    // 默认字体
    UIConfig.defaultFont = "Other/STHUPO";

    // 默认音效
    UIPackage.AddPackage("UI/Public");
    UIConfig.buttonSound = (NAudioClip)UIPackage.GetItemAssetByURL("ui://Public/btnMusic");

    // 适配相关的设置
    GRoot.inst.SetContentScaleFactor(1365, 768, UIContentScaler.ScreenMatchMode.MatchHeight);

    // 注册相关的代码
    UIObjectFactory.SetPackageItemExtension("ui://Teach/ExercisesPanel", typeof(ExercisesPanel));
    UIObjectFactory.SetPackageItemExtension("ui://Teach/ExercisesBagPanel", typeof(ExercisesBagPanel));
}

定义显示面板方法

// 显示面板
public T ShowPanel<T>(string packageName) where T : GComponent
{
    // 定义规则 组件名 面板类名 字典的key 是一致的 
    Type panelType = typeof(T);
    string panelName = panelType.Name;

    // 如果字典中有该面板的名字 证明已经创建过了 直接返回即可
    if (panelDic.ContainsKey(panelName))
    {
        panelDic[panelName].visible = true;
        return panelDic[panelName] as T;
    }

    // 加载包和依赖包 
    // 由于从Resources文件夹中加载包 会帮助我们判断重复没有 所以 这里既是重复执行也没什么问题
    UIPackage package = UIPackage.AddPackage("UI/" + packageName);
    foreach (var item in package.dependencies)
    {
        UIPackage.AddPackage("UI/" + item["name"]);
    }

    // 创建组件面板
    GComponent panel = UIPackage.CreateObject(packageName, panelName).asCom;

    // 把组件的尺寸设置的和逻辑分辨率一致
    panel.MakeFullScreen();

    // 添加到根对象
    GRoot.inst.AddChild(panel);

    // 和根对象建立 宽高关联 这样 分辨率变化时 面板也不会出问题
    panel.AddRelation(GRoot.inst, RelationType.Size);

    // 把当前显示的面板存起来 用于之后的隐藏
    panelDic.Add(panelName, panel);

    // 把父类转换成对应的 子类
    return panel as T;
}

修改Exercises脚本和ExercisesPanel脚本有用到的显示面板的逻辑

ExercisesPanel panel = UIManager.Instance.ShowPanel<ExercisesPanel>("Teach");
panel.SetPlayer(player);

m_btnBag.onClick.Add(() =>
{
    // ExercisesBagPanel bagPanel = ExercisesBagPanel.CreateInstance();
    // GRoot.inst.AddChild(bagPanel);
    // 显示背包面板
    UIManager.Instance.ShowPanel<ExercisesBagPanel>("Teach");
});

定义隐藏面板方法

// 隐藏面板
public void HidePanel<T>(bool isDispose = false) where T : GComponent
{
    // 得到key
    Type panelType = typeof(T);
    string panelName = panelType.Name;

    // 如果没有面板当前面板  就直接返回
    if (!panelDic.ContainsKey(panelName))
        return;

    // 希望移除面板
    if (isDispose)
    {
        // 移除面板 并且从字典中移除
        panelDic[panelName].Dispose();
        panelDic.Remove(panelName);
    }
    // 希望只是失活
    else
    {
        panelDic[panelName].visible = false;
    }
}

ExercisesBagPanel添加隐藏按钮和他的监听,调用隐藏方法

m_btnBack.onClick.Add(() => {
    // 隐藏背包面板
    UIManager.Instance.HidePanel<ExercisesBagPanel>();
});

定义获取面板方法

// 获得面板
public T GetPanel<T>() where T : GComponent
{
    Type panelType = typeof(T);
    string panelName = panelType.Name;
    // 如果有这个面板 直接返回
    if (panelDic.ContainsKey(panelName))
        return panelDic[panelName] as T;

    return null;
}

定义清空面板方法

// 清空面板
public void ClearPanel(bool isGC = false)
{
    // 主要用于销毁所有面板 和 资源垃圾回收的方法

    // 销毁所有面板 并且清空字典
    foreach (var item in panelDic.Values)
    {
        item.Dispose();
    }
    panelDic.Clear();

    if (isGC)
    {
        // 释放所有包资源
        UIPackage.RemoveAllPackages();
        // 垃圾回收
        GC.Collect();
    }
}

37.4 练习题代码

UIManager

using FairyGUI;
using System;
using System.Collections;
using System.Collections.Generic;
using Teach;
using UnityEngine;

//尝试自己实现一个UI管理器
//UI管理器中通过字典存储已经创建的面板
//提供显示、隐藏、获取面板的功能

public class UIManager
{
    private static UIManager instance = new UIManager();
    public static UIManager Instance => instance;

    private UIManager()
    {
        //默认字体
        UIConfig.defaultFont = "Other/STHUPO";

        //默认音效
        UIPackage.AddPackage("UI/Public");
        UIConfig.buttonSound = (NAudioClip)UIPackage.GetItemAssetByURL("ui://Public/btnMusic");

        //适配相关的设置
        GRoot.inst.SetContentScaleFactor(1365, 768, UIContentScaler.ScreenMatchMode.MatchHeight);

        //注册相关的代码
        UIObjectFactory.SetPackageItemExtension("ui://Teach/ExercisesPanel", typeof(ExercisesPanel));
        UIObjectFactory.SetPackageItemExtension("ui://Teach/ExercisesBagPanel", typeof(ExercisesBagPanel));
    }

    //用于存储已经显示的 UI面板
    private Dictionary<string, GComponent> panelDic = new Dictionary<string, GComponent>();

    //显示面板
    public T ShowPanel<T>(string packageName) where T : GComponent
    {
        //定义规则 组件名 面板类名 字典的key 是一致的 
        Type panelType = typeof(T);
        string panelName = panelType.Name;

        //如果字典中有该面板的名字 证明已经创建过了 直接返回即可
        if (panelDic.ContainsKey(panelName))
        {
            panelDic[panelName].visible = true;
            return panelDic[panelName] as T;
        }

        //加载包和依赖包 
        //由于从Resources文件夹中加载包 会帮助我们判断重复没有 所以 这里既是重复执行也没什么问题
        UIPackage package = UIPackage.AddPackage("UI/" + packageName);
        foreach (var item in package.dependencies)
        {
            UIPackage.AddPackage("UI/" + item["name"]);
        }

        //创建组件面板
        GComponent panel = UIPackage.CreateObject(packageName, panelName).asCom;

        //把组件的尺寸设置的和逻辑分辨率一致
        panel.MakeFullScreen();

        //添加到根对象
        GRoot.inst.AddChild(panel);

        //和根对象建立 宽高关联 这样 分辨率变化时 面板也不会出问题
        panel.AddRelation(GRoot.inst, RelationType.Size);

        //把当前显示的面板存起来 用于之后的隐藏
        panelDic.Add(panelName, panel);

        //把父类转换成对应的 子类
        return panel as T;
    }

    //隐藏面板
    public void HidePanel<T>(bool isDispose = false) where T : GComponent
    {
        //得到key
        Type panelType = typeof(T);
        string panelName = panelType.Name;

        //如果没有面板当前面板  就直接返回
        if (!panelDic.ContainsKey(panelName))
            return;

        //希望移除面板
        if (isDispose)
        {
            //移除面板 并且从字典中移除
            panelDic[panelName].Dispose();
            panelDic.Remove(panelName);
        }
        //希望只是失活
        else
        {
            panelDic[panelName].visible = false;
        }
    }

    //获得面板
    public T GetPanel<T>() where T : GComponent
    {
        Type panelType = typeof(T);
        string panelName = panelType.Name;
        //如果有这个面板 直接返回
        if (panelDic.ContainsKey(panelName))
            return panelDic[panelName] as T;

        return null;
    }

    //清空面板
    public void ClearPanel(bool isGC = false)
    {
        //主要用于销毁所有面板 和 资源垃圾回收的方法

        //销毁所有面板 并且清空字典
        foreach (var item in panelDic.Values)
        {
            item.Dispose();
        }
        panelDic.Clear();

        if (isGC)
        {
            //释放所有包资源
            UIPackage.RemoveAllPackages();
            //垃圾回收
            GC.Collect();
        }
    }
}

Exercises

using FairyGUI;
using System.Collections;
using System.Collections.Generic;
using Teach;
using UnityEngine;

public class Exercises : MonoBehaviour
{
    public Player player;

    void Start()
    {
        ExercisesPanel panel = UIManager.Instance.ShowPanel<ExercisesPanel>("Teach");
        panel.SetPlayer(player);
    }

    public static void ClickLinkOpenPanel(EventContext eventData)
    {
        string info = eventData.data.ToString();
        switch (info)
        {
            case "1":
                print("打开任务面板");
                break;
            case "2":
                print("打开装备面板");
                break;
            case "3":
                print("打开PVP面板");
                break;
            case "4":
                print("打开商店面板");
                break;
            default:
                print("数据未知");
                break;
        }
    }
}

ExercisesPanel

/** This is an automatically generated class by FairyGUI. Please do not modify it. **/

using FairyGUI;
using FairyGUI.Utils;

namespace Teach
{
    public partial class ExercisesPanel : GComponent
    {
        public Controller m_RadioGroup;
        public GRichTextField m_richTxt1;
        public GRichTextField m_richTxt2;
        public GTextInput m_inputTxt;
        public GTextField m_txtInfo;
        public GTextField m_txtName;
        public GButton m_btnFire;
        public GButton m_checkBoxMusic;
        public GButton m_radioBoxOpen;
        public GButton m_radioBoxClose;
        public GSlider m_soundSlider;
        public GButton m_btnBag;
        public const string URL = "ui://wrs19joehkfz2s";

        private Player player;

        public static ExercisesPanel CreateInstance()
        {
            return (ExercisesPanel)UIPackage.CreateObject("Teach", "ExercisesPanel");
        }

        public override void ConstructFromXML(XML xml)
        {
            base.ConstructFromXML(xml);

            m_RadioGroup = GetControllerAt(0);
            m_richTxt1 = (GRichTextField)GetChildAt(0);
            m_richTxt2 = (GRichTextField)GetChildAt(1);
            m_inputTxt = (GTextInput)GetChildAt(3);
            m_txtInfo = (GTextField)GetChildAt(4);
            m_txtName = (GTextField)GetChildAt(7);
            m_btnFire = (GButton)GetChildAt(15);
            m_checkBoxMusic = (GButton)GetChildAt(16);
            m_radioBoxOpen = (GButton)GetChildAt(17);
            m_radioBoxClose = (GButton)GetChildAt(18);
            m_soundSlider = (GSlider)GetChildAt(19);
            m_btnBag = (GButton)GetChildAt(20);

            m_richTxt1.onClickLink.Add(Exercises.ClickLinkOpenPanel);
            m_richTxt2.onClickLink.Add(Exercises.ClickLinkOpenPanel);

            //输入文本练习题相关
            m_inputTxt.onChanged.Add(() =>
            {
                //让文本控件的内容和输入的内容一致
                m_txtInfo.text = m_inputTxt.text;
            });

            m_btnFire.onClick.Add(() =>
            {
                player.Fire();
            });

            m_checkBoxMusic.onChanged.Add(() =>
            {
                if (m_checkBoxMusic.selected)
                    GRoot.inst.EnableSound();
                else
                    GRoot.inst.DisableSound();
            });

            Controller c = this.GetController("RadioGroup");
            c.onChanged.Add(() =>
            {
                if (c.selectedIndex == 0)
                    GRoot.inst.EnableSound();
                else
                    GRoot.inst.DisableSound();
            });

            m_soundSlider.onChanged.Add(() => {
                GRoot.inst.soundVolume = (float)m_soundSlider.value;
            });

            m_btnBag.onClick.Add(() =>
            {
                //ExercisesBagPanel bagPanel = ExercisesBagPanel.CreateInstance();
                //GRoot.inst.AddChild(bagPanel);
                //显示背包面板
                UIManager.Instance.ShowPanel<ExercisesBagPanel>("Teach");
            });
        }



        public void SetPlayer(Player player)
        {
            this.player = player;
        }
    }
}

ExercisesBagPanel

/** This is an automatically generated class by FairyGUI. Please do not modify it. **/

using FairyGUI;
using FairyGUI.Utils;

namespace Teach
{
    public partial class ExercisesBagPanel : GComponent
    {
        public GList m_svTest;
        public GButton m_btnBack;
        public const string URL = "ui://wrs19joe5b993r";

        public static ExercisesBagPanel CreateInstance()
        {
            return (ExercisesBagPanel)UIPackage.CreateObject("Teach", "ExercisesBagPanel");
        }

        public override void ConstructFromXML(XML xml)
        {
            base.ConstructFromXML(xml);

            m_svTest = (GList)GetChildAt(3);
            m_btnBack = (GButton)GetChildAt(4);

            //for (int i = 0; i < 50; i++)
            //{
            //    //方式一:
            //    //GObject img = UIPackage.CreateObject("Teach", "quanlity_3");
            //    //m_svTest.AddChild(img);

            //    //方式二:(建议使用)
            //    //创建从缓存池中取  之后 移除时  也用对应方法 放入缓存池 这样更加节约性能
            //    m_svTest.AddItemFromPool();
            //}

            //方式三:
            m_svTest.itemRenderer = RenderItem;
            //如果要显示的内容对象多 那么可以设置为虚拟列表
            m_svTest.SetVirtual();
            m_svTest.numItems = 50;


            m_btnBack.onClick.Add(()=> {
                //隐藏背包面板
                UIManager.Instance.HidePanel<ExercisesBagPanel>();
            });
        }

        private void RenderItem(int index, GObject obj)
        {
            //在这里面对每一个item进行初始化操作
        }
    }
}


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 785293209@qq.com

×

喜欢就点赞,疼爱就打赏