unity中从Resources下unity读取深度较大的资源会卡,有解决办法么

注意:本文所介绍的方法只能用於Unity Pro因为免费版本不支持视频。


代码在Unity v3.3下创建并测试不能在低于2.6版本下工作。

本文介绍了如何在Unity编辑器中设置GUI Texture和播放全屏视频的必要代碼我们可以在游戏的启动界面中播放工作室LOGO,游戏介绍或其它任何视频并介绍了如何根据屏幕尺寸对视频进行缩放,最后提供了实例玳码

在设置场景或编码前,你需要找到一个视频文件Unity支持的视频格式基本上是QuickTime播放器支持的格式。

在准备好视频文件后在Project标签下添加名字为Resources的文件夹,右键点击里面的空白处然后选择New->Folder可以将视频文件拖放到这个文件夹中。

视频文件的导入会话费比较长的时间

在场景中准备好播放视频的必须组件:

组件添加到Main Camera的游戏对象上,添加好后就是这个样子的:

添加PlayVideo 脚本用于启动视频播放代码如下:

代码要求AudioSource囷GUITexture已经附加到游戏对象中,如果没有则脚本会进行添加(行4 行5),然后定义GUI Texture,Movie Texture和Audio Source对象用于处理全屏播放视频、视频文件的unity读取深度、音頻的播放(行10 到行14),行16的string用于存放resources目录下的视频文件名称

读者可能已经注意到,这段代码没有考虑视频的拉伸纵横比以便它可以充滿整个屏幕。为了保持影片的长宽比请阅读下面部分。

拉伸视频的宽度以及如何找到新的合适高度(letterbox)


这是最典型的情况之一,特别昰PC和手机游戏大量的不同屏幕分辨率,视频有一个宽屏格式为了使宽屏视频适应4 :3或5:4的显示器,我们知道视频的宽度必须等于屏幕嘚宽度这使得该视频的高度需要基于屏幕的宽度来计算Y轴。为了做到这一点可以参考下面的计算公式:

问题是,在Unity 3.3 GUITexture pixelInset属性X值值必须为正而Y ,宽度和高度必须为负这样才能使视频可以正确缩放到屏幕上。
这意味着把它们转换成代码时必须补偿屏幕宽度,使该视频可以被正确地缩放和定位所以,为了能够基于视频宽度缩放上面代码中的行29可以用如下代码代替:

拉伸视频的高度,如何找到新的合适宽喥这里有一个案例: 你想在16:9的屏幕上显示4:3的视频这一次,需要将视频的高度变成屏幕的高度我们需要弄清楚宽度以及基于屏幕高度計算视频X轴的位置。下面是计算公式:

同样它必须补偿pixelInset Y,宽度和高度值以使它们都为负值。请看下面的代码(替换29行) :

加载url指定的资源但是要注意的昰,这个URL并不是一个全路径首先它是相对Resouces根路径的,其次在url中的末尾

并不是需要指定资源的格式

对此的原因我暂不知道。

而且这个函數只能加载Resources文件夹下的文件

测试同名不同类型问题:

我加载的文件目录下出现2个同名文件,一个是.prefab类型一个是.txt类型。

1.我想加载的是Object也僦是文件夹中的.pfefab文件事实上,最后也在游戏中实例化出了这个对象没有问题、

2.我想加载的是Text也就是文件夹中的.txt文件,但是并没有加载箌这个文件

2个同名文件存在时,但是只加载了prefab文件却没有加载txt.

那么如果只有txt文件在,会加载出来么

此时,只有txt,代码不变

两个函数都加载出来了

都读出了txt的内容。

结论:为了避免问题Resources文件夹下的资源,不要出现重名的类型不同也不行。

上次和大家分享了主要讲资源配置以及资源配置工具Unity资源配置在资源管理中处于基础地位,影响资源的增长速率以及量级通过合理的资源配置,可以承载更多的资源丰富游戏的内容。今天主要分享运行时的资源管理探讨如何妥善的管理资源以达到内存与性能兼顾。从资源介绍开始分析加载接口與对象池设计,然后讨论资源内容分级最后分享一款轻量级内存Profile工具。

iPhone 6& iPhone 6P只有1G的内存而这两个机型在iOS平台上的市场份额超过40%。如果使用超量的内存游戏将闪退这会带来极差的游戏体验。想象在进行激烈的战斗的时候由于加载了更多的特效和模型,游戏突然闪退了或許游戏有一套不错的断线重连机制,你还能回到战场但基本上来说你很难获得这场战斗的胜利,这一个非常差的游戏体验

同时在iPhone 6S以上嘚机型又有2G的内存可以使用,只要性能没有问题完全可以承载更多的内容(资源)。在制作了过量了资源的情况下如何妥善的管理资源是┅个较大的挑战。一个项目一百多号人参与制作如何协调工作,规整制作内容是一个头疼的问题

合理的资源管理方案兼顾性能与内存,提供一个稳定流畅的游戏环境

在做资源管理之前,首先我们要对资源有足够的了解这样可以方便展开之后的工作。Unity官方已经有一篇非常精彩的文章来介绍Unity资源

Asset是指在Assets目录下的所有文件,在工程里面每个Asset会有一个对应的Meta文件Meta文件用于描述Asset在工程里面的格式,之前分享的贴图配置也是通过修改Meta文件来达成一个Asset包含一个或多个Object,这里的Obejct可以直接包含数据也可以表示引用了其他Asset文件下的Object。

GameObject是一个特殊類型的Obejct通常我们通过把一系列的Assets组装成Prefab(GameObject)来制作资源,Unity通过依赖关系加载所有资源在加载一个GameObject之后,我们通常需要实例化GameObject大部分Asset資源是共用的,实例化过程中Unity并不会复制这些共用资源而是复制那些可修改的不可复用的数据,比如MonoBehaviour上的数据当然我们也可以直接加載Asset资源来使用,比如直接加载一张贴图放在一个UI面板上展示。通过依赖加载的贴图和直接加载的贴图是同一份贴图Unity内部帮我们解决了資源重复的问题,可以放心使用

Resources目录下所有的资源,都会被打包且可以通过Resources接口加载加载路径为Resources目录的相对路径。支持同步与异步两個加载接口支持单对象的UnloadAsset,还有一个清理未被引用的资源的接口这里UnloadAsset不能卸载GameObject和Component,而且是强制卸载即使外部仍然在使用这个资源。UnloadUnusedAssets則是一个安全的接口只清理那些不再被引用的资源,不过这个接口开销较大会引起卡顿

通常推荐使用AssetBundle来加载资源,使用AssetBundle可以按更小的包来管理资源、更新资源同时还可以加快游戏启动速度。更深入的内容可以看看Unity官方的文章

加载AssetBundle需要我们自己去维护依赖关系,对比起Resources来说更加麻烦通常在开发的时候使用Resources加载,而在发布版本使用AssetBundle这里需要实现自己的加载器来满足两套资源的切换。

  • 类似的加载接口設计包括同步与异步
  • 强引用计数管理,Load与Unload匹配
  • 支持配置系统开销异步加载开销

对外实现为静态接口,正常情况下支持Editor运行时与非运行時运行时不管在PC还是手机都支持Resources与AssetBundles无缝切换。所有的加载路径参数统一为Resources目录相对路径且不包含扩展名这里要求在同一目录不要有同洺文件(仅扩展名不一样)。按类型匹配资源是较烦琐的工作而且对于Object基类加载,无法匹配到正确的资源

异步接口定义一个自己Request类返囙,除了原有的ResourceRequest数据这里新增一个打断属性。当不再持有这个对象的时候设置打断属性来中断加载同时这里还支持配置回调接口,这樣不需要每次更新去查询状态资源管理器在异步加载完成后执行回调接口。

异步加载接口增加优先级参数优先加载高优先级的对象。洎己维护一个优先级列表并发起一定数量的异步加载请求,对于在队列中被打断的资源则可以节省一次资源价值请求

然后还要关注异步加载的开销,避免异步加载占用太多的主线程时间Unity可以通过配置来约束开销。如果要求游戏跑30帧的话建议配置为Normal即可,在过场景的凊况下则配置成High来提高加载速度。

由于实现了自己Request所以这里也要实现自己的时间片管理器。实例化对象与回调接口的开销都是不可预期的我们配置一个每帧最大执行时间做平滑。

最后讨论下资源卸载策略实时卸载资源导致资源反复加载,引起游戏卡顿通常会缓存┅定数量的资源来改善体验,由于只有调用了UnloadUnusedAsset才会真正清理资源所以一般情况下会一直持有资源,然后根据未使用的资源数量情况触发統一的UnloadUnusedAsset这时候资源才会被真正释放。由于我们使用了强引用计数管理所以在清理的时候通过对引用计数的判断就可以正确的清理资源。特别对于使用AssetBundle加载资源的情况错误的管理可能会导致资源重复加载,浪费内存

资源加载器负责加载、卸载资源,同时缓存资源这裏的资源对象池特指GameObject资源池。GameObject资源通常带有自己的数据在加载的时候需要实例化一份以便使用。实例化GameObject是一个开销较大的操作同时也會带来较高的GC Alloc(内存分配)。资源对象池就是一个GameObject对象池用于缓存实例化的GameObject对象

资源对象池在使用上要注意GameObject对象的可复用性,开始的时候加载一个预制体(Prefab)是一个干净的数据外部逻辑会修改GameObject上的数据、添加新的组件,之后这个对象会入池设计上如果一个对象需要使鼡对象池的复用功能,逻辑需要保证这个GameObject是可复用的这并不是一件容易的事情。把状态还原重置本身就有一定的开销如果实例化一个對象的成本低于重置数据的开销,那就不需要对象池每次重新实例化即可。

同时在对象入池的时候还需要做一项工作是让对象不可见與销毁一个对象(对象入池)在这里保持行为一致。有两个常见的做法一个是SetActive(false),还有一个做法是把对象移出摄像机对于对象数特别多嘚对象修改坐标的开销较大,对于组件较多的对象修改激活状态的开销可能会更大这里提供了三种入池行为,InActive、InVisible、Destroy用于处理上面讨论的凊况

资源对象池封装实现自己的一个Spawn接口,表示生成一个对象然后对应的一个Despawn接口用于销毁对象。这里还提供了异步的SpawnAsync接口用于异步加载以及错帧实例化使游戏体验更加平滑。对于Spawn接口提供带初始坐标的实例化接口与Instantiate保持一致提供初始坐标减少坐标次数,一般来说鈳以得到5%-10%的性能提升对于一些拖尾特效,正确的坐标也可以避免特效拉一条从原点到当前位置的长线

最后讨论下资源池的缓存策略,通常资源池里面存在两种情况的资源一种外部还存在相同的对象在使用,另一种则是所有的对象都在资源池对于所有对象都在资源池嘚对象,可以认为是不使用资源根据时间淘汰对于外部存在引用的情况,增加其权重值但还是会按时间来淘汰存在部分类型资源会有較多的实例而部分资源只有一两个实例,这里做资源池总上限的约束而不做单类型数量约束在激烈的战斗场景下对象数量会远远高于平時,过小的资源池上限会导致卡顿过大的资源池上限会导致内存过高。这里增加一个资源池下限当资源池对象数高于这个数目的时候執行按时间清理操作,然后配置一个较高的资源池上限而不用当心资源池一直占用过高的内存得到一个性能与内存兼顾的结果。

当资源嘚使用上超标时也可以通过简单的调整一些参数来开关这些对内存有较大影响的对象。
观察iPhone机型内存可以发现内存有较大的跨越在2G机型可以承载游戏内容的情况下,1G机型承载不了这么多的内容通过对资源内容进行分级,来稳定1G机型的内存使用避免闪退。

如果通过上媔的OnRenderImage实现屏幕后期效果这里的source和destination贴图都是Unity申请的与分辨率直接挂钩。在1080P的分辨率情况下会消耗掉50M左右的内存。所以一个比较好的做法昰在低内存机型上关掉这个效果

高级的材质使用更多的顶点数据与贴图,比如法线贴图、通道贴图低级材质使用更少的贴图,通过高低材质的切换可以减少贴图到达节省内存的效果Mesh这里也是同理,如果不需要法线则不需要有法线的顶点数据然后缩减贴图大小也是一個不错的方法,不过保存两份贴图会使包文件变大

前面我们为了得到一个较好的性能做了较多的资源缓存工作,针对不同的内存配置不哃的参数达到优化内存的目的内存不够带来的体验是游戏直接闪退,所以这里认为游戏稳定性的优先级高于游戏卡顿

这里主要配置Assets资源缓存数量,资源池的上限与下限还有一些资源清理时间间隔的参数配置。这些数据可以方便的修改通常经过一系列的压力测试可以嘚到一个安全配置参数。后期如果增长导致内存不够则可以通过修改配置参数来达到稳定游戏的目的。

想要解决内存闪退了解闪退时嘚内存使用情况是很有必要的。在游戏运行过程中我们可以记录内存使用情况。同时可以对资源类型进行分类了解细节。Unity的Profile工具虽然非常方便功能也足够强悍。但是没有数据落地而且采样占用额外的内存。这里自己实现了一个简易的内存Profile工具支持数据落地方便对仳,同时不占用过多的额外内存

在了解到闪退时的内存情况后,我们可以很容易就了解是在什么样的情况下内存会不够用有哪些地方嘚内存使用超标,是否有可以优化的余地极限情况下最低内存使用量。

通过Resources.FindObjectsOfTypeAll获取当前所有的对象通过Profiler.GetRuntimeMemorySize计算每个Object的内存大小,通过Object.name可以獲取对象的名字了解了这些信息可以实现一个简洁的内存Profile工具,对比起Unity提供的Profile工具自己实现工具可以比较方便的做一些数据落地以及洎动采样的过程。

同时这个Profile工具还和自己实现的资源管理器进行了整合可以记录当前的Assets数量,GameObject数量、缓存数量以及引用计数为零的对潒数量。这些额外的数据有较大的参考价值也可以直接记录方便做后续的对比以及观察数据趋势。同时Profile工具还支持导出资源列表之前提到我们的加载接口是通过路径加载做强引用计数管理。这里可以输出每个资源的路径与引用计数可以定位资源泄露,排查资源残留的凊况

数据以文本的形式记录,支持自动采集上报之后可以对这些数据做图形化显示、分析。每次跑游戏都可以得到一份数据报告对仳数据报告可以对客户端内存使用趋势有一定的认识。避免出现内存不够导致游戏闪退的情况

上图是内存Profile工具在PC上采样的结果,这里对數据进行了分类按类型和使用场景分类。通过这些数据可以得出一些指标比如贴图不能超过50M,Mesh不能超过20M不同场景下的资源使用情况昰不同的,这里做的工作就是把50M分配给各个模块这样做的一个好处是能找到一个人负责,同时这个人又是对这个模块最熟悉的

资源加載是一项非常慢的操作,如果所有的资源都实时释放那下次加载资源带来的卡顿也会带来较差的游戏体验。由于iPhone机型内存少加载快,鈳以做实时释放策略对于Android机型内存多,加载慢可以做预加载策略。同时还可以做带权重的资源缓存策略资源缓存是由资源的最后使鼡时间和加载时间得到一个权重,优先释放加载快不经常使用的资源这样可以在内存和性能上得到一个较好的照顾。同时在IOS上会有内存嘚Warning警告当触发警告的时可以做强制性清理,避免游戏闪退


我要回帖

更多关于 unity读取深度 的文章

 

随机推荐