如何在React Three Rapier中为挤出SVG绑定贴合刚体物理
解决@react-three/rapier中挤出SVG的贴合碰撞体问题
问题根源
- 嵌套Mesh结构:
ExtrudedSvg组件返回父Mesh包裹多个子Mesh,react-three-rapier的自动碰撞体生成逻辑(colliders="hull")无法正确遍历嵌套层级的几何体数据,导致读取顶点数组时抛出TypeError: Cannot read properties of undefined (reading 'array')错误。 - 位置偏移:
<RigidBody>和ExtrudedSvg同时设置position,坐标叠加后造成碰撞体与视觉Mesh位置不匹配。
解决方案
方案1:合并几何体,扁平化Mesh结构
将SVG挤出的多个子几何体合并为单一BufferGeometry,让react-three-rapier能直接识别并生成贴合的hull碰撞体:
修改ExtrudedSvg组件:
import { useMemo } from "react"; import * as THREE from "three"; import { SVGLoader } from "three/examples/jsm/Addons.js"; import { useLoader } from "@react-three/fiber"; type ExtrudedSvgProps = { filename: string; scale?: number; position?: [number, number, number]; }; export default function ExtrudedSvg({ filename, scale = 1, position = [-10, 10, 0], }: ExtrudedSvgProps) { const svgData = useLoader(SVGLoader, filename); const mergedGeometry = useMemo(() => { const geometries: THREE.ExtrudeGeometry[] = []; svgData.paths.forEach(path => { const shapes = path.toShapes(true); shapes.forEach(shape => { const geo = new THREE.ExtrudeGeometry(shape, { depth: 5, bevelEnabled: false, steps: 30, }); geometries.push(geo); }); }); // 合并所有几何体 return THREE.BufferGeometryUtils.mergeGeometries(geometries); }, [svgData]); return ( <mesh scale={scale} rotation={[Math.PI, 0, 0]} position={position} geometry={mergedGeometry} > <meshPhongMaterial color={svgData.paths[0]?.color || "#fff"} side={THREE.DoubleSide} /> </mesh> ); }
方案2:手动为子Mesh添加碰撞体
如果合并几何体不符合需求,可在每个子Mesh外嵌套<Collider>组件,手动指定碰撞体类型:
修改ExtrudedSvg组件:
import { useMemo } from "react"; import * as THREE from "three"; import { SVGLoader } from "three/examples/jsm/Addons.js"; import { useLoader } from "@react-three/fiber"; import { Collider } from "@react-three/rapier"; type ExtrudedSvgProps = { filename: string; scale?: number; position?: [number, number, number]; }; export default function ExtrudedSvg({ filename, scale = 1, position = [-10, 10, 0], }: ExtrudedSvgProps) { const svgData = useLoader(SVGLoader, filename); const shapes = useMemo(() => svgData.paths.map(p => p.toShapes(true)), [svgData]); return ( <group scale={scale} rotation={[Math.PI, 0, 0]} position={position} > {shapes.map((shapeGroup, i) => ( <> {shapeGroup.map((shape, j) => ( <Collider key={`${i}-${j}`} type="hull"> <mesh> <extrudeGeometry args={[shape, { depth: 5, bevelEnabled: false, steps: 30 }]} /> <meshPhongMaterial color={svgData.paths[i].color} side={THREE.DoubleSide} /> </mesh> </Collider> ))} </> ))} </group> ); }
修正位置偏移问题
统一位置控制,只在<RigidBody>上设置position,ExtrudedSvg的position设为原点:
<RigidBody colliders="hull" position={[-5, 20, 0]}> <ExtrudedSvg filename="fanaro.io.svg" scale={0.1} position={[0, 0, 0]} /> </RigidBody>
关于InstancedMesh的说明
不需要使用<InstancedMesh>,你的场景是多个独立的挤出形状,合并几何体或手动添加碰撞体是更直接的方案,InstancedMesh主要用于优化大量重复相同几何体的渲染性能。
内容的提问来源于stack exchange,提问作者psygo
相关产品推荐
相关产品推荐

