Three.js如何按调用顺序加载GLTF模型并存储到对应数组中
问题原因
GLTFLoader的load方法是异步IO操作,回调执行顺序和调用顺序没有必然关联:体积小的模型会更快加载完成、更早触发回调执行push操作,最终数组顺序就是加载完成顺序,和你调用loadGLTF的顺序不一致,自然会出现下标错乱的问题。
解决方案
可以把加载逻辑封装成Promise,再用Promise.all统一处理,它的返回结果顺序和你传入Promise的顺序完全一致,天然就能保证下标和调用顺序对应,同时不会牺牲并发加载的性能。
修改后的代码如下:
var models = { tile: [], building: [], character: [] }; const loader = new GLTFLoader(); // 封装加载逻辑为返回Promise的函数 function loadGLTF(path, type){ return new Promise((resolve, reject) => { loader.load( path, (gltf) => { gltf.scene.traverse( ( node ) => { if( node.material ) { node.material.metalness = 0; } if ( node.isMesh ) { node.castShadow = type !== 'tile'; node.receiveShadow = true; } } ); // 加载成功直接返回处理好的gltf resolve(gltf); }, (xhr) => { console.log( `${path} 加载进度: ${( xhr.loaded / xhr.total * 100 ).toFixed(2)}%` ); }, (error) => { console.log( `${path} 加载失败: `, error); reject(error); } ); }) } // 按预期顺序定义各类型的加载路径列表 const loadList = { tile: [ 'resources/models/tiles/tile_000.glb', 'resources/models/tiles/tile_001.glb' ], building: [ 'resources/models/buildings/building_000.glb', 'resources/models/buildings/building_001.glb', 'resources/models/buildings/building_002.glb', 'resources/models/buildings/building_003.glb', 'resources/models/buildings/building_004.glb', 'resources/models/buildings/building_005.glb', 'resources/models/buildings/building_006.glb', 'resources/models/buildings/building_007.glb', 'resources/models/buildings/building_008.glb' ], character: [ 'resources/models/characters/Male.glb' ] } // 批量按顺序加载 async function loadAllModels() { for (const type in loadList) { // 生成该类型所有加载任务的Promise数组,顺序和loadList里的路径顺序一致 const tasks = loadList[type].map(path => loadGLTF(path, type)); // Promise.all返回结果顺序和tasks顺序完全一致,直接赋值即可保证下标对应 models[type] = await Promise.all(tasks); } // 所有模型加载完成后执行后续逻辑,代替原来的checkLoad checkLoad(); } // 调用加载入口启动加载 loadAllModels();
如果你不想调整原有调用写法,也可以给loadGLTF加第三个入参index,回调里直接用models[type][index] = gltf代替push,也能保证顺序,只是手动传下标容易出现书写错误,更推荐上述Promise方案。
内容的提问来源于stack exchange,提问作者Vardan Betikyan
相关产品推荐
相关产品推荐

