Unity UGUI 中butto 的transition的使用 的Animation 怎么用

关于Unity中的UGUI优化,你可能遇到这些问题 - UWA Blog
一、界面制作
Q1:UGUI里的这个选项 ,应该是ETC2拆分Alpha通道的意思,但是在使用中并没起作用?请问有没有什么拆分的标准和特别要求呢?
据我们所知,alpha split 的功能最初只对 Unity 2D 的 Sprite(SpriteRenderer)有完整的支持,而UI的支持是在Unity 5.4版本之后的。建议大家在Unity 5.4版本以后的UGUI中尝试该功能。
Q2:在UI界面中,用Canvas还是用RectTransform做根节点更好?哪种方法效率更高?
Canvas划分是个很大的话题。简单来说,因为一个Canvas下的所有UI元素都是合在一个Mesh中的,过大的Mesh在更新时开销很大,所以一般建议每个较复杂的UI界面,都自成一个Canvas(可以是子Canvas),在UI界面很复杂时,甚至要划分更多的子Canvas。同时还要注意动态元素和静态元素的分离,因为动态元素会导致Canvas的mesh的更新。最后,Canvas又不能细分的太多,因为会导致Draw Call的上升。我们后续将对UI模块做具体的讲解,尽请期待。
Q3:UWA性能检测报告中的Shared UI Mesh表示什么呢?
Shared UI Mesh是在Unity 5.2 版本后UGUI系统维护的UI Mesh。在以前的版本中,UGUI会为每一个Canvas维护一个Mesh(名为BatchedMesh,其中再按材质分为不同的SubMesh)。而在Unity 5.2版本后,UGUI底层引入了多线程机制,而其Mesh的维护也发生了改变,目前Shared UI Mesh作为静态全局变量,由底层直接维护,其大小与当前场景中所有激活的UI元素所生成的网格数相关。
一般来说当界面上UI元素较多,或者文字较多时该值都会较高,在使用UI/Effect/shadow和UI/Effect/Outline时需要注意该值,因为这两个Effect会明显增加文字所带来的网格数。
Q4:在使用NGUI时,我们通常会将很多小图打成一个大的图集,以优化内存和Draw Call。而在UGUI时代,UI所使用的Image必须是Sprite;Unity提供了SpritePacker。 它的工作流程和UGUI Atlas Paker有较大的差别。在Unity Asset中,我们压根看不到图集的存在。 问题是:
1. SpritePacker大概的工作机制是什么样的?
2. 如果Sprite没有打包成AssetBundle,直接在GameObject上引用,那么在Build时Unity会将分散的Sprite拼接在一起么?如果没有拼接,那SpritePacker是不是只会优化Draw Call,内存占用上和不用SpritePacker的分离图效果一样?
3. 如果将Sprite打成AssetBundle,AssetBundle中的资源是分散的Sprite吗?如果不是,不同的AssetBundle中引用了两张Sprite,这两张Sprite恰好用SpritePacker拼在了一起,是不是就会存在两份拼接的Sprite集?
4. 如果想使用NGUI Atlas Packer的工作流程,该如何去实现?
简单来说,UGUI和 NGUI 类似,但是更加自动化。只需要通过设定 Packing Tag 即可指定哪些 Sprite 放在同一个 Atlas 下。
可以通过 Edit -& Project Settings -& Editor -& Sprite Packer 的 Mode 来设置是否起效,何时起效(一种是进入 Play Mode 就生效,一种是 Build 时才生效)。所以只要不选 Disabled,Build 时就会把分散的 Sprite 拼起来。
可以认为 Sprite 就是一个壳子,实际上本身不包含纹理资源,所以打包的时候会把Atlas 打进去。如果不用依赖打包,那么分开打两个 Sprite 就意味各自的AssetBundle 里都会有一个 Atlas。
可以通过第三方工具(如 Texture Packer)制作 Atlas,导出 Sprite 信息(如,第 N 个 Sprite 的 Offset 和 Width,Height 等),然后在 Unity 中通过脚本将该 Atlas 转成一个 Multiple Mode 的 Sprite 纹理(即一张纹理上包含了多个 Sprite),同时禁用 Unity 的 Sprite Packer 即可。
两种做法各有利弊,建议分析一下两种做法对于自身项目的合适程度来进行选择。
Q5:在Unity 5.x版本下,我们在用UGUI的过程中发现它把图集都打进了包里,这样就不能自动更新了,请问图集怎么做自动更新呢?
在Unity 5.x中UGUI使用的Atlas确实是不可见的,因此无法直接将其独立打包。但我们建议,可以把Packing Tag相同的源纹理文件,打到同一个AssetBundle中(设置一样的AssetBundle Name),从而避免Atlas的冗余。同时这样打包可以让依赖它的Canvas的打包更加自由,即不需要把依赖它的Canvas都打在一个AssetBundle中,在更新时直接更新Atlas所在的AssetBundle即可。
Q6:ScrollRect在滚动的时候,会产生Canvas.SendwillRenderCanvases,有办法消除吗?
ScrollRect在滚动时,会产生OnTransformChanged的开销,这是UI元素在移动时触发的,但通常这不会触发Canvas.SendWillRenderCanvases。
如果观察到Canvas.SendWillRenderCanvases耗时较高,可以检查下ScrollRect所在的Canvas是否开启了Pixel Perfect的选项,该选项的开启会导致UI元素在发生位移时,其长宽会被进行微调(为了对其像素),而ScrollRect中通常有较多的UI元素,从而产生较高的Canvas.SendWillRenderCanvases开销。因此可以尝试关闭Pixel Perfect看效果是否可以接受,或者尝试在滚动过程中暂时关闭Pixel Perfect等方式来消除其开销。
二、网格重建
Q1:我在UGUI里更改了Image的Color属性,那么Canvas是否会重建?我只想借用它的Color做Animation里的变化量。
如果修改的是Image组件上的Color属性,其原理是修改顶点色,因此是会引起网格的Rebuild的(即Canvas.BuildBatch操作,同时也会有Canvas.SendWillRenderCanvases的开销)。而通过修改顶点色来实现UI元素变色的好处在于,修改顶点色可以保证其材质不变,因此不会产生额外的Draw Call。
Q2:Unity自带的UI Shader处理颜色时,改_Color属性不会触发顶点重建吗?
在UI的默认Shader中存在一个Tint Color的变量,正常情况下,该值为常数(1,1,1),且并不会被修改。如果是用脚本访问Image的Material,并修改其上的Tint Color属性时,对UI元素产生的网格信息并没有影响,因此就不会引起网格的Rebuild。但这样做因为修改了材质,所以会增加一个Draw Call。
Q3:能否就UGUI Batch提出一些建议呢?是否有一些Batch的规则?
在 UGUI 中,Batch是以Canvas为单位的,即在同一个Canvas下的UI元素最终都会被Batch到同一个Mesh中。而在Batch前,UGUI会根据这些UI元素的材质(通常就是Atlas)以及渲染顺序进行重排,在不改变渲染结果的前提下,尽可能将相同材质的UI元素合并在同一个SubMesh中,从而把DrawCall降到最低。而Batch的操作只会在UI元素发生变化时才进行,且合成的Mesh越大,操作的耗时也就越大。
因此,我们建议尽可能把频繁变化(位置,颜色,长宽等)的UI元素从复杂的Canvas中分离出来,从而避免复杂的Canvas频繁重建。
Q4:我用的是UGUI Canvas,Unity 5.3.4版本,请问如何查看每次Rebuild Batch影响的顶点数, Memory Profiler是个办法但是不好定位。
由于Unity引擎在5.2后开始使用Shared UI Mesh来存储UI Mesh,所以确实很难查看每次Rebuild的UI顶点数。但是,研发团队可以尝试通过Frame Debugger工具对UI界面进行进一步的查看。
Q5:动静分离或者多Canvas带来性能提升的理论基础是什么呢?如果静态部分不变动,整个Canvas就不刷新了?
在UGUI中,网格的更新或重建(为了尽可能合并UI部分的DrawCall)是以Canvas为单位的,且只在其中的UI元素发生变动(位置、颜色等)时才会进行。因此,将动态UI元素与静态UI元素分离后,可以将动态UI元素的变化所引起的网格更新或重建所涉及到的范围变小,从而降低一定的开销。而静态UI元素所在的Canvas则不会出现网格更新和重建的开销。
Q6:UWA建议“尽可能将静态UI元素和频繁变化的动态UI元素分开,存放于不同的Panel下。同时,对于不同频率的动态元素也建议存放于不同的Panel中。”那么请问,如果把特效放在Panel里面,需要把特效拆到动态的里面吗?
通常特效是指粒子系统,而粒子系统的渲染和UI是独立的,仅能通过Render Order来改变两者的渲染顺序,而粒子系统的变化并不会引起UI部分的重建,因此特效的放置并没有特殊的要求。
Q7:多人同屏的时候,人物移动会使得头顶上的名字Mesh重组,从而导致较为严重的卡顿,请问一下是否有优化的办法?
如果是用UGUI开发的,当头顶文字数量较多时,确实很容易引起性能问题,可以考虑从以下几点入手进行优化:
尽可能避免使用UI/Effect,特别是Outline,会使得文本的Mesh增加4倍,导致UI重建开销明显增大;
拆分Canvas,将屏幕中所有的头顶文字进行分组,放在不同的Canvas下,一方面可以降低更新的频率(如果分组中没有文字移动,该组就不会重建),另一方面可以减小重建时涉及到的Mesh大小(重建是以Canvas为单位进行的);
降低移动中的文字的更新频率,可以考虑在文字移动的距离超过一个阈值时才真正进行位移,从而可以从概率上降低Canvas更新的频率。
三、界面切换
Q1:游戏中出现UI界面重叠,该怎么处理较好?比如当前有一个全屏显示的UI界面,点其中一个按钮会再起一个全屏界面,并把第一个UI界面盖住。我现在的做法是把被覆盖的界面 SetActive(False),但发现后续 SetActive(True) 的时候会有 GC.Alloc 产生。这种情况下,希望既降低 Batches 又降低 GC Alloc 的话,有什么推荐的方案吗?
可以尝试通过添加一个 Layer 如 OutUI, 且在 Camera 的 Culling Mask 中将其取消勾选(即不渲染该 Layer)。从而在 UI 界面切换时,直接通过修改 Canvas 的 Layer 来实现“隐藏”。但需要注意事件的屏蔽,禁用动态的 UI 元素等等。
这种做法的优点在于切换时基本没有开销,也不会产生多余的 Draw Call,但缺点在于“隐藏时”依然还会有一定的持续开销(通常不太大),而其对应的 Mesh 也会始终存在于内存中(通常也不太大)。
以上的方式可供参考,而性能影响依旧是需要视具体情况而定。
Q2:如图,我们在UI打开或者移动到某处的时候经常会观测到CPU上的冲激,经过进一步观察发现是因为Instantiate产生了大量的GC。想请问下Instantiate是否应该产生GC呢?我们能否通过资源制作上的调整来避免这样的GC呢?如下图,因为一次性产生若干MB的GC在直观感受上还是很可观的。
准确的说这些 GC Alloc 并不是由Instantiate 直接引起的,而是因为被实例化出来的组件会进行 OnEnable 操作,而在 OnEnable 操作中产生了 GC,比如以上图中的函数为例:
上图中的 Text.OnEnable 是在实例化一个 UI 界面时,UI 中的文本(即 Text 组件)进行了 OnEnable 操作,其中主要是初始化文本网格的信息(每个文字所在的网格顶点,UV,顶点色等等属性),而这些信息都是储存在数组中(即堆内存中),所以文本越多,堆内存开销越大。但这是不可避免的,只能尽量减少出现次数。
因此,我们不建议通过 Instantiate/Destroy 来处理切换频繁的 UI 界面,而是通过 SetActive(true/false),甚至是直接移动 UI 的方式,以避免反复地造成堆内存开销。
四、加载相关
Q1:UGUI的图集操作中我们有这么一个问题,加载完一张图集后,使用这个方式获取其中一张图的信息:assetBundle.Load (subFile, typeof (Sprite)) as S 这样会复制出一个新贴图(图集中的子图),不知道有什么办法可以不用复制新的子图,而是直接使用图集资源 。
经过测试,这确实是 Unity 在 4.x 版本中的一个缺陷,理论上这张“新贴图(图集中的子图)”是不需要的,并不应该加载。 因此,我们建议通过以下方法来绕过该问题:
在 assetBundle.Load (subFile, typeof (Sprite)) as S 之后,调用
Texture2D t = assetBundle.Load (subFile, typeof (Texture2D)) as Texture2D;
Resources.UnloadAsset(t);
从而卸载这部分多余的内存。
Q2:加载UI预制的时候,如果把特效放到预制里,会导致加载非常耗时。怎么优化这个加载时间呢?
UI和特效(粒子系统)的加载开销在多数项目中都占据较高的CPU耗时。UI界面的实例化和加载耗时主要由以下几个方面构成:
纹理资源加载耗时
UI界面加载的主要耗时开销,因为在其资源加载过程中,时常伴有大量较大分辨率的Atlas纹理加载,我们在之前的有详细讲解。对此,我们建议研发团队在美术质量允许的情况下,尽可能对UI纹理进行简化,从而加快UI界面的加载效率。
UI网格重建耗时
UI界面在实例化或Active时,往往会造成Canvas(UGUI)或Panel(NGUI)中UIDrawCall的变化,进而触发网格重建操作。当Canvas或Panel中网格量较大时,其重建开销也会随之较大。
UI相关构造函数和初始化操作开销
这部分是指UI底层类在实例化时的ctor开销,以及OnEnable和OnDisable的自身开销。
上述2和3主要为引擎或插件的自身逻辑开销,因此,我们应该尽可能避免或降低这两个操作的发生频率。我们的建议如下:
在内存允许的情况下,对于UI界面进行缓存。尽可能减少UI界面相关资源的重复加载以及相关类的重复初始化;
根据UI界面的使用频率,使用更为合适的切换方式。比如移进移出或使用Culling Layer来实现UI界面的切换效果等,从而降低UI界面的加载耗时,提升切换的流畅度。
对于特效(特别是粒子特效)来说,我们暂时并没有发现将UI界面和特效耦合在一起,其加载耗时会大于二者分别加载的耗时总和。因此,我们仅从优化粒子系统加载效率的角度来回答这个问题。粒子系统的加载开销,就目前来看,主要和其本身组件的反序列化耗时和加载数量相关。对于反序列化耗时而言,这是Unity引擎负责粒子系统的自身加载开销,开发者可以控制的空间并不大。对于加载数量,则是开发者需要密切关注的,因为在我们目前看到的项目中,不少都存在大量的粒子系统加载,有些项目的数量甚至超过1000个,如下图所示。因此,建议研发团队密切关注自身项目中粒子系统的数量使用情况。一般来说,建议我们建议粒子系统使用数量的峰值控制在400以下。
Q3:我有一个UI预设,它使用了一个图集, 我在打包的时候把图集和UI一起打成了AssetBundle。我在加载生成了GameObject后立刻卸载了AssetBundle对象, 但是当我后面再销毁GameObject的时候发现图集依然存在,这是什么情况呢?
这是很可能出现的。unload(false)卸载AssetBundle并不会销毁其加载的资源 ,是必须对其调用Resources.UnloadAsset,或者调用Resources.UnloadUnusedAssets才行。关于AssetBundle加载的详细解释可以参考我们之前的文章:
五、字体相关
Q1:我在用Profiler真机查看iPhone App时,发现第一次打开某些UI时,Font.CacheFontForText占用时间超过2s,这块主要是由什么影响的?若iPhone5在这个接口消耗2s多,是不是问题很大?这个消耗和已经生成的RenderTexture的大小有关吗?
Font.CacheFontForText主要是指生成动态字体Font Texture的开销, 一次性打开UI界面中的文字越多,其开销越大。如果该项占用时间超过2s,那么确实是挺大的,这个消耗也与已经生成的Font Texture有关系。简单来说,它主要是看目前Font Texture中是否有地方可以容下接下来的文字,如果容不下才会进行一步扩大Font Texture,从而造成了性能开销。
你或许感兴趣的文章
手机扫描下方二维码在Unity3D中使用uGUI实现3D旋转特效
最近一位朋友问我,如何在Unity引擎中实现类似《英雄联盟》中选择皮肤时的3D滚动视图效果,虽然我非常不喜欢这个游戏,可是大学四年在宿舍里被周围同学们耳濡目染,对这个游戏中常见英雄的口头禅还是颇为熟悉的,曾经在周围同学的&硝烟&和&噪杂&中熬夜,此时此刻想起来大概是最能让我怀念和骄傲的记忆了。剑圣说&你的剑就是我的剑&,伊泽瑞尔说&是时候表演真正的技术了&,杰斯说&为了更美好的明天而战&&&或许曾经的某一瞬间,我们曾经有过类似的让你我疯狂着迷的人生信条,可是不管怎样,我希望我们可以将这些永远地铭刻在心里,如同心中栽种下一棵红莲,在黑夜中静静地等待开放,这样当此去经年亦或时过境迁的时候,我们不会说是时光抹去了你我年轻的棱角,因为我相信真正的棱角会因为磨砺而变得更加明亮,绝对不会因为此刻的苟且就变的麻木甚至迷茫。好了,喝完我这碗心灵鸡汤,下面我们来一起学习如何在Unity3D中使用uGUI实现3D滚动视图效果。
??首先,我们先来对这个需求进行分析,从这篇文章的题目我们获得的一个关键信息是,希望通过某种方式实现3D滚动特效。因此我们首先要解决的一个问题是,我们应该采用2D方式来实现还是采用3D方式来实现这种界面效果。我们假定这里希望实现的效果如下图所示,我们可以注意到从这张图片的设计初衷来看,它更像是一种介绍产品特性的文案设计,我们这里仅仅是想通过这张图告诉大家,我们需要实现一个什么样的效果。软件开发过程中最大的成本在我看来主要来自沟通。因为事实上对普通用户而言技术并不重要,重要的是能否实现用户想要的功能,可是大部分情形是用户并不知道自己想要什么,除非你将实际的产品放到用户眼前甚至手中。好了,在对需求有了一个基本的印象以后,我们来思考如何实现这个需求。
??具体来讲,我们有两种思路:
* 其一是采用真实的3D来制作,即我们通过一个圆柱体或者是多棱柱将图片&粘贴&在不同的面上,通过对圆柱体或者多棱柱进行旋转,然后以真实的3D的形式来呈现给用户。
* 其二是采用伪3D来制作,即我们通过在2D平面内对图片的层次进行合理化调整实现伪3D效果,配合插值、缩放等技巧来实现2D平面上的旋转,然后给用户一种视觉上的3D效果。
??在这里我们选择采用伪3D来制作,为什么选择这种方案呢?因为它简单啊,哈哈。好了,我们现在将实际的需求进行抽象,我们会发现什么呢?我们注意到这本质上是一个曲线问题,我们可以将每个图片的中心用平滑的曲线连接起来,然后我们就得到了一条抛物线或者是圆锥曲线或者是贝塞尔曲线,在这里我们将其理解为什么样的曲线并不重要,因为这最终影响到的是曲线的平滑度问题,即细节上的调整。沿着这个思路,我们就意识到,这是一个根据曲线平均分布坐标点的过程,假设我们这里5张图片,并且曲线在中间位置可以找到一条垂直的对称轴,那么我们只需要将这5个点在水平方向上平均分布即可,事实上根据人类视觉的特点,这个距离应该是越来越小的,就像我们看到的一排并列的树木,越远的地方它们的间距会越来越小,而事实上它们的间距是一样的,根据这个特性我们可以表现出这种视觉上的纵深的感觉,在实际项目中它取决于美术设定和策划设定,我们这里就从最简单的情况开始分析。
??好了,在解决了精灵放置的这个问题以后,我们接下来要解决的是什么呢?答案是精灵的层级,因为层级能够帮助我们营造一种视觉上的层次感和立体感,比如在跑酷游戏中我们常常使用视差滚动这种技术来表现3D效果,以及传统的斜45度瓦片地图来实现2.5D效果都是使用2D来模拟3D效果的经典案例。所以在这里除了确定每个精灵的放置位置以外,我们还有一个问题,如何对这些精灵进行排序,所幸的是在uGUI中我们可以通过SetSiblingIndex方法来设置一个精灵的深度,当每次通过按钮切换精灵的时候,我们都需要对所有精灵重新计算坐标和深度,而为了更好的视觉表现力,我们可以在切换的时候做一个简单的位移动画,至此我们就可以开始动手实现功能啦。
??首先我们来搭建一个基本的场景,我们这里将一切浮华褪尽,我们可以看到在场景中有两个按钮,它们可以让我们当前选中的卡片,而界面底部的标签会显示我们当前选择的角色名称。虽然在这里采用触屏滑动的效果更好,可我们这里主要的目的是为了说明如何实现我们的思路,当引入这部分功能的设计以后,会增加大家在整体理解上的难度,所以我们这里以快速实现功能为主。注意到场景中的卡片此时都是相当&任性&地放置在界面上,这是因为我们稍后会采用算法计算每个卡片的实际位置,所以在这里完全可以忽略其&美观性&。
??这里,我们设定场景的大小为800x460,那么在这种情况下,我们可以按照下面图中所示的曲线轨迹来构造一条曲线,考虑到椭圆方程比贝塞尔曲线更加简单易用,所以我们这里选择椭圆方程来作为场景中这些卡片排列的曲线方程。
vcq9vfjQ0MXFwdDAsqGjz8LD5tK7xvDAtL+0tPrC68jnus7Ktc/Wo7o8L3A+DQo8cHJlIGNsYXNzPQ=="brush:">
//初始化精灵数组
int childCount = transform.childC
//计算两侧精灵数目
halfSize = (childCount-1)/2;
//初始化精灵
sprites = new GameObject[childCount];
for(int i=0;i
这里sprites显然是一个GameObject[],因为卡片的数目为奇数个,所以halfSize是指中间位置卡片的索引,这里需要两个辅助方法,SetPosition和SetDeepin,从名字我们就知道这两个方法分别是设置卡片位置和设置卡片深度。当我们提到代码注释的时候,好多人以代码自注释为理解逃避注释,孰不知这建立在命名规范的基础上,如果你连这点基本的要求都做不到,我建议你还是多写点注释、少写点代码。好了,这两个方法的实现细节如下:
/// 设置精灵位置
private void SetPosition(int index)
//计算第index个精灵的角度
float angle = 0.0f;
if (index & halfSize) {
angle = startAngle - (halfSize - index) * DeltaA
} else if (index & halfSize) {
angle = startAngle + (index - halfSize) * DeltaA
angle = startA
//计算第index个精灵的坐标
float x = A* Mathf.Cos((angle/180) * Mathf.PI) + Center.x;
float y = B* Mathf.Sin((angle/180) * Mathf.PI) + Center.y;
Vector3 v3 = Camera.main.WorldToScreenPoint(new Vector3(x,y,0));
v3 = Camera.main.ScreenToWorldPoint(v3);
Vector2 v2 = new Vector2(v3.x,v3.y);
sprites[index].GetComponent().anchoredPosition = v2;
可以注意到,在这里我们根据精灵索引index和两侧精灵数目halfSize的关系,按照DeltaAngle这个增量来计算每个精灵实际的角度,在此基础上结合椭圆的参数方程,我们可以非常容易地计算出每个精灵实际的位置,这样就可以保证精灵中心都在椭圆曲线上。好了,接下来我们会遇到一个新的问题,这些精灵的层级应该是从中间位置向两边依次递减的,所以为了解决这个问题,我们还需要对每个精灵的层级进行计算,这部分代码的实现细节如下:
/// 设置精灵深度
private void SetDeepin(int index)
//计算精灵深度
int deepin = 0;
if(indexhalfSize){
deepin = sprites.Length-(1+index);
deepin = halfS
sprites[index].GetComponent().SetSiblingIndex(deepin);
事实上,我在这里并不清楚SetSiblingIndex这个方法的真正作用:),可是它的确能够实现我们想要的功能。有时候在维护一个古老的项目的时候,可能你会在代码中看到各种有趣的注释,而这些注释中有相当一些都充满了一种&形而上学&的味道在里面,我们不知道这个世界为什么会是这样,可是看起来它们都运行地非常良好。或许这就是这个世界的奇妙之处,无论我们是否想要尝试打破这些规则,这个世界上总是有些我们难以理解的东西存在,可是存在即合理,不是吗?理性思维的缺陷在于想要为一切问题找到一个答案,所以这次苏格拉没有底,我们就感性一次又何妨呢,这个问题就让它没有答案吧!
??现在,显然我们需要解决一个新的问题,就像上帝在我们关上一扇门的同时,会为我们开启一扇窗口。理论上任何问题都可以通过引入一个中间层来解决,而引入中间层的同时毫无疑问地引入了一个新的问题。在这里我们已经完成了让所有精灵按照椭圆曲线进行排布以及精灵的层级关系这两个问题,可是我们这是一个静态的过程啊,我们需要的是让它能够滚动起来,所以怎么解决这个问题呢?我们可以注意到的一点是,精灵的这种&滚动&效果,实际上是将数组中的第一个元素sprites[0]或者最后一个元素sprites[sprites.Length-1],依次和数组中的第i个元素进行交换。比如精灵整体向右侧&滚动&,我们只需要从第一个元素开始依次和最后一个元素进行交换就可以啦,所以这里的实现实际上是:
/// 向后翻页
public void OnNext()
int length = sprites.L
for(int i=0;i
我们在对数组内的元素重新组织后,需要重新计算每个精灵的位置和深度。我这里在思考的一个问题是:精灵的位置和深度实际上是确定的,所以我们可以考虑将它们存储起来&复用&,这样可以减少每次的重复计算。其实,代码的优化和重构是一个需要时间来酝酿的过程,没有人能够在写代码的时候,就可以意识到代码中的瑕疵,而这种发现问题的眼光通常需要长时间的培养,这是我们之所以提倡不要过早优化的原因,除非你能够快速地找到代码中的优化点。好了,现在采用类似的思路,我们可以实现向前翻页的逻辑啦,这里的代码非常简单不再赘述。
??好了,现在我们可以看看到目前为止我们实现了一个怎样的功能吧!
其实这篇文章我还想继续再往下写的,可是因为我比较懒一直拖着不写,以及接下来相当多的内容都是和界面相关的东西,所以我决定这篇文章就暂时写到这里,目前这个方案可以实现一个简单的&3D&滚动的效果,按照这个思路,接下来我们要做的事情是让滚动更加平滑以及支持鼠标或者触屏操作,毕竟这个需求的出发点是来自一个游戏,所以我们可以考虑在&滚动&的时候增加插值特性,与此同时,为了让它更加具有&3D&的感觉,可以在设置精灵层级的时候为不同的精灵设置不同的缩放比例,这样会更加符合美术中的透视关系,效果应该会更好吧!我认识的一位朋友使用uGUI中原生控件ScrollRect实现了类似的功能,感觉她还是非常厉害的啊,果然我不再从事Unity开发以后,我在这块的技术完全跟不上整个技术圈的节奏啊。
??本文介绍了一种基于曲线方程来构建伪3D效果的思路,主要借助椭圆的参数方程来计算精灵位置,使其实现按照椭圆曲线进行排布的效果,在此基础上配合层级调整、插值、缩放等技巧,在一定程度上可以实现2D平面内的伪3D旋转效果。因为博主身患拖延症晚期,所以这篇文章在拖延了很久以后,终于成功的成为了一个没有填完的坑,不过我相信掌握原理比获取代码更为重要,所以这篇文章更多的是希望能给大家提供相关思路,博主在这篇文章中没有实现的功能,各位读者有兴趣的话可以考虑自行实现,写完这篇文章表示心好累,好了,就这样吧,各位晚安!}

我要回帖

更多关于 transition的使用 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信