Three.js + Rapier:带Y轴旋转的建筑碰撞盒错位问题
Three.js + Rapier:带Y轴旋转的建筑碰撞盒错位问题
看起来你遇到的是旋转变换顺序导致的坐标系不匹配问题,我来帮你拆解下原因和解决办法:
问题根源分析
你提到_center是旋转后的AABB中心——这就是问题所在!Rapier的碰撞盒是基于刚体的本地坐标系定义的,但你用了旋转后的世界坐标系下的中心来计算碰撞盒位置,自然会和视觉上的模型错位。
当你给Building应用Y轴旋转后,模型的本地坐标系和世界坐标系已经发生了偏移,这时候用世界空间的AABB中心来做碰撞盒的相对位置,相当于在旋转后的空间里又叠加了一次偏移,肯定对不上。
具体解决办法
改用本地坐标系的中心计算碰撞盒
在基类Building中,一定要在应用旋转、位移等世界变换之前,计算模型的本地边界框中心:// 基类Building的初始化流程 async loadModel() { // 1. 加载GLTF模型 const gltf = await this.loadGLTF(); this.model = gltf.scene; // 2. 先计算本地AABB和本地中心(关键:这时候还没加任何世界变换) this.model.geometry.computeBoundingBox(); const localAABB = this.model.geometry.boundingBox; this._localCenter = localAABB.getCenter(new THREE.Vector3()); // 3. 再应用旋转、位移等世界变换 this.model.rotation.y = this.rotationY; this.model.position.set(this.posX, this.posY, this.posZ); // 4. 最后创建碰撞盒 this._computeColliders(); }然后在子类的
_computeColliders里,用this._localCenter来定义碰撞盒的相对位置:// 子类的_computeColliders示例 _computeColliders() { // 基于本地中心定义碰撞盒的本地偏移 const wallCollider = RAPIER.ColliderDesc.cuboid(0.5, 3, 10) .setTranslation( this._localCenter.x + 2, // 相对于本地中心的X偏移 this._localCenter.y + 1.5, this._localCenter.z ); this.rigidBody.addCollider(wallCollider); }确保碰撞盒跟随刚体的世界变换
Rapier的刚体(RigidBody)会自动继承你在Three.js中设置的旋转、位移,所以碰撞盒只需要定义相对于刚体本地坐标系的位置和大小,不需要再手动加世界旋转。如果你的刚体是和Building模型绑定的,刚体的变换要和模型保持一致:// 基类中创建刚体时同步变换 this.rigidBody = this.world.createRigidBody( RAPIER.RigidBodyDesc.fixed() .setTranslation(this.model.position.x, this.model.position.y, this.model.position.z) .setRotation(this.model.quaternion) );避免变换顺序错误
记住Three.js的变换优先级是缩放→旋转→位移(SRT),如果你的基类中变换顺序搞反了(比如先位移再旋转),也会导致坐标系混乱。严格按照「计算本地边界→应用变换→创建碰撞盒」的顺序来,就能避免大部分错位问题。
最后再提醒下
核心就是别把本地坐标系和世界坐标系搞混——Rapier碰撞盒认的是刚体的本地空间,而你之前误用了旋转后的世界空间坐标来定义,自然会和视觉模型错开。调整下计算中心的时机,换成本地坐标系的参考,应该就能完美对齐了!




