You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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获取碰撞体,修改其尺寸参数:

  1. 用状态管理圆柱体的尺寸,替换原有的scale状态:
const [cylinderSize, setCylinderSize] = useState([4, 4, 2, 32]); // [radiusTop, radiusBottom, height, radialSegments]
  1. 在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]);
            }
        }
    }
});
  1. 将几何体的args绑定到状态:
<cylinderGeometry args={cylinderSize} />

方案2:动态重建RigidBody(适配复杂几何体)

如果是复杂网格,无法直接修改碰撞体参数,可通过状态触发RigidBody重建:

  1. 添加控制重建的状态:
const [colliderKey, setColliderKey] = useState(0);
  1. 尺寸更新时改变colliderKey,触发RigidBody重新渲染:
useFrame(() => {
    if (!obstacle.current.collided) {
        setScale(prev => prev + 0.1);
        // 避免帧帧重建,可设置间隔更新(比如每5帧)
        setColliderKey(prev => prev + 1);
    }
});
  1. 给RigidBody添加key属性:
<RigidBody 
    key={colliderKey}
    ref={rigidBody}
    colliders="hull"
    // 其余属性保持不变
>
    {/* 模型部分不变 */}
</RigidBody>

额外注意事项

  • 方案1性能更优,适合规则几何体;方案2适合复杂网格,但频繁重建会有性能开销,需控制频率。
  • 避免直接给mesh添加自定义collided属性,建议用React状态管理碰撞状态(比如将hasCollided改为组件内部状态)。
  • 注意Rapier碰撞体参数与Three.js几何体参数的差异(如圆柱体的半高/全高),需对应转换。

内容的提问来源于stack exchange,提问作者Cyril

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 03:42:08