1. RigidBody vs RapierRigidBody
基本概念
import { RigidBody, RapierRigidBody } from "@react-three/rapier";
// RigidBody - React 组件(JSX 元素)
<RigidBody type="dynamic" position={[0, 5, 0]}>
<mesh />
</RigidBody>
// RapierRigidBody - TypeScript 类型(引用对象)
const ref = useRef<RapierRigidBody>(null);2. RigidBody 组件详解
作用
桥接 Three.js 和 Rapier 物理引擎
自动同步视觉对象和物理对象的位置
声明式配置物理属性
实现原理(简化版源码)
// @react-three/rapier 简化实现
import { useEffect, useRef } from 'react';
import { useFrame } from '@react-three/fiber';
import * as RAPIER from '@dimforge/rapier3d-compat';
function RigidBody({
children,
type = 'dynamic',
position = [0, 0, 0],
colliders = 'cuboid',
...props
}) {
const groupRef = useRef();
const rigidBodyRef = useRef();
const { world } = useRapier(); // 获取物理世界实例
// 1. 创建物理刚体
useEffect(() => {
// 创建刚体描述符
const rigidBodyDesc = type === 'dynamic'
? RAPIER.RigidBodyDesc.dynamic()
: RAPIER.RigidBodyDesc.fixed();
// 设置初始位置
rigidBodyDesc.setTranslation(...position);
// 在物理世界中创建刚体
const rigidBody = world.createRigidBody(rigidBodyDesc);
rigidBodyRef.current = rigidBody;
// 2. 创建碰撞器(如果指定)
if (colliders === 'cuboid') {
const colliderDesc = RAPIER.ColliderDesc.cuboid(0.5, 0.5, 0.5);
world.createCollider(colliderDesc, rigidBody);
}
return () => {
// 清理
world.removeRigidBody(rigidBody);
};
}, []);
// 3. 每帧同步:物理世界 → Three.js 视觉对象
useFrame(() => {
if (!rigidBodyRef.current || !groupRef.current) return;
// 从物理引擎获取位置
const translation = rigidBodyRef.current.translation();
const rotation = rigidBodyRef.current.rotation();
// 更新 Three.js 对象的位置
groupRef.current.position.set(
translation.x,
translation.y,
translation.z
);
// 更新旋转
groupRef.current.quaternion.set(
rotation.x,
rotation.y,
rotation.z,
rotation.w
);
});
// 4. 返回 Three.js group 包裹子元素
return (
<group ref={groupRef}>
{children}
</group>
);
}3. 工作流程图
┌─────────────────────────────────────────────┐
│ React 组件层 │
│ <RigidBody type="dynamic" position={[0,5,0]}>│
│ <mesh>...</mesh> │
│ </RigidBody> │
└─────────────┬───────────────────────────────┘
│
▼
┌─────────────────────────────────────────────┐
│ 物理世界初始化 │
│ ┌─────────────────────────────────────┐ │
│ │ RAPIER.World.createRigidBody() │ │
│ │ - 创建物理刚体 │ │
│ │ - 设置质量、摩擦力等 │ │
│ └─────────────────────────────────────┘ │
│ ┌─────────────────────────────────────┐ │
│ │ RAPIER.World.createCollider() │ │
│ │ - 创建碰撞形状 │ │
│ │ - 绑定到刚体 │ │
│ └─────────────────────────────────────┘ │
└─────────────┬───────────────────────────────┘
│
▼
┌─────────────────────────────────────────────┐
│ 每帧同步循环 (useFrame) │
│ │
│ 物理计算: │
│ ┌─────────────────────────────────────┐ │
│ │ world.step(deltaTime) │ │
│ │ - 重力影响 │ │
│ │ - 碰撞检测 │ │
│ │ - 力的计算 │ │
│ │ → 更新刚体位置 │ │
│ └─────────────────────────────────────┘ │
│ ▼ │
│ 同步到 Three.js: │
│ ┌─────────────────────────────────────┐ │
│ │ const pos = rigidBody.translation() │ │
│ │ mesh.position.set(pos.x, pos.y, pos.z)│ │
│ └─────────────────────────────────────┘ │
└─────────────────────────────────────────────┘4. RapierRigidBody 类型详解
定义
// @react-three/rapier 类型定义
interface RapierRigidBody {
// 位置控制
translation(): { x: number; y: number; z: number };
setTranslation(pos: { x: number; y: number; z: number }, wakeUp: boolean): void;
// 速度控制
linvel(): { x: number; y: number; z: number };
setLinvel(vel: { x: number; y: number; z: number }, wakeUp: boolean): void;
// 旋转控制
rotation(): { x: number; y: number; z: number; w: number };
setRotation(quat: Quaternion, wakeUp: boolean): void;
// 力的施加
applyImpulse(impulse: { x: number; y: number; z: number }, wakeUp: boolean): void;
applyForce(force: { x: number; y: number; z: number }, wakeUp: boolean): void;
applyTorqueImpulse(torque: { x: number; y: number; z: number }, wakeUp: boolean): void;
// 其他属性
mass(): number;
isSleeping(): boolean;
wakeUp(): void;
// ... 更多方法
}使用示例
import { useRef } from "react";
import { useFrame } from "@react-three/fiber";
import { RigidBody, RapierRigidBody } from "@react-three/rapier";
import { useKeyboardControls } from "@react-three/drei";
function Player() {
// ✅ 使用 RapierRigidBody 类型定义 ref
const rigidBodyRef = useRef<RapierRigidBody>(null);
const [, getKeys] = useKeyboardControls();
useFrame((state, delta) => {
if (!rigidBodyRef.current) return;
const { forward, backward, left, right, jump } = getKeys();
// 1. 读取当前状态
const position = rigidBodyRef.current.translation();
const velocity = rigidBodyRef.current.linvel();
console.log('位置:', position);
console.log('速度:', velocity);
// 2. 控制移动
const speed = 5;
const moveVector = { x: 0, y: velocity.y, z: 0 };
if (forward) moveVector.z = -speed;
if (backward) moveVector.z = speed;
if (left) moveVector.x = -speed;
if (right) moveVector.x = speed;
// 设置线性速度
rigidBodyRef.current.setLinvel(moveVector, true);
// 3. 跳跃(施加冲量)
if (jump && Math.abs(velocity.y) < 0.1) {
rigidBodyRef.current.applyImpulse(
{ x: 0, y: 5, z: 0 },
true
);
}
// 4. 限制最大速度
if (velocity.y < -20) {
rigidBodyRef.current.setLinvel(
{ x: velocity.x, y: -20, z: velocity.z },
true
);
}
// 5. 重置位置(掉出世界时)
if (position.y < -10) {
rigidBodyRef.current.setTranslation(
{ x: 0, y: 5, z: 0 },
true
);
rigidBodyRef.current.setLinvel(
{ x: 0, y: 0, z: 0 },
true
);
}
});
return (
// ✅ 将 ref 绑定到 RigidBody 组件
<RigidBody
ref={rigidBodyRef}
type="dynamic"
colliders={false}
position={[0, 4, 0]}
>
<mesh>
<capsuleGeometry args={[0.5, 1, 8, 16]} />
<meshStandardMaterial color="orange" />
</mesh>
<CapsuleCollider args={[0.6, 0.3]} />
</RigidBody>
);
}5. 双向同步机制
物理 → 视觉(自动)
// RigidBody 组件内部每帧执行
useFrame(() => {
// 从物理引擎读取
const pos = rapierRigidBody.translation();
const rot = rapierRigidBody.rotation();
// 同步到 Three.js 对象
threeJsGroup.position.copy(pos);
threeJsGroup.quaternion.copy(rot);
});视觉 → 物理(手动)
// 你的代码控制物理对象
rigidBodyRef.current.setTranslation({ x: 10, y: 0, z: 0 }, true);
// 下一帧自动同步回视觉
// threeJsGroup.position 会变成 (10, 0, 0)6. 完整的物理引擎架构
┌──────────────────────────────────────────────┐
│ React Three Fiber (R3F) │
│ ┌────────────────────────────────────────┐ │
│ │ <Canvas> │ │
│ │ <Physics> ← 创建 RAPIER.World │ │
│ │ <RigidBody> ← 创建 RAPIER.RigidBody│ │
│ │ <mesh /> ← Three.js 视觉对象 │ │
│ │ </RigidBody> │ │
│ │ </Physics> │ │
│ │ </Canvas> │ │
│ └────────────────────────────────────────┘ │
└──────────────┬───────────────────────────────┘
│
▼
┌──────────────────────────────────────────────┐
│ Rapier 物理引擎 │
│ ┌────────────────────────────────────────┐ │
│ │ World (物理世界) │ │
│ │ ├── RigidBody 1 (刚体) │ │
│ │ │ └── Collider (碰撞器) │ │
│ │ ├── RigidBody 2 │ │
│ │ │ └── Collider │ │
│ │ └── step() 物理计算 │ │
│ └────────────────────────────────────────┘ │
└──────────────┬───────────────────────────────┘
│
▼
┌──────────────────────────────────────────────┐
│ 每帧更新 (useFrame) │
│ 1. world.step(deltaTime) │
│ 2. 碰撞检测、重力计算 │
│ 3. 更新刚体位置 │
│ 4. 同步到 Three.js mesh.position │
└──────────────────────────────────────────────┘7. 核心 API 对比
RigidBody 组件的 Props
<RigidBody
// 类型
type="dynamic" // 'dynamic' | 'kinematic' | 'fixed'
// 位置和旋转
position={[0, 5, 0]}
rotation={[0, 0, 0]}
// 物理属性
mass={1}
gravityScale={1}
linearDamping={0.5}
angularDamping={0.5}
// 约束
enabledRotations={[true, true, true]}
enabledTranslations={[true, true, true]}
lockRotations // 快捷方式,等于 enabledRotations={[false, false, false]}
// 碰撞器
colliders="cuboid" // 'cuboid' | 'ball' | 'trimesh' | false
// 其他
friction={0.5}
restitution={0.3} // 弹性系数
ccd={true} // 连续碰撞检测
/>RapierRigidBody ref 的方法
const ref = useRef<RapierRigidBody>(null);
// 位置
ref.current.translation() // 读取
ref.current.setTranslation({ x, y, z }, true) // 设置
// 速度
ref.current.linvel() // 线性速度
ref.current.setLinvel({ x, y, z }, true)
ref.current.angvel() // 角速度
ref.current.setAngvel({ x, y, z }, true)
// 力
ref.current.applyImpulse({ x, y, z }, true) // 冲量
ref.current.applyForce({ x, y, z }, true) // 持续力
ref.current.applyTorqueImpulse({ x, y, z }, true) // 扭矩
// 状态
ref.current.isSleeping() // 是否休眠
ref.current.wakeUp() // 唤醒
ref.current.mass() // 质量8. 实际应用示例
import { useGLTF, useKeyboardControls } from "@react-three/drei";
import { CapsuleCollider, RigidBody, RapierRigidBody } from "@react-three/rapier";
import { useRef } from "react";
import { useFrame } from "@react-three/fiber";
useGLTF.preload("/models/Actor/actor.gltf");
function Player() {
const { scene } = useGLTF("/models/Actor/actor.gltf");
const rigidBodyRef = useRef<RapierRigidBody>(null);
const [, getKeys] = useKeyboardControls();
useFrame((state, delta) => {
if (!rigidBodyRef.current) return;
const { forward, backward, left, right, jump } = getKeys();
const velocity = rigidBodyRef.current.linvel();
// 移动逻辑
const speed = 5;
const impulse = { x: 0, y: velocity.y, z: 0 };
if (forward) impulse.z = -speed;
if (backward) impulse.z = speed;
if (left) impulse.x = -speed;
if (right) impulse.x = speed;
rigidBodyRef.current.setLinvel(impulse, true);
// 跳跃
if (jump && Math.abs(velocity.y) < 0.1) {
rigidBodyRef.current.applyImpulse({ x: 0, y: 5, z: 0 }, true);
}
});
return (
<RigidBody
ref={rigidBodyRef}
colliders={false}
type="dynamic"
enabledRotations={[false, false, false]}
position={[0, 4, 0]}
>
<primitive object={scene} />
<CapsuleCollider args={[0.6, 0.3]} />
</RigidBody>
);
}
export default Player;总结
核心流程:
<RigidBody>创建 Rapier 刚体ref={rigidBodyRef}获取刚体实例useFrame中读取/修改物理状态Rapier 计算物理
自动同步到 Three.js 视觉对象
这就是 React Three Fiber + Rapier 的完整物理引擎架构!