如何正确使用Ammo.js的setUserPointer/getUserPointer获取碰撞物体名称
根因分析
- 第一种
setUserPointer方案失效原因:你使用的Web版本Bullet物理引擎(大概率是Ammo.js)的setUserPointer是C++接口的封装,仅支持接收WASM堆内存的数字指针,无法直接存储JS对象引用,所以你存的JS对象会被直接忽略,调用getUserPointer自然拿不到有效数据。 - 第二种直接挂载自定义属性方案失效原因:
getManifoldByIndexInternal返回的body0/body1是C++刚体对象的临时JS包装实例,每次调用接口获取碰撞体都会生成全新的JS对象,你之前在创建刚体时挂载的自定义属性仅存在于当时的JS实例上,和碰撞回调里拿到的实例不是同一个对象,因此读取不到。
可行解决方案
推荐使用「用户索引+全局映射表」的方案实现,不需要操作WASM内存,稳定性高:
- 首先创建全局映射表存储刚体对应的数据
// 全局存储:key为刚体的userIndex整数值,value为你需要存储的自定义数据 const bodyUserData = new Map<number, { name: string }>(); // 用于生成不重复的刚体id let nextBodyId = 1;
- 创建刚体时存入映射关系
// 给刚体设置唯一的用户索引 this.body.setUserIndex(nextBodyId); // 把自定义数据存入全局映射表 bodyUserData.set(nextBodyId, { name: name }); // 再把刚体加入物理世界 physicsWorld.addRigidBody(this.body); nextBodyId++;
- 碰撞检测时从映射表读取数据
function detectCollison(): void { const dispatcher = physicsWorld.getDispatcher(); const numManifolds = dispatcher.getNumManifolds(); for (let i = 0; i < numManifolds; i++) { const contactManifold = dispatcher.getManifoldByIndexInternal(i); const body0 = contactManifold.getBody0(); const body1 = contactManifold.getBody1(); // 读取刚体的用户索引,再从全局映射表拿数据 const data0 = bodyUserData.get(body0.getUserIndex()); const data1 = bodyUserData.get(body1.getUserIndex()); console.log("first object: " + data0?.name); console.log("second object: " + data1?.name); } }
如果有刚体销毁的需求,记得同步调用bodyUserData.delete(对应id)清理映射表,避免内存泄漏。
内容的提问来源于stack exchange,提问作者8Observer8
相关产品推荐
相关产品推荐

