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