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初始化成功。');
  })
  .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 嘗試對全局變量的賦值問題

接下來可以正常進行創建軟體等操作了👻

載入中......
此文章數據所有權由區塊鏈加密技術和智能合約保障僅歸創作者所有。