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

Three.js中Car类方法返回Mesh为undefined的问题求助

问题分析:异步加载的执行顺序坑

这是个典型的异步操作问题!THREE.BinaryLoader.load()是异步方法——它会在后台默默加载模型文件,不会暂停后续代码的执行。所以当你在load()外面打印mesh时,回调函数里创建Mesh的逻辑还根本没跑呢,自然输出undefined;而回调内部打印正常,是因为只有当模型文件加载完成后,这段代码才会被触发执行。

下面给你三种实用的解决方案,你可以根据自己的代码场景选择:

方案1:把后续逻辑直接塞进回调里(最简单)

既然只有回调里能拿到完整的mesh,那直接把需要用mesh的代码写到回调里,或者抽成一个专门的方法在回调里调用:

this.loadCarToScene = function (carsVector, carName) {
  var carLoader = new THREE.BinaryLoader();
  const carData = carsVector[Object.keys(carsVector)[0]]; // 先把重复的对象提出来,代码更简洁

  carLoader.load(carData.url, function(geometry) {
    geometry.sortFacesByMaterialIndex();
    console.log("url--->" + carData.url);
    
    var m = [];
    for (var i in carData.mmap) {
      m[i] = carData.mmap[i];
    }

    const mesh = new THREE.Mesh(geometry, m);
    mesh.rotation.set(...carData.init_rotation); // 用set方法简化代码
    mesh.scale.setScalar(carData.scale);
    mesh.position.set(...carData.position);
    scene.add(mesh);

    // 在这里写所有需要用到mesh的逻辑,或者调用专门的处理方法
    this.handleLoadedCar(mesh);
  }.bind(this)); // 别忘了绑定this,不然拿不到类里的其他方法
};

// 专门处理加载完成后的mesh
this.handleLoadedCar = function(mesh) {
  console.log("终于拿到可用的mesh啦:", mesh);
  // 比如给mesh加动画、绑定点击事件、调整材质等等
};

方案2:用Promise包装(最灵活,推荐)

如果你的代码里有多处需要等待这个mesh加载完成,把加载过程包装成Promise是更好的选择,这样就能用async/await来优雅处理异步:

this.loadCarToScene = async function (carsVector, carName) {
  return new Promise((resolve, reject) => {
    var carLoader = new THREE.BinaryLoader();
    const carData = carsVector[Object.keys(carsVector)[0]];

    carLoader.load(
      carData.url,
      // 加载成功的回调
      function(geometry) {
        geometry.sortFacesByMaterialIndex();
        var m = [];
        for (var i in carData.mmap) {
          m[i] = carData.mmap[i];
        }

        const mesh = new THREE.Mesh(geometry, m);
        mesh.rotation.set(...carData.init_rotation);
        mesh.scale.setScalar(carData.scale);
        mesh.position.set(...carData.position);
        scene.add(mesh);

        resolve(mesh); // 把加载好的mesh传递出去
      },
      // 加载进度的回调(可选)
      function(xhr) {
        console.log(`加载进度: ${(xhr.loaded / xhr.total * 100)}%`);
      },
      // 加载失败的回调
      function(error) {
        console.error("模型加载失败:", error);
        reject(error); // 把错误抛出去
      }
    );
  });
};

// 使用的时候这么写:
async function initMyCar() {
  try {
    const carMesh = await yourCarInstance.loadCarToScene(carsVector, carName);
    console.log("加载完成的mesh:", carMesh);
    // 这里就可以放心使用carMesh做任何操作了
  } catch (err) {
    console.error("初始化汽车失败:", err);
  }
}

方案3:把mesh存为类的实例属性

如果你的Car类需要在多个方法里用到这个mesh,可以把它作为类的属性存储,同时加个加载完成的标记,避免后续操作时mesh还没加载好:

// 先在类的构造函数里初始化属性
constructor() {
  this.carMesh = null;
  this.isCarLoaded = false;
}

this.loadCarToScene = function (carsVector, carName) {
  var carLoader = new THREE.BinaryLoader();
  const carData = carsVector[Object.keys(carsVector)[0]];

  carLoader.load(carData.url, function(geometry) {
    geometry.sortFacesByMaterialIndex();
    var m = [];
    for (var i in carData.mmap) {
      m[i] = carData.mmap[i];
    }

    this.carMesh = new THREE.Mesh(geometry, m);
    this.carMesh.rotation.set(...carData.init_rotation);
    this.carMesh.scale.setScalar(carData.scale);
    this.carMesh.position.set(...carData.position);
    scene.add(this.carMesh);
    this.isCarLoaded = true; // 标记加载完成

    console.log("mesh加载完成:", this.carMesh);
  }.bind(this)); // 绑定this,确保能访问到类的属性
};

// 后续其他方法里使用mesh时,先检查状态
this.moveCar = function() {
  if (this.isCarLoaded && this.carMesh) {
    this.carMesh.position.x += 0.1; // 比如让汽车往前移动
  } else {
    console.log("别急,模型还在加载呢!");
  }
};

内容的提问来源于stack exchange,提问作者Ioana B

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.29 08:57:20