查看原文
其他

程序丨我问大咖:Unity3D 5.3场景加载难点解答

2017-04-21 Gad-腾讯游戏开发者平台

在GAD的《我问大咖》栏目中,程序【温度大师】针对Unity3D 5.3场景加载,提出了4个自己真实遇到的问题。腾讯TDR客户端专家,《全民超神》等知名游戏的架构设计指导,腾讯程序大神邓涛(我们都亲切的称呼他“涛哥”),针对这些问题作出了详细的解答。


邓涛

腾讯TDR客户端专家

《全民超神》等知名游戏的架构设计指导


问题一


我想请教下 U3D 5.3 新推出的SceneManager.MergeScenes();的一些困惑。


新的SceneManager 场景管理。要想把 两个文件合并 需要使用SceneManager.MergeScenes() 但是 SceneManager.MergeScenes() 视乎很难在正常情况使用。


首先要求要将 场景要读取到 【图一的列表】。



但是问题是 通过 SceneManager.LoadSceneAsync("22",LoadSceneMode.Additive);

读取的方式 会报错(如图2)。



目前 只有一种情况 使用成功 ,就是直接用鼠标拖动场景文件到图一里面。这样的问题是 场景文件 无法打包 也无法存档,【版本为 5.51 和 5.6B10】



邓涛:

我贴个代码放这里吧,这个问题我希望你自己去寻求答案。

貌似贴不了图片 ,将就着看文本吧, (这份代码可以跑)


// Use this for initialization

void Start () {

SceneManager.LoadScene("scene1", LoadSceneMode.Additive);

SceneManager.LoadScene("scene2", LoadSceneMode.Additive);

Scene s1 = SceneManager.GetSceneByName("scene1");

Scene s2 = SceneManager.GetSceneByName("scene2");

StartCoroutine(MergeScene(s2, s1));

}

IEnumerator MergeScene(Scene src, Scene dest)

{

Debug.Log("begin merge scene");

yield return new WaitForEndOfFrame();

SceneManager.MergeScenes(src, dest);

Debug.Log("end merge scene");

}

另外有个属性,请注意一下:Scene.isLoaded


问题二


拷贝一个SceneA 里的【烘焙好的模型A】到 SceneB .在SceneB 中【烘焙好的模型A】的烘焙贴图就失效了。那么这样,除非让 SceneA 和 SceneB 融合到一个 场景进行统一渲染烘焙,否则是没法使用的。因为在很多情况,我不希望一个场景里面有太多模型统一烘焙,因为这样烘焙时间太久了,烘焙的时候内存也容易爆表。希望切分来烘焙,请问怎么解决这个问题呢?


邓涛:

烘培的原理是将整个场景的光照影响(照明和阴影)预先计算出来,存在一张光照贴图(lightmap)上,当场景里面摆了一堆物件的时候,烘培过程可能给这一堆物体生成一张或多张光照贴图,渲染的时候,使用对应的shader,把光照贴图用上去,使得实时过程中,这部分已经烘培的光照无需再计算。


那么问题来了,这些光照贴图和场景物件的关系是怎么样的?


1.场景物件(具体指模型)需要为lightmap增加一层纹理坐标(uv)——(纹理需要在模型表现起作用,必须要有纹理坐标)


2.一张lightmap可以给多个物件使用,当然,一个很大的物体也可能有多张lightmap,这取决于烘培是3D空间坐标单位与像素单位的比例。


一般来说lightmap一旦产生,对影响的物体最好在场景中是固定不动的,因为动了效果就会不对(想想,有个房子在地面上留下一个阴影,当房子移开的时候,阴影还在地面上,会不会很奇怪),所以,unity规定要烘培的物体应该是static的,既然是static的,怎么可以随便让你拷贝到其他地方去呢?


从技术上来说,把贴图和模型都拷贝走,运行是没问题的,但是效果不对,原因在于lightmap表达了光和场景物件以及物件之间的照明关系,相互有牵连,所以你觉得unity烘培太慢,可以试试更稳定的渲染器,max或者maya——以前我们做烘培都是美术用max和maya直接烘的,当然这样的话,纹理怎么组织更是由我们自己控制的了。


问题三


如何实现不卡顿的动态加载 在使用SceneManager.LoadSceneAsync("22",LoadSceneMode.Additive);的时候,发现被异步加载的文件一旦很大,在异步读取的时候就会很卡,影响当前游戏的操作。


邓涛:

1)客观来说,没有特殊的硬件支持(主机游戏机一般有特殊硬件,所以能做到即使加载场景过程中,还可以流畅地玩特定的小游戏)


2)如果无法进入底层,控制CPU的时间片分配,也是无法从技术上根本解决不卡顿(技术上叫帧率平稳并达到一定要求)


那么,如何做到加载不卡顿呢?


1)尽量将加载的单个任务切细(加载任务是我们自己可以控制的粒度,而cpu时间片则不是,当然,实际上如果从下往上,全部自己开发,也有更大的空间来将任务粒度变小——比如一次只执行一部分的磁盘数据加载或者处理,音频视频就是这样干的)


2)自己做好一帧时间的分配,有多少留给用户体验,有多少留给加载,这个依赖于上面的任务划分。


具体部署,开线程也好,放在主线程也好,主要是思想是要分帧管理。


特别提到的是,一般图形API(opengl 不建议在子线程调用,这样会把资源管线切断(如果放在子线程的话),切断了,我们就用两个阶段(子线程负责把资源从磁盘加载到内存,并负责解码到图形API可以使用的格式,再将解码后的格式转给主线程,它负责调用图形驱动的api,将数据传给显卡),同样分帧管理。


所以, SceneManager.LoadSceneAsync("22",LoadSceneMode.Additive)会卡其实很很正常,你能做的是,把资源粒度做小,自己管理加载任务的投放(当然,unity自己本身是可以优化的)。


另外,没特殊需求,尽量不要在游戏核心体验(战斗)过程中从磁盘加载资源,一开始就全部加进来,如果可以容得下。


问题四


有没有能将其他Scene场景文件 读取到内存的技术。最近做一个项目里面是一个有8层楼的图书馆,每一层都很巨大 在切换的时候卡顿严重。用SceneManager.LoadSceneAsync("22",LoadSceneMode.Additive);之后。还需要每一个场景里面物品都放到一个父亲物体下面 ,然后通过 STATIC 把 这个父物体 注册到一个统一管理各个楼层的控制器里面。我发现 【如图1】ggg\11\22 这个三个文件 不通过 STATIC 很难实现通信。传统的GameObject.FindObject()或者跨文件的引用 都没有用 返回都是 NULL。请问有什么好的解决办法? 或者更好的管理办法?



邓涛:你可以把场景做成一个prefab,切换场景的操作,就变成了切换显示prefab的操作(实际上,你最终可能就只有1个unity的scene,而你的场景,则是scene的一个gameobject)


以上是【我问大咖】中精选的问答内容,希望对大家有所帮助。如果想了解更多相关知识,可点击“阅读原文”,报名今晚8点的live,邓涛将作客live栏目,为大家讲解游戏客户端的框架设计。


时间:今晚(4月21日)20:00-21:00

主题:腾讯程序大牛——游戏客户端的框架设计



嘉宾简介:

邓涛,腾讯权威TDR客户端专家,《全民超神》架构设计及技术指导,《全民斗三国》《QQ仙侠传》《微宝大冒险》《智能玩具小车》等游戏担任主程序,在腾讯内部指导过《龙之谷》《寻仙》《王者荣耀》等等知名项目的技术问题。GAD《我问大咖》中出色的帮助一批小伙伴解决了各种各样的客户端框架设计方面的困难。作为一名程序大牛,涛哥并不像大多数程序员们一样不善言辞,反而在答疑解惑时有着一股令人叹服的耐心细致,技术难题找他就对了!


点击“阅读原文”,报名参与今晚的live吧!


今日推荐


《GuiltyGate 罪之门》 Unity3D开发技术全解析



添加小编微信,可享双重福利

1.加入GAD程序猿交流基地

获取行业干货资讯,观看大牛分享直播

2.直接领取60G独家程序资料库,地址在小编朋友圈

包括腾讯内部分享、文章教程、视频教程等全套资料

 

↓长按添加小编GAD苏苏↓


您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存