Need_an_AwP

Need_an_AwP

github

在react中为Babylon.js启用ammojs插件

babylonjs 实现软体的方式是通过其内置的 ammojs 插件调用 ammo 物理引擎

scene.enablePhysics(null, new BABYLON.AmmoJSPlugin());

这里的 AmmoJSPlugin 其实是一个提供给 babylonjs 的接口,并不包含 ammo 自身,所以还需要自行准备 ammojs

import Ammo from 'ammojs-typed'

这里使用带有类型定义的 ammojs-typed

然后使用其推荐的初始化方法:

Ammo()
  .then(() => {
    console.log('Ammo.js initialized successfully.');
  })
  .catch((err) => {
    console.error('Failed to initialize Ammo.js:', err);
  });

结果得到了错误Uncaught TypeError: Cannot set properties of undefined (setting 'Ammo')

需要注意的是这个错误并没有被 catch 正常捕获,错误直接发生在Ammo()
它貌似是在设置全局变量时出错的

在 babylonjs 的官方示例代码中,并没有提到关于 ammo 的导入或者初始化
我猜测 ammo 是直接通过 script 导入的,直接就提供了一个可用的全局 Ammo

在 babylonjs 的启用物理代码中

scene.enablePhysics(null, new BABYLON.AmmoJSPlugin());

第一个参数是重力,不指定时使用默认的地球重力,第二个参数指定插件
而不传入参数给插件调用函数时使用默认的全局 Ammo

根据 devtool 的提示可以直接查看到错误来源于 ammojs-typed 的一行
this.Ammo = b;

其实已经可以猜到 Ammo 在初始化时尝试将导出的 api 赋值给全局对象,而当前的 ES 模块的顶层 this 是 undefined
ammojs 是一个 emscripten 编译的 bullet 物理引擎,emscripten 生成的胶水 js 代码在传统上会将导出赋值给全局对象,类似this.Ammo = b;或者window.Ammo = b;
但是在 es 模块中因为封装性和防污染,其顶层作用域是 undefined

所以最后的解决方法来自
https://forum.babylonjs.com/t/using-ammojs-with-babylon/26413/18?u=lz_k

const ammo = await Ammo.call({});
scene.enablePhysics(null, new AmmoJSPlugin(true, ammo));

使用.call手动指定函数内部的 this 为新传入的参数,这里指定为一个空对象
使用一个临时的空对象并不会影响什么,我们只需要让初始化过程不出错就可以了,因为Ammo()实际返回的 Promise 中也会包含可用的 Ammo api
然后将初始化得到的 ammo 实例传给 babylon 的 ammojs 插件就可以正常启用 babylon 的 ammojs 插件了
这个小技巧成功绕过了 ammojs 尝试对全局变量的赋值问题

接下来可以正常进行创建软体等操作了👻

加载中...
此文章数据所有权由区块链加密技术和智能合约保障仅归创作者所有。