问题描述
上一个问答解释了如何在 A-Frame 物理系统中移动动态物体,方法是在更改位置后使用 syncToPhysics() 方法调用。 How to translate A-Frame dynamic body
我一直在使用这种技术,在使用 CANNON.js 驱动程序时它对我来说效果很好。
参见例如https://unmarred-ambiguous-shad.glitch.me/
但是,我想使用 Ammo 驱动程序,如此处所述。 https://github.com/n5ro/aframe-physics-system/blob/master/AmmoDriver.md
(想要使用 Ammo 驱动程序的一个关键原因是此时,CANNON.js 不支持 A-Frame 1.2+ https://github.com/n5ro/aframe-physics-system/issues/187 ).
我希望我可以在 ammo-body 组件上使用 syncToPhysics() 做同样的事情:
this.el.components["ammo-body"].syncToPhysics();
但我在这个故障中尝试这样做,但它不起作用 - 身体只是卡在一个地方。 https://roasted-snow-replace.glitch.me/
有没有什么办法可以让这个功能与Ammo一起使用,或者说使用Ammo驱动时无法直接操纵动态物体的位置?
解决方法
这并不优雅,但我发现“传送”对象的唯一解决方案是:
- 将身体碰撞标志设置为 KINEMATIC (2) - 否则子弹将忽略运动状态更新
- 应用新位置(使用框架物理时通过
syncToPhysics()
) - 重置身体速度,使物体不会在传送后立即反弹
- 恢复原始标志(使用框架物理时通过
updateCollisionFlags()
)
类似于这里:
<script src="https://aframe.io/releases/1.2.0/aframe.min.js"></script>
<script src="https://mixedreality.mozilla.org/ammo.js/builds/ammo.wasm.js"></script>
<script src="https://cdn.jsdelivr.net/gh/n5ro/[email protected]/dist/aframe-physics-system.js"></script>
<script>
AFRAME.registerComponent("moving-dynamic-body",{
init: function() {
// wait until the physics engine is ready
this.el.addEventListener("body-loaded",e => {
// cache the ammo-body component
this.ammoComponent = this.el.components["ammo-body"];
// use this vector to zero the velocity
// keep in mind this needs to be deleted manually from the memory with Ammo.destroy(this.zeroSpeed)
this.zeroSpeed = new Ammo.btVector3(0,0);
});
},tick: function() {
// wait for the physics
if (!this.ammoComponent) return;
// restore stuff if the "teleport magic" has been done in the last renderloop.
// this should probably be done in steps instead of tick
if (this.collisionFlag === 2) {
// this just tells us that we reverted to normal
this.collisionFlag = 0;
// restore the original collision flags
this.ammoComponent.updateCollisionFlags();
// reset the speed,or the body will bounce away
this.ammoComponent.body.setLinearVelocity(this.zeroSpeed);
}
// if the body is below 1m
if (this.el.object3D.position.y < 1) {
// set the THREEJS position.y
this.el.object3D.position.y = 2;
// change the collision flag to the KINEMATIC_BODY
this.collisionFlag = 2;
// apply the flag
this.ammoComponent.body.setCollisionFlags(this.collisionFlag);
// sync the physisc transforms to the THREEJS transform
this.ammoComponent.syncToPhysics();
}
}
});
</script>
<a-scene physics="driver: ammo; debug: true; debugDrawMode: 1;" renderer="colorManagement:true">
<a-box id="test1" ammo-body="type: dynamic" ammo-shape="type: box" material="color:#dd1111" height="0.1" width="0.1" depth="0.1" position="0 2 -1" moving-dynamic-body>
</a-box>
</a-scene>
一种可能的解决方案是直接在 Ammo 中设置线速度。这消除了使用新位置数据更新物理引擎的需要。
由于摩擦,块会变慢,因此您需要将其设置为恒定速度(也可能配置材料以消除摩擦)。
此外,在 Ammo WASM 完全初始化之前,tick 函数就开始触发,如果您过早地尝试创建新的 Ammo.btVector3,则会遇到错误。
经过一些实验,看起来检查 Ammo.asm.$ 是否存在是确定 WASM 内容何时准备就绪的好方法。但这里可能有更好的解决方案。
tick: function(time,timeDelta) {
// Ammo WASM takes a moment to initialize.
// Don't try to do anything that involves creating Ammo objects until
// Ammo.asm.$ exists.
if (Ammo.asm.$) {
const velocity = new Ammo.btVector3(1,0);
this.el.body.setLinearVelocity(velocity);
Ammo.destroy(velocity);
}
}
包含此解决方案的新故障...
https://pinto-big-pecorino.glitch.me/
我用来帮助回答这个问题的几个参考资料: BulletPhysics (ammo.js) - How would you go about applying force to an object?
https://github.com/n5ro/aframe-physics-system/blob/master/AmmoDriver.md#using-the-ammojs-api
,我创建了 aframe-physics-system
包的一个分支,以使其至少部分兼容 A-Frame 1.2。
这是更新后的文件:
https://github.com/gearcoded/aframe-physics-system/blob/master/dist/aframe-physics-system.js
我没有测试所有东西,但场景编辑器、重力和碰撞工作。