先来看下报错的具体内容:
Unable to load assets from /static/model/xx/xx.glb: Scene has been disposed
从字面意义上来看,是场景Scene被释放了,加载资源失败。
为什么会出现这种情况呢?我们先来看下整个的业务流程:
1、进入页面,创建场景1;
2、在页面进行了某些操作,销毁场景1,使用的是scene.dispose
3、执行完dispose之后,然后加载场景2,即重新new一个scene,并加载资源。
报错出现的操作手法是这样的,如果“在页面进行了某些操作”发生时间比较晚,就不出现这个报错,发生的比较早的话,就出现这个报错。如果踩过同样的坑,或者心思缜密的童鞋,可能很快发现了问题。但我今天确实兜了一些弯路。
因为“在页面进行了某些操作”,实际上就是为了加载场景2,并在场景2里面加载资源。在加载场景2的资源的时候,发生了“Scene has been disposed”的报错,正常的思维都是:我这个scene不是刚new出来的吗,怎么会已经被dispose 了呢?
于是我开始怀疑,是不是这个dispose不是同步的,是异步的,开始执行了dispose之后就立马返回了,因为我的代码是这么写的:
scene.dispose();
scene = null;
//---
scene = new BABYLON.Scene();
xxx.LoadAssest(xxx, xxx, scene); //加载资源。就随意写下伪代码了。
如果dispose是同步的,那么scene = new BABYLON.Scene()就应该是让scene能够重新建立的;出现上面的问题的一种可能就是:dispose不是同步的,它可以立马返回,但dispose的真正操作是后面再进行的,也就变成了如下顺序:
1、dispose方法被调用,立马返回;
2、new了一个新的scene
3、此时dispose真的发挥了作用,scene又被干掉了
4、加载资源,scene已经不存在,报错
是不是完美解释了上述报错,我当时也觉得这个解释真完美,心里还暗暗骂道,怎么这个引擎这么lj...
如果真的是这样,也得解决这个问题呀,解决方案就是要到真正dispose执行完毕之后才能去做上面的第2和第4步。看了下api文档,有个onDispose的响应方法,于是加上,同时打下log检查下实际的执行顺序。
然后我傻眼了:onDispose的响应,的的确确明明白白在“2、new了一个新的scene”前面执行了。
难道onDispose也是不正确的?不至于吧,堂堂这么大一个引擎,如果是有这种问题就真的很lj了。
正当百思不得其解之时,在想是不是要用些什么取巧办法来绕开这个问题。我再次仔细看了下报错,报错语句里面前面半句,是提供了具体要加载的资源的路径。
咦,这个路径,不是我执行完dispose之后,然后加载场景2想要加载的资源哦,应该是“进入页面,创建场景1”时就要加载的资源!
这个时候我恍然大悟了,这个报错里面,已经被dispose的scene,并不是新new出来的scene,而是被我们手动dispose的scene。这个报错引发的过程如下:
1、进入页面,创建场景1,第一个scene,此时加载资源1、资源2、资源3......;
2、资源n开始加载,但还没从服务端下载下来;
3、第一个scene被dispose掉了
4、第二个scene被new 出来了,然后开始加载新的资源A,资源B,资源C,。。。;
5、资源n此时从服务端拉下来了,引擎内部开始加载到场景,也就是第一个scene,然而第一个scene已经被dispose了,于是就报了文章一开始的那个报错。
这也完美解释了为什么如此诡异:如果“在页面进行了某些操作”发生时间比较晚,就不出现这个报错,发生的比较早的话,就出现这个报错。而且这个报错也并不影响页面后续的操作,因为新加载的scene及其资源都是正常的!
整个问题的核心就是babylon内部对于这种资源异步加载的处理还不够到位。dispose本身并没什么问题,冤枉它了。正常来说,像这种情况,资源还没下载下来,scene就被dispose了的情况,正在发送和即将要发送的资源加载请求,应该中断;无法中断,且资源已经加载回来之后,scene被dispose了,也就是爹妈没了,自然而然资源就应该终止它的生命周期。至少我认为完美的处理是如此,毕竟在写法上,资源跟scene是绑定到了一块。不绑定的话,各干各的,就另说了。
整个问题的解决过程便是如此。也有童鞋问了,为什么不是一开始就能发现是第一个scene被dispose掉导致的呢,我觉得在这次的事件里面主要有2个原因:
(1)因为场景1要加载的资源还是比较多,我们把资源url写到了数据库,读取了接口再进行的加载,url这玩意我们就不去写死在代码里,所以对URL并没什么感觉,并不知道哪个资源对应哪个场景的什么状态。
(2)这个报错的发生时机是在我们加载了第二个场景之后发生的,正常的线性思维便是如此,这个时候发生的事情就跟当前这个操作有关。当然段位比较高就能一眼看出哈哈哈哈哈。
总之一句话,异步真的有坑啊。。。。