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

如何在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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 21:09:52