You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Three.js OBJ与MTL加载器循环调用仅最后一次执行问题求助

问题本质

你遇到的是JavaScript常见的异步循环变量捕获问题,同时存在异步逻辑执行顺序错误的问题,具体原因和解决方法如下:


问题根源

  • 变量作用域问题:你用var声明的i、materialPath、objectPath都是函数级作用域,整个循环只会共享同一份变量。Three.js的load方法是异步执行的,等回调函数触发时,同步的for循环早就执行完毕,此时i已经是循环结束的最终值,路径变量也保留的是最后一次迭代的赋值,所以只有最后一次的加载能正常运行。
  • 执行顺序错误:你把layer1Elements.push等逻辑放在了load方法的外面,加载逻辑还没执行完就往数组里添加空的分组,会导致后续操作拿到空对象。
  • 分组未重置:原代码没有在每次循环中重新初始化layer1Group、layer2Group、layer3Group,所有部件都会被添加到同一个分组里,不符合多部件拆分的需求。

解决方法

只需要做三处修改即可:

  1. 把for循环的var i改成let i,materialPath、objectPath也用let声明,利用let的块级作用域特性,让每次循环的回调都能捕获到当前迭代的正确变量值。
  2. 把数组push的逻辑移动到objLoader.load的回调内部,等对象加载、克隆完成后再往数组里添加。
  3. 每次循环开始时新建三个分组,避免部件互相污染。

如果需要在所有部件加载完成后执行统一操作(比如渲染首次画面),可以把每个加载逻辑封装为Promise,再用Promise.all监听所有加载任务的完成状态。


修正后代码

function draw(currentlySelectedCake) {
    layerArray = [];
    // 加载蛋糕部件
    // 把var改成let,块级作用域绑定
    for (let i = 0; i < currentCakeElements.length; i++) {
        if (currentCakeElements[i].endsWith(".mtl")) {
            // 路径变量也用let声明,每次循环独立绑定
            let materialPath = "uploads/" + currentlySelectedCake + "/" + currentCakeElements[i];
            let objectPath = "uploads/" + currentlySelectedCake + "/" + currentCakeElements[i + 1];
            // 每次循环新建独立分组
            let layer1Group = new THREE.Group();
            let layer2Group = new THREE.Group();
            let layer3Group = new THREE.Group();

            let cakeLoader = new THREE.MTLLoader();
            cakeLoader.load(materialPath, function (materials) {
                materials.preload();
                let objLoader = new THREE.OBJLoader();
                objLoader.setMaterials(materials);
                objLoader.load(objectPath, function (object) {
                    let layer1 = object.clone();
                    let layer2 = object.clone();
                    let layer3 = object.clone();

                    layer1.name = "Layer1Part" + i;
                    layer2.name = "Layer2Part" + i;
                    layer3.name = "Layer3Part" + i;

                    layer1.traverse((child) => {
                        if (child.isMesh) {
                            child.material = child.material.clone();
                        }
                    });

                    layer2.traverse((child) => {
                        if (child.isMesh) {
                            child.material = child.material.clone();
                        }
                    });

                    layer3.traverse((child) => {
                        if (child.isMesh) {
                            child.material = child.material.clone();
                        }
                    });

                    layer2.position.y = tortenhoehe;
                    layer3.position.y = tortenhoehe * 2 * 0.75;

                    camera.lookAt(layer2.position);

                    layer1Group.add(layer1);
                    layer1Group.name = "Layer1";
                    layer2Group.add(layer2);
                    layer2Group.name = "Layer2";
                    layer3Group.add(layer3);
                    layer3Group.name = "Layer3";

                    // 加载完成后再往数组中添加,放在回调内部
                    layer1Elements.push(layer1Group);
                    layer2Elements.push(layer2Group);
                    layer3Elements.push(layer3Group);
                });
            });
        }
    }
}

内容的提问来源于stack exchange,提问作者Tobias Gerling

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.29 14:27:03