简体   繁体   中英

JavaScript function only shoes the last element (Three.js)

I'm trying to create a fence generator function with Three.js , but my function only return the last fence, I don't know why...

function generateFence(nb){
  var i;
  var value = -5;
  var loadingManager;
  for(i = 0; i < nb ; i++) {
    var arrayFence = [];
    loadingManager = new THREE.LoadingManager( function () {
      scene.add( arrayFence[i] );
    });

    var loader = new THREE.ColladaLoader( loadingManager );
    loader.load( 'fence/model.dae', function ( collada ) {
      arrayFence[i] = collada.scene;
      arrayFence[i].position.x = value;
      arrayFence[i].position.z = -5;
    });
    value = value + 3;      
  }
}

generateFence(3);

There are a couple of problems with your code, but I think the issue is the old async problem. Loaders are asynchronous, which means that the code is actually executed later, but your code assumes it is synchronous. Because of this, all your fences are reading the value when all the looping has been done, and they will usually only trigger after they are all done. Here a way to refactor your function so it behaves as expected:

 function generateFence(nb){ // First we will load the model once, as we want to reuse it. // Lets wrap it in a promise so its easier to use later. const value = -5; const fences = new THREE.Group; const model = new Promise(function( resolve ){ THREE.ColladaLoader( loadingManager ).load( 'fence/model.dae', resolve ); }); // Use a `let` declaration to ensure that each loop remembers // what the value for i was wen it ran, even if you perform an async operation. for( let i = 0; i < nb; i++ ){ // The bit after `then` happens asynchronously, waiting until the model is loaded // Because of this, we need to ensure we don't rely too much on values defined in the synchronous loop. model.then(model => { // Let's clone it so we create copies of the one model const fence = model.scene.clone(); // Calculate the x position based on value and the current state of i. fence.position.set( value + i * 3, 0, -5 ); fences.add( fence ); }); } return fences; } // Returns a THREE.Group. When the fences are loaded, they will // be added to the group, which is already an invisible part of the scene. scene.add( generateFence(3) ); 

The issue is probably that all fences are exactly the same and, because of the async operation, all calculated to be in the same place, looking like there is only one fence.

There's several issues.

  • The code is creating a loading manager for every fence. You only need one at most
  • The code is loading the same fence 5 times. It should probably load it once and clone it
  • The code is not doing anything with the loading manager except waiting for the scene but it's already getting the scene from the ColladLoader so it does't need the loading manager at all. Loading managers are for helping wait on multiple things to load but it's only loading one thing
  • yThe code is using value in a callback but there is only one instance of value. It will be the same in all the callbacks so all the fences will be at the exact same place.

This should work.

 function generateFence(nb){ const loader = new THREE.ColladaLoader(); loader.load( 'fence/model.dae', function ( collada ) { const copy = collada.scene.clone(); scene.add(copy); let value = -5; for(var i = 0; i < nb ; i++) { copy.position.x = value; copy.position.z = -5; value = value + 3; } }); } generateFence(3); 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM