【问题标题】:ThreeJS: Remove object from sceneThreeJS:从场景中删除对象
【发布时间】:2013-08-23 19:56:46
【问题描述】:

我正在使用 ThreeJS 开发一个显示实体列表的 Web 应用程序,每个实体都有相应的“查看”和“隐藏”按钮;例如entityName 查看隐藏。当用户点击查看按钮时,会调用以下函数并成功在屏幕上绘制实体。

function loadOBJFile(objFile){            
    /* material of OBJ model */                                          
    var OBJMaterial = new THREE.MeshPhongMaterial({color: 0x8888ff});
    var loader = new THREE.OBJLoader();
    loader.load(objFile, function (object){
        object.traverse (function (child){
            if (child instanceof THREE.Mesh) {
                child.material = OBJMaterial;
            }
        });
        object.position.y = 0.1;
        scene.add(object);
    });     
}

function addEntity(object) {
    loadOBJFile(object.name);
}

点击隐藏按钮,会调用以下函数:

function removeEntity(object){
    scene.remove(object.name);
}

问题是,当点击隐藏按钮时,实体并没有从屏幕上移除。我该怎么做才能使 隐藏 按钮起作用?

我做了一个小实验。我在scene.add(object); 之后在addEntity 函数中添加了scene.remove(object.name);,结果,当单击“查看”按钮时,没有绘制实体(如预期的那样),这意味着scene.remove(object.name);addEntity 中工作得很好。但我仍然无法弄清楚如何在 removeEntity(object) 中使用它。

另外,我检查了 scene.children 的内容,它显示:[object Object],[object Object],[object Object],[object Object],[object Object],[object Object]

完整代码:http://devplace.in/~harman/model_display1.php.html

如果需要更多详细信息,请询问。我用 ThreeJS 的 rev-59-dev 和 rev-60 进行了测试。

谢谢。 :)

【问题讨论】:

  • 也许你忘了更新场景(重新渲染)?是否存在渲染更新(requestAnimationFrame 循环或 renderer.render(...))?
  • 我尝试在scene.remove(object.name);removeEntity(object) 之后调用animate() (update() 和render() 在animate() 中调用),但没有变化。 :(

标签: javascript three.js


【解决方案1】:

我认为查看您对 addEntity 和 removeEntity 代码的使用会有所帮助,但我的第一个想法是您实际上是在设置 object.name 吗?在 scene.add(object); 之前尝试在你的加载器中像这样:

object.name = "test_name";
scene.add(object);

可能发生的情况是 Object3D 的默认“名称”为“”,因此当您随后调用 removeEntity 函数时,由于场景对象名称为“”而失败

另外,我注意到您将 object.name 传递给您的加载程序?这是您将 URL 存储到资源的地方吗?如果是这样,我建议使用 Object3D 的内置 .userData 方法来存储该信息并保留名称字段以用于场景识别。

编辑:对新添加代码的响应

首先要注意的是,在您的对象名称中包含“/”并不是一个好主意,它似乎工作正常,但您永远不知道某些算法是否会决定转义该字符串并破坏您的项目。

第二项是现在我已经看到了您的代码,它实际上是直截了当的。您的删除功能正在尝试按名称删除,您需要一个 Object3D 来删除。试试这个:

function removeEntity(object) {
    var selectedObject = scene.getObjectByName(object.name);
    scene.remove( selectedObject );
    animate();
}

在这里你看到我通过传入你的对象标签的name 属性在 Three.js Scene 中查找你的Object3D。希望有帮助

【讨论】:

  • 实际上,传递给addEntityremoveEntity 的“object”参数是一个JavaScript 默认关键字,它选择相应“View”按钮单击的实体的“名称”(addEntity 和removeEntity 是onclick被调用函数),所以object.name实际上包含实体的名称。 scene.add(object) 工作正常,因为我可以看到屏幕上绘制的对象。问题仅出在 scene.remove(object.name) 上,因为它既不显示任何错误也不显示任何结果。
  • 我明白了,我不确定确切的问题,但是 Three.js 中的 scene.remove 函数正在寻找一个 Object3D.name,它可能等效也可能不等效。唉,我认为为了清楚起见,可能需要更多代码来查看添加删除对象代码的对象构造和用法。否则,我会 console.log 在你的 add 函数和 remove 函数中记录 Mesh.name ,看看你得到了什么输出。
  • 我刚刚编辑了这个问题,添加了更多细节,并提到了完整代码的链接。请检查。谢谢。
  • 我只是按照您的建议尝试了,没有任何反应。我还通过将scene.remove( scene.getObjectByName(object.name) ); 放入 alert() 进行检查并得到“未定义”。由于我对 ThreeJS 了解不多,但发现对象都在scene.children 中。我们添加到场景中的每个对象都会附加到场景.children 的末尾。所以我写了以下代码:var lastIndex = scene.children.length - 1; endElement = scene.children[lastIndex]; scene.remove(endElement); 这将删除我绘制的最后一个元素。现在你能帮我参考对象名称从scene.children中获取元素的ID吗?
  • +1 用于建议和有关名称属性的信息,但重要的是,在使用此方法时,您必须确保场景中的每个对象都有唯一的名称。
【解决方案2】:

我遇到了和你一样的问题。我尝试了这段代码,它工作得很好: 当你创建你的对象时,把这个 object.is_ob = true

function loadOBJFile(objFile){            
    /* material of OBJ model */                                          
    var OBJMaterial = new THREE.MeshPhongMaterial({color: 0x8888ff});
    var loader = new THREE.OBJLoader();
    loader.load(objFile, function (object){
        object.traverse (function (child){
            if (child instanceof THREE.Mesh) {
                child.material = OBJMaterial;
            }
        });
        object.position.y = 0.1;
      // add this code
        object.is_ob = true;

        scene.add(object);
    });     
}

function addEntity(object) {
    loadOBJFile(object.name);
}

然后你删除你的对象试试这个代码:

function removeEntity(object){
    var obj, i;
            for ( i = scene.children.length - 1; i >= 0 ; i -- ) {
                obj = scene.children[ i ];
                if ( obj.is_ob) {
                    scene.remove(obj);

                }
            }
}

试一下,告诉我是否有效,似乎三个js在添加到场景后无法识别对象。但是有了这个技巧就可以了。

【讨论】:

  • 我不确定这应该做什么,你只是添加一个随机标志。
  • 您可以使用所有对象中存在的名称属性,默认为''。它不必是唯一的,您可以通过这种方式拥有许多类对象。
【解决方案3】:

你可以用这个

function removeEntity(object) {
    var scene = document.querySelectorAll("scene");                               //clear the objects from the scene
    for (var i = 0; i < scene.length; i++) {                                    //loop through to get all object in the scene
    var scene =document.getElementById("scene");                                  
    scene.removeChild(scene.childNodes[0]);                                        //remove all specified objects
  }   

【讨论】:

【解决方案4】:
clearScene: function() {
    var objsToRemove = _.rest(scene.children, 1);
    _.each(objsToRemove, function( object ) {
          scene.remove(object);
    });
},

这使用 undescore.js 迭代场景中的所有子项(第一个除外)(这是我用来清除场景的代码的一部分)。只需确保在删除后渲染场景至少一次,否则画布不会改变!不需要“特殊” obj 标志或类似的东西。

另外你不按名称删除对象,只是按对象本身,所以调用

scene.remove(object); 

而不是scene.remove(object.name); 就够了

PS:_.eachunderscore.js的函数

【讨论】:

  • 常规 for(let x of foo) 有什么问题?还是常规的 for 循环?
  • 当然你可以用一个简单的for循环,没毛病,我只是碰巧用过_.each而已。
  • 完全有效的解决方案,但下划线是一个额外的依赖项,除非您已经在使用它,否则它会使代码库变得混乱。
  • 哦,这些图书馆上瘾的家伙...他们似乎忘记了所有的 JS 库只不过是使用...普通的、旧的、原生的 JS! ;-)
  • 你知道2013年javascript的状态已经完全不同了吗?最重要的是,图书馆解决了很多经常发生的问题,例如lodash,经过良好测试。自己实现逻辑的理由是 0。此外,如果出于 X 原因您已经包含了一个库,那么没有理由不重用现有模块来解决其他问题。如果您导入 jquery 以使用 id 访问 dom 节点,那么您做错了。
【解决方案5】:

如果您的元素不直接在您的场景中,请返回父级将其删除

  function removeEntity(object) {
        var selectedObject = scene.getObjectByName(object.name);
        selectedObject.parent.remove( selectedObject );
    }

【讨论】:

    【解决方案6】:

    这很好用 - 我测试过 所以,请为每个对象设置名称

    在创建时为对象命名

        mesh.name = 'nameMeshObject';
    

    如果你必须删除一个对象,请使用它

        delete3DOBJ('nameMeshObject');
    
    
    
        function delete3DOBJ(objName){
            var selectedObject = scene.getObjectByName(objName);
            scene.remove( selectedObject );
            animate();
        }
    

    打开一个新场景,添加对象

    删除一个对象并创建一个新对象

    【讨论】:

      【解决方案7】:

      我开始将它保存为一个函数,并在需要时根据需要调用它:

      function Remove(){
          while(scene.children.length > 0){ 
          scene.remove(scene.children[0]); 
      }
      }
      

      现在你可以调用 Remove();适当的功能。

      【讨论】:

        【解决方案8】:

        当你使用:scene.remove(object); 物体已从场景中移除,但与它的碰撞仍处于启用状态!

        要删除与对象的碰撞,您可以使用它(用于数组): objectsArray.splice(i, 1);

        例子:

        for (var i = 0; i < objectsArray.length; i++) {
        //::: each object ::://
        var object = objectsArray[i]; 
        //::: remove all objects from the scene ::://
        scene.remove(object); 
        //::: remove all objects from the array ::://
        objectsArray.splice(i, 1); 
        

        }

        【讨论】:

          【解决方案9】:

          this 示例可能会给您一种不同的方法。我也试图在我的项目中使用scene.remove(mesh) 实现类似的功能。然而,网格的几何形状和材质属性的处理对我有用! source

          【讨论】:

            【解决方案10】:

            我来晚了,但在阅读了答案后,需要说更多的澄清。

            你写的删除函数

            function removeEntity(object) {
                // scene.remove(); it expects as a parameter a THREE.Object3D and not a string
                scene.remove(object.name); // you are giving it a string => it will not remove the object
            }
            

            从 Three.js 场景中移除 3D 对象的好习惯

            function removeObject3D(object3D) {
                if (!(object3D instanceof THREE.Object3D)) return false;
            
                // for better memory management and performance
                object3D.geometry.dispose();
                if (object3D.material instanceof Array) {
                    // for better memory management and performance
                    object3D.material.forEach(material => material.dispose());
                } else {
                    // for better memory management and performance
                    object3D.material.dispose();
                }
                object3D.removeFromParent(); // the parent might be the scene or another Object3D, but it is sure to be removed this way
                return true;
            }
            

            【讨论】:

            【解决方案11】:

            我改进了 removeObject3D 的 Ibrahim 代码,添加了一些几何或材质检查

            removeObject3D(object) {
                if (!(object instanceof THREE.Object3D)) return false;
                // for better memory management and performance
                if (object.geometry) {
                    object.geometry.dispose();
                }
                if (object.material) {
                    if (object.material instanceof Array) {
                        // for better memory management and performance
                        object.material.forEach(material => material.dispose());
                    } else {
                        // for better memory management and performance
                        object.material.dispose();
                    }
                }
                if (object.parent) {
                    object.parent.remove(object);
                }
                // the parent might be the scene or another Object3D, but it is sure to be removed this way
                return true;
            }
            

            【讨论】:

              【解决方案12】:

              使用 scene.remove(Object) 要从场景中移除的对象

              【讨论】:

                猜你喜欢
                • 2015-06-07
                • 2022-01-10
                • 2017-10-13
                • 1970-01-01
                • 2013-10-19
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多