谈谈为什么要用FairlyGUI以及UGUI的自适应与FairlyGUI的自适应

之前刚来现在这个项目的时候,我就跟我们的主程提过使用FGUI去替代UGUI的事情。 原因有以下:

  1. 可以让策划与美术参与到UI的制作中, 并且贯穿整个游戏开发的全程。 对于游戏项目来说,UI开发是必不可少且十分费时间的一件事情,而且在一个功能模块在完成之后, 逻辑的大改的次数可能不是特别多, 但是UI大改的次数, 我觉得至少都得3次以上。 至少我到现在,没有遇到那个游戏项目的UI的改版是在3次以下。 所以让UI的制作能脱离程序环节是必不可少的。
  2. 上面一点说到UI脱离程序环节的的重要性,可能会有童鞋说,UGUI 和NGUI也可以让策划参与制作啊, 也能让程序不脱离拼UI的苦逼境界。 确实,这点我无法反驳。 但是我可以用实际的例子举例, 我的第二个游戏项目就是使用的Unity, 而且UI是用的UGUI, 那个时候我们使用的Unity的版本是4.6. 我们的UI的制作是全部由策划去拼的,程序不用参与UI制作当中,确实节省了程序大量的时间,但是或多或少还是有一些缺点。说说策划参与UGUI制作我遇到的问题:
    • 但是因为UGUI的节点是由父子关系的, 而且程序在写逻辑代码的时候大概率会依赖于这个UI结构。所以需要策划先去学习Unity的UGUI的层级的概念, 而且还有一些复杂的结构,所以一般情况在策划制作完成UI之后,程序可能都需要简单的修改修改,而且策划同时还要管理Unity的UI的UI的合理规划,所以对于策划来说技术门槛有点高。
    • 当进行UI修改的时候, 难免会修改到UI的层级路径, 因为我们是项目纯lua开发。所以UI逻辑也是lua依靠find写的。所以一旦策划修改层级路径之后,就会出现运行不了的Bug。
    • UGUI的DrawCall的合并依赖于UGUI层级的规划,层级规划的好与不好的情况,一个复杂的UI界面的Drawcall应该会多10-20个。
    • UGUI的重绘也是依赖UGUI的Canvas, 即每次重绘都是整个Canvas一起重绘。所以需要对UI的设计有较好的规划,比如一个复杂的界面是一个单独的Canvas,这样在UI重绘的时候,不会引起其它的界面重绘,但这个与项目是怎么显示和隐藏界面的方法有很大的关系,这里就不展开了。
  3. FGUI是仿照Adobe系列制作的UI以及功能设计,同时也是所见即所得的方式,而且还能够编辑器进行简单的测试。能够很方便的使美术参与到游戏UI的设计修改中, 不仅仅是策划参与,也给予了美术足够高的参与度。这是UGUI不能提供的一个很便利的方式,因为UI美术基本是不可能去学或者说学不会Unity的操作,而且流程确实过于复杂。
  4. FGUI的资源管理很方便,美术只要自己规划的好, 就不会出现资源重复,或者找不到等等问题, 因为编辑器是怎么放置资源,怎么引用资源,公有资源放在哪里,独有资源放哪里,在编辑器中都是一目了然。都很方便,还有其它很多比较方便的功能,需要大家自己去体验和学习。
  5. 还有一个很重要的原因,也是我个人坚决想要推行FGUI的原因。 就是针对于初级开发者来说, 做UI这个活是又脏又累,而且还学不到太多东西, 花的也很多。 但是你说你不做呢, 有不太可能, 因为游戏项目,UI的开发比重其实占比是很大的。最后还是勉为其难的做下去,导致个人觉得自己在干了1-2年之后技术实力还是没有什么提高,最后对UI开发又了很强的抵触心理。 我自己开发UI的时间不长, 除了第一家公司的时候, 全公司基本就我一个写UI, 而且是使用Cocos2dx用C++手写UI之后,就基本没有再写过什么UI模块了, 一般是开发底层框架和开发工具居多。但是这样的事情我看的很多,又很多感受。 我觉得做一个leader,一定要考虑下面兄弟们的感受, 要让有自己在公司工作技术能有所精进, 合理安排工作。

我记得我在和我们Leader在讨论这个问题的时候,我给他讲了一些FGUI的好处, 很明显,他很也意动, 但是因为他可能需要考虑很多其它的问题, 所以也不敢下决定, 所以让我给他演示一下FGUI的一些简单的用法与操作, 做一个UGUI的ScrollRectList无限滚动列表。 很可惜,我自己在尝试使用FGUI并没有专门去研究这个, 我只是看过FGUI的UnitySDK的源码, 觉得代码不错,而且扩展方便, 在使用上至少是一个可控级别的。 所以我没有办法给他简单的展示这个例子, 我告诉他FGUI的例子中有, 但是我估计他是觉得连我都不知道怎么去用,就向他热情的安利, 本来就摇摆的心, 直接就给出了拒绝的答复。 之后项目就开始大量进人手, 在之后就开始使用uGUI进行大量的开发,想要调整,也需要很长的时间,所以也就搁置了这个提议。

其实我个人在他拒绝这个提议的时候,是很伤感的。 为什么呢? 因为在我加入到这个团队的时候, 团队属于初创, 当时才3个人。而且UI的东西完全就没有动起来。 我觉得完全可以拿出一周左右的时间,或者派专人, 或者他自己亲自去调研,体验一下这个东西的利弊, 然后再做决定。 谈起这个,说说我成都的老东家, 当时属于第一次使用Unity做游戏, 也是一个初创团队,我也很有幸再立项之前就加入到了团队中。 也是我在项目中提议使用 当时特别新的技术 ulua 做游戏开发以及基础框架, 同时做热更新。我记得那个时候是14年, 当我提出来之后, CTO,主程还有我们大家都亲自去试验了一把, 最后大家一致决定采用(原来的公司有PC开发背景,使用lua特别多)。 在这里给我们点个赞。 对比起来,现在的项目Leader不够果断。虽然可能有一些客观原因。我个人也是很理解,但是我还是希望这个东西能融进项目中来, 因为这个融入进来之后有一个很大的作用。

最后再说说我自己在搭建自己的框架的时候使用FGUI进行开发的时候,遇到的一些小小困扰:

  1. 首先其冲其实就是不管用什么UI开发游戏都会遇到,适配的问题。在FGUI的官网上, 我看到了FGUI有专门针对Unity的教程,而且教程里面也有一篇专门将适配的文章, 因为我自己之前没有做过适配,也没有太多经验,一直都很疑惑FGUI的适配到底应该去做。 在谈FGUI的适配的时候, 我们简单看看我们在UGUI中是如何去做UI适配的。
    不管是在那个项目中,在UI上面应该都有这么一个概念, 那么就是什么是设计分辨率, 什么是运行分辨率。 举例:
    在我们设计UI的时候, 所有UI的最大尺寸按照 1334x750 去设计。 1334x750 就是我们的设计分辨率。 然后当我们的游戏运行在比如说 Iponex 上面,Iponex的分辨率为: 2436x1125 那么这个2436x1125就是我们的运行分辨率。 这个时候我们的设计分辨率跟运行分辨率就产生不一致, 这个时候我们就需要对我们的界面进行一个整体的缩放,来达到运行分辨率靠近设计分辨率的目的。 在Unity中有一个这个组件Canvas Scaler的组件,这个组件就是用来干这个用的,看下图:
    谈谈为什么要用FairlyGUI以及UGUI的自适应与FairlyGUI的自适应_第1张图片
    图中1的 Reference Resolution: 表示我们的设计分辨率,在这里我填的是1334x750。 Screen Math Mode:表示我们的整体适配的方式。 这里我选的是以宽度或者高度适配, 下面的进度表示宽和高各自在适配中的占比。MathMode还有其他的选项, 一个是Expand: 表示以扩展的形式进行整体适配,这种方式的好处是保证所有的UI元素,全在内部, 但是会留边, 同时Canvas的大小会比设计分辨率的大小大一点。
    谈谈为什么要用FairlyGUI以及UGUI的自适应与FairlyGUI的自适应_第2张图片 一个是Shrink: 表示以缩放的形式进行整体适配。看下图,这种方式不会留边,但是会让UI元素有可能会超出屏幕, 特别是设置为跟随界面扩展的UI元素,同时Canvas的大小会比设计分辨率小一点。
    谈谈为什么要用FairlyGUI以及UGUI的自适应与FairlyGUI的自适应_第3张图片

基于以上的3个种整体适配方式,就可以做我们的ugui的适配。 我们先只好整体适配方式, 然后对于一些比较普通的UI元素, 是不需要关心适配的详细情况的, 对于需要匹配全屏的界面我们只需要设置为随着屏幕扩展就可以了。然后需要处理的是一些特殊情况只有, 需要固定位置的UI元素, 比如我有一个聊天界面, 这个聊天界面是永远固定在屏幕坐下角的, 这个时候就需要使用UGUI提供的锚点的方式处理, 将这个UI元素锚在右下角。 (如果对这个不太理解,估计你还需要再研究一段UI制作和写一段时间的UI)
这应该就是UGUI的最简单的适配方式。

下面我们再来看FGUI的适配, FGUI的适配与UGUI的大同小异的。FGUI提供一个组件名为: UI Content Scaler
谈谈为什么要用FairlyGUI以及UGUI的自适应与FairlyGUI的自适应_第4张图片
图中: Scale Mode 表示你的整体适配方式: 提供选项与Unity的组件差不太多,一般我们选择 Scale With Screen Size , 采用缩放屏幕的方式进行整体适配. Design Resolution 为项目的设计分辨率,会以为基准做缩放处理, Screen Math Mode 为缩放基准选择: 1. 以高度或者宽度适配 2. 以宽度适配 3. 以高度适配。 FGUI没有提供相比于Unity那么完善的配置, 但是这些已经足够我们做适配了。 一般情况下我们都选以高度或者宽度适配来进行适配。
同样的,对于普通的UI元素我们不用考虑适配的问题。 我们只需考虑需要规定位置的UI元素和全屏的UI元素。
这个时候请允许我直接引用官网上面的内容,用于描述我们要做的适配的问题:

全屏界面适配

全局缩放后,大部分UI都不需要做任何调整,只有一个例外,就是设计为全屏的界面。在上例中,在设计分辨率下,全屏界面的大小是960x640,我们也是按这个大小设计全屏组件的。全局缩放后,这时逻辑屏幕的大小变成1138x640了,那大小就不一致了。这是我们需要重新调整组件大小使之满屏,即
//设置组件全屏,即大小和逻辑屏幕大小一致。
//组件的内部应该做好关联处理, 以应对大小改变。
aComponent.SetSize(GRoot.inst.width, GRoot.inst.height);
//或者更简洁的方式
aComponnet.MakeFullScreen();
当然,这仅仅是处理全屏界面的一种方式。在有的情况下,例如如果选用“MatchHeight”模式,也就是高度优先的适配方法,这种方法保证了UI界面垂直方向的内容总是铺满,而水平方向就有可能超出屏幕。这种适配方式需要设计师有“安全区域”的设计思维,不能安排内容在超出屏幕的部分。例如,将全屏界面居中,牺牲掉两边的内容:
aComponent.x = (GRoot.inst.width – aComponent.width)/2;
这样,左边缘和右边缘将会被屏幕边缘裁剪掉,这要求设计师在设计时就考虑到这种情况。
自动调整UI布局

全屏组件在适配过程中需要重新设置全屏,那么组件大小就会发生变化,这时需要使用关联系统让组件内的元素自动布局在正确的位置。实例可以参考关联系统。
全屏界面适配

全局缩放后,大部分UI都不需要做任何调整,只有一个例外,就是设计为全屏的界面。在上例中,在设计分辨率下,全屏界面的大小是960x640,我们也是按这个大小设计全屏组件的。全局缩放后,这时逻辑屏幕的大小变成1138x640了,那大小就不一致了。这是我们需要重新调整组件大小使之满屏,即
//设置组件全屏,即大小和逻辑屏幕大小一致。
//组件的内部应该做好关联处理, 以应对大小改变。
aComponent.SetSize(GRoot.inst.width, GRoot.inst.height);
//或者更简洁的方式
aComponnet.MakeFullScreen();
当然,这仅仅是处理全屏界面的一种方式。在有的情况下,例如如果选用“MatchHeight”模式,也就是高度优先的适配方法,这种方法保证了UI界面垂直方向的内容总是铺满,而水平方向就有可能超出屏幕。这种适配方式需要设计师有“安全区域”的设计思维,不能安排内容在超出屏幕的部分。例如,将全屏界面居中,牺牲掉两边的内容:
aComponent.x = (GRoot.inst.width – aComponent.width)/2;
这样,左边缘和右边缘将会被屏幕边缘裁剪掉,这要求设计师在设计时就考虑到这种情况。

自动调整UI布局

全屏组件在适配过程中需要重新设置全屏,那么组件大小就会发生变化,这时需要使用关联系统让组件内的元素自动布局在正确的位置。实例可以参考关联系统。

所以对于FGUI来说, 使用自己设置全局界面 来替代Unity的自扩张的UI锚点设计, 使用关联系统来替代UGUI的锚点定位功能。

但是再我实际的使用中, 我发现官网的方法在使用有一些困扰。 比如我当前的运行分辨率是跟之前一样是2280x1242, 根据官网的方式去设置, 我会把我需要设置的组件的大小设置成为 2280x1242。 这我想象的不太一样, 我需要的应该是类似Unity的Expand模式下的那种, 以设计的分辨率为基准,经过一点扩张或者缩小正好满足铺满整个屏幕的的分辨率。 因为假如我们底图不是九宫格类型的话, 当进行这样设置之后, 如果设计分辨率和运行分辨率相差太大的话,会导致大量内容看不到, 所以我应该计算当前屏幕经过整体缩放之后的分辨率。然后将我们的全屏界面设置为当前分辨率。

function UIMgr.InitSize()
    UIMgr.DesignWidth = 1334
    UIMgr.DesiginHeight = 750
    scale = math.max(UIMgr.DesiginHeight / GRoot.inst.height,   UIMgr.DesignWidth / GRoot.inst.width)
    UIMgr.width  = GRoot.inst.width * scale + 2
    UIMgr.height = GRoot.inst.height * scale + 2
end

大致代码就在上面了, 我们通过这样计算出当前屏幕的分辨率,然后设置给需要表示为全屏的界面,为什么要+2的原因,是因为这里计算出来数值是个浮点数,会不精确,可能会导致界面出现很小黑边。

function BaseView:MaskBgFull()
    self:MaskAsFull("full_bg")
end

function BaseView:MaskAsFull( name )
    local aObject = self.m_goView:GetChild(name)
    if aObject then
        aObject:SetSize(UIMgr.width, UIMgr.height)
        aObject:SetPosition(0,0,0)
    end
end

上面是我们调用的代码, 在基类封装一个方法, 如果有需要设置为全屏幕的UI元素就调用这个方法, 同时约定好如果有需要设置为全屏幕的背景, 那么在制作UI的时候就设置名字为: full_bg。这个方法会在初始化的时候进行调用。

以上就是我理解的UGUI的适配方法 和 FGUI的适配方法。

诸君共勉。

你可能感兴趣的:(Unity游戏开发)