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初始化成功。');
})
.catch((err) => {
console.error('初始化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 嘗試對全局變量的賦值問題
接下來可以正常進行創建軟體等操作了👻