React-Three-Fiber+Rapier:运行时动态更新RigidBody尺寸
问题:React-Three-Fiber + RapierJS运行时无法同步更新RigidBody碰撞体尺寸
我正在复刻一款名为Orbital的小游戏(仅作学习用途)。游戏中玩家将圆形物体发射至竞技场,该物体会在场内某处停下并持续扩大尺寸,直至与竞技场边界或其他圆形物体发生碰撞。
目前遇到的问题是:当圆形物体扩大时,其绑定的RigidBody并未同步增大尺寸,始终保持创建时的初始大小。我使用React-Three-Fiber和Rapier JS开发,尝试过通过scale属性结合状态管理修改大小(模型会更新但RigidBody不同步)、禁用刚体休眠、修改刚体类型、直接修改args属性等方法,均无效。仅修改初始尺寸时RigidBody可正常适配,问题仅出现在运行时动态修改场景。
以下是我的代码:
import { useRef, useState } from "react"; import { useFrame } from "@react-three/fiber"; import { RigidBody } from "@react-three/rapier"; const Obstacle = ({ size = [4,4,2,32], position = [0, 0, 0], color = "red", id, hasCollided = false }) => { const rigidBody = useRef(); const obstacle = useRef(); const [scale, setScale] = useState(1); useFrame(() => { if (obstacle.current.collided == false) { setScale(scale + 0.1); } }); return ( <RigidBody ref={rigidBody} colliders="hull" friction={0} restitution={1.3} type="fixed" position={position} ccd={true} onCollisionEnter={() => obstacle.current.collided = true} scale={[scale, 1, scale]} canSleep={false} > <mesh ref={obstacle} collided={hasCollided} > <cylinderGeometry args={size} /> <meshStandardMaterial color={color} /> </mesh> </RigidBody> ); } export default Obstacle;
解决方案
RapierJS的碰撞体默认创建后不会自动跟随模型的scale变化,需要手动更新碰撞体尺寸或重建碰撞体,以下是两种可行方案:
方案1:手动更新规则几何体碰撞体(性能优先)
针对圆柱体这类规则几何体,可以直接通过RigidBody的ref获取碰撞体,修改其尺寸参数:
- 用状态管理圆柱体的尺寸,替换原有的
scale状态:
const [cylinderSize, setCylinderSize] = useState([4, 4, 2, 32]); // [radiusTop, radiusBottom, height, radialSegments]
- 在
useFrame中更新尺寸,同步修改碰撞体:
useFrame(() => { if (!obstacle.current.collided) { // 仅放大半径部分,高度保持不变 const newSize = [cylinderSize[0] + 0.1, cylinderSize[1] + 0.1, cylinderSize[2], cylinderSize[3]]; setCylinderSize(newSize); // 获取并更新碰撞体尺寸 if (rigidBody.current) { const collider = rigidBody.current.collider(0); if (collider) { // Rapier圆柱体参数为:半高、半径、边数,需和Three.js参数转换 collider.setAsCylinder(newSize[2]/2, newSize[0], newSize[3]); } } } });
- 将几何体的
args绑定到状态:
<cylinderGeometry args={cylinderSize} />
方案2:动态重建RigidBody(适配复杂几何体)
如果是复杂网格,无法直接修改碰撞体参数,可通过状态触发RigidBody重建:
- 添加控制重建的状态:
const [colliderKey, setColliderKey] = useState(0);
- 尺寸更新时改变
colliderKey,触发RigidBody重新渲染:
useFrame(() => { if (!obstacle.current.collided) { setScale(prev => prev + 0.1); // 避免帧帧重建,可设置间隔更新(比如每5帧) setColliderKey(prev => prev + 1); } });
- 给
RigidBody添加key属性:
<RigidBody key={colliderKey} ref={rigidBody} colliders="hull" // 其余属性保持不变 > {/* 模型部分不变 */} </RigidBody>
额外注意事项
- 方案1性能更优,适合规则几何体;方案2适合复杂网格,但频繁重建会有性能开销,需控制频率。
- 避免直接给
mesh添加自定义collided属性,建议用React状态管理碰撞状态(比如将hasCollided改为组件内部状态)。 - 注意Rapier碰撞体参数与Three.js几何体参数的差异(如圆柱体的半高/全高),需对应转换。
内容的提问来源于stack exchange,提问作者Cyril
相关产品推荐
相关产品推荐

