记录生活,记录成长,记录经验,记录技术。
1.MonoBehaviour的脚本生命周期
2.图片压缩格式?PC,android,IOS
3.纹理加载进内存以后占用内存如何计算?
4.UGUI常用优化技巧?
1.MonoBehaviour的脚本生命周期
1、场景第一次加载时,为场景中每个对象去调用事件函数(初始化)
Awake:实例化对象之后才会调用(非活动状态的对象,只有激活时,才会调用)
OnEnable:(对象处于激活后,才会调用SetActive)
OnLevelWasLoaded:告知游戏已加载新关卡(场景)
2、Editor编辑器模式下
Reset:使用Reset命令初始化脚本属性
3、第一帧更新前(初始化)
Start:当脚本启用实例后,才会在第一帧更新前调用Start
3、更新顺序
游戏逻辑、交互、动画、摄像机位置,使用不同事件函数,绝大多数逻辑在Update
FixedUpdate(物理Physics):固定更新频率,用作于物理渲染。调用此函数之后会执行物理计算和更新,Fixedupdate里计算运动,无需乘以Time.deltaTime,因为已有独立可靠计时器。调用FixedUpdate频率高于Update。
Update:每一帧调用一次Update,帧更新主要函数,执行绝大多数逻辑。
LateUpdate:每一帧在Update调用完毕后,调用LateUpdate。LateUpdate常用用途是跟随第三人称摄像机,保证角色在Update中的移动和旋转,在LateUpdate中执行所有摄像机的移动和旋转计算。确保角色在摄像机跟踪器位置之前已完全移动。
4、协程
Update 函数返回后将运行正常协程更新。协程是一个可暂停执行 (yield) 直到给定的 YieldInstruction 达到完成状态的函数。
协程的不同用法:
yield 在下一帧上调用所有 Update 函数后,协程将继续。
yield WaitForSeconds 在为帧调用所有 Update 函数后,在指定的时间延迟后继续协程
yield WaitForFixedUpdate 在所有脚本上调用所有 FixedUpdate 后继续协程
yield WWW 在 WWW 下载完成后继续。
yield StartCoroutine 将协程链接起来,并会等待 MyFunc 协程先完成。
5、销毁对象时
OnDestroy:对象存在的最后一帧完成所有帧更新之后,调用此函数(可能应 Object.Destroy 要求或在场景关闭时销毁该对象)。
6、退出时
在场景中的所有活动对象上调用以下函数:
OnApplicationQuit:在退出应用程序之前在所有游戏对象上调用此函数。在编辑器中,用户停止播放模式时,调用函数。
OnDisable:行为被禁用或处于非活动状态时,调用此函数。
链接: 参考文章1(官方文档).
2.图片压缩格式?(PC,android,IOS)
1、纹理压缩的策略
手游开发(Android/iOS)中,使用3个级别的压缩程度:
高清晰无压缩、中清晰中压缩、低清晰高压缩,下面分别对应。
4种压缩方法:RGBA 32, RGBA16+Dithering,ETC1+Alpha/PVRTC4。
Unity原生不支持Dithering抖动,需要使用TexturePacker工具,Dithering抖动对拉伸放大是不友好的。
ETC1不带透明通道,还需要挂一张ETC1的Alpha通道图,ETC1+Alpha
PVRTC4在Unity中是直接支持的,不过要注意的细节是,它必须是二次方正方形;也就是说,长宽在二次方的同时,还必须要相等。
不同平台的推荐纹理压缩格式
2、纹理压缩格式
DXT格式是Nvidia Tegra提供的
ETC是安卓原生支持的,OPNEGL2.0都支持
ETC2只有OPENGL3.0支持
PVRTC是Imagination PowerVR提供的
ATC是Qualcomm Snapdragon提供的
一般来说,IOS只支持PVRTC的压缩格式。
所有设备对16BITS/ARGB 16BITS/RGB A16BITS/RGB 24BITS/ARGB 32BITS等支持都很好,只是这些格式算是非压缩格式,对内存消耗和渲染消耗非常不友好。
3、几种纹理格式的对比
一个商业项目,混搭多种纹理格式是在所难免的事情。把项目纹理划分成高、中、低三种质量需求。在项目中,尽可能是使用ETC1和PVRTV4等GPU直接支持的图片格式,不仅内存占用低、性能也更好;当出现质量不及格时,再逐步的提升压缩格式,来满足需要。
链接: 参考文章1(官方文档).
链接: 参考文章2.
链接: 参考文章3.
链接: 参考文章4.
3.纹理加载进内存以后占用内存如何计算?
纹理Texture加载进内存后,大小计算公式如下:
纹理内存大小(字节) = 纹理宽度 x 纹理高度 x 像素字节
像素字节 = 像素通道数(R/G/B/A) x 通道大小(1字节/半字节)
举例:比如一个1024 * 1204的RGBA 32bit的纹理占用多大内存?
1024 * 1024 * (4*8bit)/8 byte
4.UGUI常用优化技巧?
1、设置图集可以合批
ProjectSetting>Editor>SpritePacker>Always Enabled (Legacy Sprite Packer)
创建一个SpriteAtlas图集,设置Objects For Packing,将合批的文件拖入
合批的文件中的Sprite中Packing Tag属性设置成SpriteAtlas名称
2、避免不同图集或材质的倾轧OverLay,Scene视图调节为Writeframe模式可以更容易查看
3、倾轧时OverLay,在Hierarchy视图中排序错误,导致合批的步骤增加,注意Hierarchy中顺序,层级关系
4、 Mask会增加一个Draw,并且Mask里面的图片不会和外面的图片合批
5、 Text和RichText区别,富文本会造成三角面增加,版本问题,2019版本不会增加
6、空的Image造成一个Drawcall并且会打断合批
7、颜色渐变和可以用通过脚本控制material材质,本质是更改Tint属性,这样既能满足颜色渐变,又能避免网格频繁重建造
通过 ImageColor 来改变材质球属性,最后达到不重构Mesh的效果。
切换元素的贴图时也一样可以做到不重构的效果,由于贴图更换会导致重构,为了达到不重构的目的可以给一个自定义材质球的并且更换材质球中的贴图。
观察Profiler中,Hierarchy面板下,搜索SendWillRenderCanvases和BuildBatch两个函数
Canvas.SendWillRenderCanvases()主要是计算Canvas下面所有的顶点数据
Canvas.BuildBatch()则是负责进行Mesh绘制与合批
8.、接受射线脚本:Empty4Raycast
使用的此脚本可以在有效接受点击事件的情况下,不增加drawcall和打断合批
public class Empty4Raycast : MaskableGraphic
{
protected Empty4Raycast()
{
useLegacyMeshGeneration = false;
}
//当一个UI元素生成顶点数据时会调用OnPopulateMesh(VertexHelper vh)函数,
//我们可以在这个函数中修改顶点的数据或者获取顶点的数据,这里是清空,不增加drawcall。
protected override void OnPopulateMesh(VertexHelper toFill)
{
toFill.Clear();
}
}
9、减少OverDraw脚本:PolygonImage
OverDraw就是指GPU对屏幕一片区域的重复绘制次数;
慎用Mask组件,它自带两层OverDraw;
慎用Text组件的OutLine和Shadow,Shadow会增加一层OverDraw,而OutLine是复制了四份Shadow实现的;
不使用空白或透明的Image,尽管alpha = 0,还是会渲染并增加一层OverDraw
可以使用Text Mesh Pro性能更好,功能强大,还免费
脚本代码链接: 脚本PolygonImage
10、 关闭不需要的Raycast Target选项(Image、Text、RawImage),降低每帧检测射线的性能消耗。
解决RaycastTarget勾选过多的问题,使用DebugUILine脚本检测,会出现蓝色线框表示勾选了射线检测
static Vector3[] fourCorners = new Vector3[4];
void OnDrawGizmos()
{
foreach (MaskableGraphic g in GameObject.FindObjectsOfType<MaskableGraphic>())
{
if (g.raycastTarget)
{
RectTransform rectTransform = g.transform as RectTransform;
rectTransform.GetWorldCorners(fourCorners);
Gizmos.color = Color.blue;
for (int i = 0; i < 4; i++)
Gizmos.DrawLine(fourCorners[i], fourCorners[(i + 1) % 4]);
}
}
}
11、用多个Canvas将屏幕划分出不同的区域,降低网格重建带来的性能损耗
12. 避免或者降低赋值的操作,降低网格重建频率 例如在Update中执行textComponent.text = “菜鸟小听歌”,如果数值没有变化可以不用赋值,有变化一秒赋值一次
13. Canvas避免使用Blocking Objects来遮挡射线,因为这是一个相当消耗性能的操作
14. Screen Space 和 World Space 中的Camera必须要指定,负责会每帧7-10次调用Object.FindObjectWithTag!!!
15. 频繁需要SetActive的物体可以使用Canvas Group组件,可以有效降低重建消耗
禁用Canvas组件本身可以避免重建消耗来达到隐藏的效果(前提节点没有改变),但是对应脚本上的OnEnable、OnDisable会触发,所以可以使用Canva Group组件达到效果
16. 降低使用字体的种类。字体Font资源需要单独打包,否则会造成字体被重复打包到对应的UI Bundle里面
17. Mask 与 Rect Mask 2D 的区别
Rect Mask 2D
节省 DrawCall 和 Overdraw
增加 Cull 开销(Canvas.SendWillRenderCanvases())
持续开销较低
拖动时开销较高
18、UI动静分离,降低drawcall动态合批
原因:元素移动就会导致Mesh重绘,将动态和静态分离,让合并范围缩小
方法:UGUI使用Canvas节点作为划分,将动态UI元素放入专门Mesh合并的Canvas节点上,将静态UI元素放入专门Mesh合并Canvas节点
动态Canvas
静态Canvas
19、UI预加载
在特殊的空闲时间点预加载资源,不集中在某一个时间点
19、UI字体拆分
字体单独打包,常用字拆出来,单独生成一个字体文件
20、UI滚屏优化
链接: 参考文章1.
链接: 参考文章2 UI优化.