【问题标题】:aframe-physics-extras: collision of dynamic entity with static gltf modelaframe-physics-extras:动态实体与静态 gltf 模型的碰撞
【发布时间】:2020-12-02 18:44:09
【问题描述】:

我正在尝试捕捉动态球体和静态 gltf 模型之间的碰撞事件。我正在通过以下方式构建 gltf 实体:

    const template = document.createElement('a-entity');
    template.setAttribute('physics-collider', 'ignoreSleep: false');
    template.setAttribute('collision-filter', 'collisionForces: false');
    template.setAttribute('body', 'type:static; shape:hull');
    // add position, scale, url etc
    // ...
    template.addEventListener('collisions', e => {
        // debugger;
    })

这段代码创建了一个实体,但在调试模式下它周围没有网络,并且collisions 事件没有被触发。 出于调试目的,我尝试了不同的形状。这会在实体周围创建一个圆柱体,但它看起来太大了。动态形状穿过圆柱体,但并非总是触发 collisions 事件,相当罕见。

    template.setAttribute('body', 'type:static; shape:cylinder');

然后我尝试手动构建形状:

    template.setAttribute('body', 'type:static; shape:none');
    template.setAttribute('shape', 'shape: cylinder; height: 5;');

在这种情况下,我收到以下错误:

Cannot read property 'bodyOverlapKeeper' of null at NewComponent.<anonymous> (physics-collider.js:34)

所以现在我被困住了。有人可以建议我做错了什么。我想使用 gltf 模型本身的形状。我用blender打开了,好像还可以,不明白shape:hull为什么不行。

P。 S.:如果重要的话,我正在使用 webpack

【问题讨论】:

    标签: javascript game-physics aframe


    【解决方案1】:

    0。使用 setAttribute

    setAttribute 不会处理如下列表:

    template.setAttribute('body', 'type:static; shape:none');
    

    Rather 提供对象的新属性:

    element.setAttribute("name", {
      "property 1": "value 1",
      "property 2": "value 2"
    }); 
    

    1.动态身体/形状设置

    话虽如此,您可以像这样创建自定义静态圆柱体:

    element.setAttribute("body", {
       "type": "static",
       "shape": "none"
    })
    
    element.setAttribute("shape__cylinder", {
       'shape': 'cylinder',
       "height": 1.5,
       "radiusTop": 0.1,
       "radiusBottom": 0.2
    })
    

    查看fiddle


    2。模型的动态形状

    至于为 gltf 模型创建动态形状。我个人在使用cannon 时运气不佳,尽管它与ammo 驱动程序配合得很好。另一方面,我的 FPS 大幅下降(在较旧的移动设备上),因此如果可能,请尝试使用简单的碰撞网格来提高性能。

    你可以通过一个简单的函数I made得到一个蒙皮模型的边界框:

    let box = new THREE.Box3()
    THREE.Box3Utils.fromSkinnedMesh(skinnedMesh, box);
    // box should be the bounding box of the skinned mesh
    

    3。动画模型

    我强烈建议创建简单的碰撞形状并将它们附加到模型中的某个骨骼上。起点可以是this component

    <a-gltf-model bone-collider="bone: boneName; halfExtents: 0.15 0.15 0.15">
    

    边界框方法将非常复杂,因为:
    • 您必须为非旋转网格计算边界框
    • 对结果应用旋转(边界框是世界对齐的)
    • 在每个刻度上更新正文,甚至删除形状并重新添加 (source)

    你可以在this example看到这两种方法

    【讨论】:

    • 感谢您的回复。我设法从简单的部分创建了一个复合网格。但是我的 gltf 模型有一个动画。有没有办法可以“移动”碰撞网格的各个部分以及动画模型?
    • @NikitaFedorov 你有可以附加碰撞网格的骨骼吗?
    • 模型位置是固定的,所以我想我可以创建一些锚点,如果这就是你的意思。
    【解决方案2】:

    同时,我能够为 gltf 模型实现动画碰撞网格。 我使用了由@Piotr Adam Milewski here 实现的辅助函数。在this 答案中查看有关该功能的更多详细信息。 感谢您提出任何其他提高性能的建议。

    AFRAME.registerComponent('animated-collision-shape', {
        init: function () {
            this.nodeMap = {};
    
            this.el.setAttribute('body', 'type: static; shape: none;')
    
            this.el.addEventListener('model-loaded', () => {
                const size = new THREE.Vector3();
                let box = new THREE.Box3().setFromObject(this.el.object3D);
                box.getSize(size);
                this.offset = new CANNON.Vec3(0, size.y / 2, 0);
    
                let mesh = this.el.getObject3D("mesh");
                mesh.traverse(node => {
                    if (node.isSkinnedMesh) {
                        this.skinnedMesh = node;
                        this.nodeMap[node.uuid] = {
                            mesh: node,
                            box: new THREE.Box3()
                        }
                    }
                });
    
                if (!Object.keys(this.nodeMap).length) {
                    this.nodeMap[0] = {
                        mesh: this.el.object3D,
                        box: new THREE.Box3()
                    };
                }
    
                this.el.components["body"].shouldUpdateBody = true;
            })
        },
    
        remove: function () {
            this.removeBoxes();
        },
    
        tick: (function () {
            const size = new THREE.Vector3();
            let common_box_uuid = null;
    
            return function tick() {
                if (
                    !Object.keys(this.nodeMap).length ||
                    !this.el.body) {
                    return;
                }
    
                let combine = this.data.combine === true
    
                let i = 0;
                for (let uuid in this.nodeMap) {
                    // Non - skinned case
                    if (!this.nodeMap[uuid].mesh.isSkinnedMesh) {
                        this.nodeMap[uuid].box.setFromObject(this.el.object3D);
                        return;
                    }
                    // skinned model. Either separate boxes, or combined
                    if (common_box_uuid && combine) {
                        utils.SkinnedMeshBBox.expandAABB(this.nodeMap[uuid].mesh, this.nodeMap[common_box_uuid].box);
                    } else {
                        utils.SkinnedMeshBBox.getAABB(this.nodeMap[uuid].mesh, this.nodeMap[uuid].box);
                        common_box_uuid = uuid
                    }
    
                    if (isFinite(this.nodeMap[common_box_uuid].box.max.x)) {
                        this.nodeMap[common_box_uuid].box.getSize(size);
                        if (this.el.body.shapes[i]) {
                            this.el.body.shapes[i].halfExtents = new CANNON.Vec3(size.x / 2, size.y / 2, size.z / 2);
                            this.el.body.shapes[i].updateConvexPolyhedronRepresentation();
                        } else {
                            let shape = new CANNON.Box(new CANNON.Vec3(size.x / 2, size.y / 2, size.z / 2))
                            this.el.body.addShape(shape, this.offset, shape.orientation);
                        }
                        i++;
                    }
                }
    
                this.el.components["body"].shouldUpdateWireframe = true;
            };
        })()
    })
    

    【讨论】:

    • 嗨,也许我使用不正确(fiddle),但我认为有两个问题:1)扩展框不与动态主体交互 2)旋转模型的框不正确形状(因为它是由一个与世界对齐的边界框组成的)同时我想我已经设法为动画模型获得了一些结果(link)但是我个人将碰撞器附加到骨骼上是如此的混乱(比如here )
    • 我用过这个SkinnedMeshBBox。老实说,我没有比较他们。在您的小提琴中,看起来 bbox 仅在动画期间被扩展并且没有“折叠”回来。碰撞事件对我有用,虽然我没有创建一个测试小提琴,但会尝试去做。顺便说一句,谢谢你的例子,它们也会,但是我怎样才能得到骨骼名称?
    • 您可以在搅拌机中检查它们(或在搅拌机中命名)。如果模型很复杂,您可以在遍历网格时检查节点名称是什么,以了解您要查找的内容:)
    • 类似this ?
    • 如果您喜欢,请随意给 repo 加注星标 ;)
    猜你喜欢
    • 1970-01-01
    • 2011-12-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-07-06
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多