r3f-physics
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseReact Three Fiber Physics (Rapier)
React Three Fiber 物理系统(基于Rapier)
Quick Start
快速开始
tsx
import { Canvas } from '@react-three/fiber'
import { Physics, RigidBody, CuboidCollider } from '@react-three/rapier'
import { Suspense } from 'react'
function Scene() {
return (
<Canvas>
<Suspense fallback={null}>
<Physics debug>
{/* Falling box */}
<RigidBody>
<mesh>
<boxGeometry />
<meshStandardMaterial color="orange" />
</mesh>
</RigidBody>
{/* Static ground */}
<CuboidCollider position={[0, -2, 0]} args={[10, 0.5, 10]} />
</Physics>
</Suspense>
<ambientLight />
<directionalLight position={[5, 5, 5]} />
</Canvas>
)
}tsx
import { Canvas } from '@react-three/fiber'
import { Physics, RigidBody, CuboidCollider } from '@react-three/rapier'
import { Suspense } from 'react'
function Scene() {
return (
<Canvas>
<Suspense fallback={null}>
<Physics debug>
{/* Falling box */}
<RigidBody>
<mesh>
<boxGeometry />
<meshStandardMaterial color="orange" />
</mesh>
</RigidBody>
{/* Static ground */}
<CuboidCollider position={[0, -2, 0]} args={[10, 0.5, 10]} />
</Physics>
</Suspense>
<ambientLight />
<directionalLight position={[5, 5, 5]} />
</Canvas>
)
}Installation
安装
bash
npm install @react-three/rapierbash
npm install @react-three/rapierPhysics Component
Physics 组件
The root component that creates the physics world.
tsx
import { Physics } from '@react-three/rapier'
<Canvas>
<Suspense fallback={null}>
<Physics
gravity={[0, -9.81, 0]} // Gravity vector
debug={false} // Show collider wireframes
timeStep={1/60} // Fixed timestep (or "vary" for variable)
paused={false} // Pause simulation
interpolate={true} // Smooth rendering between physics steps
colliders="cuboid" // Default collider type for all RigidBodies
updateLoop="follow" // "follow" (sync with frame) or "independent"
>
{/* Physics objects */}
</Physics>
</Suspense>
</Canvas>这是创建物理世界的根组件。
tsx
import { Physics } from '@react-three/rapier'
<Canvas>
<Suspense fallback={null}>
<Physics
gravity={[0, -9.81, 0]} // Gravity vector
debug={false} // Show collider wireframes
timeStep={1/60} // Fixed timestep (or "vary" for variable)
paused={false} // Pause simulation
interpolate={true} // Smooth rendering between physics steps
colliders="cuboid" // Default collider type for all RigidBodies
updateLoop="follow" // "follow" (sync with frame) or "independent"
>
{/* Physics objects */}
</Physics>
</Suspense>
</Canvas>On-Demand Rendering
按需渲染
For performance optimization with static scenes:
tsx
<Canvas frameloop="demand">
<Physics updateLoop="independent">
{/* Physics only triggers render when bodies are active */}
</Physics>
</Canvas>针对静态场景的性能优化方案:
tsx
<Canvas frameloop="demand">
<Physics updateLoop="independent">
{/* 仅当物体处于活动状态时,物理系统才会触发渲染 */}
</Physics>
</Canvas>RigidBody
RigidBody(刚体)
Makes objects participate in physics simulation.
使物体参与物理模拟。
Basic Usage
基础用法
tsx
import { RigidBody } from '@react-three/rapier'
// Dynamic body (affected by forces/gravity)
<RigidBody>
<mesh>
<boxGeometry />
<meshStandardMaterial color="red" />
</mesh>
</RigidBody>
// Fixed body (immovable)
<RigidBody type="fixed">
<mesh>
<boxGeometry args={[10, 0.5, 10]} />
<meshStandardMaterial color="gray" />
</mesh>
</RigidBody>
// Kinematic body (moved programmatically)
<RigidBody type="kinematicPosition">
<mesh>
<sphereGeometry />
<meshStandardMaterial color="blue" />
</mesh>
</RigidBody>tsx
import { RigidBody } from '@react-three/rapier'
// 动态物体(受重力、力和碰撞影响)
<RigidBody>
<mesh>
<boxGeometry />
<meshStandardMaterial color="red" />
</mesh>
</RigidBody>
// 静态物体(不可移动)
<RigidBody type="fixed">
<mesh>
<boxGeometry args={[10, 0.5, 10]} />
<meshStandardMaterial color="gray" />
</mesh>
</RigidBody>
// 运动学物体(通过代码控制移动)
<RigidBody type="kinematicPosition">
<mesh>
<sphereGeometry />
<meshStandardMaterial color="blue" />
</mesh>
</RigidBody>RigidBody Types
刚体类型
| Type | Description |
|---|---|
| Affected by forces, gravity, collisions (default) |
| Immovable, infinite mass |
| Moved via setNextKinematicTranslation |
| Moved via setNextKinematicRotation |
| 类型 | 描述 |
|---|---|
| 受重力、力和碰撞影响(默认类型) |
| 不可移动,质量无限大 |
| 通过setNextKinematicTranslation方法移动 |
| 通过setNextKinematicRotation方法移动 |
RigidBody Properties
刚体属性
tsx
<RigidBody
// Transform
position={[0, 5, 0]}
rotation={[0, Math.PI / 4, 0]}
scale={1}
// Physics
type="dynamic"
mass={1}
restitution={0.5} // Bounciness (0-1)
friction={0.5} // Surface friction
linearDamping={0} // Slows linear velocity
angularDamping={0} // Slows angular velocity
gravityScale={1} // Multiplier for gravity
// Collider generation
colliders="cuboid" // "cuboid" | "ball" | "hull" | "trimesh" | false
// Constraints
lockTranslations={false} // Prevent all translation
lockRotations={false} // Prevent all rotation
enabledTranslations={[true, true, true]} // Lock specific axes
enabledRotations={[true, true, true]} // Lock specific axes
// Sleeping
canSleep={true}
ccd={false} // Continuous collision detection (fast objects)
// Naming (for collision events)
name="player"
/>tsx
<RigidBody
// 变换属性
position={[0, 5, 0]}
rotation={[0, Math.PI / 4, 0]}
scale={1}
// 物理属性
type="dynamic"
mass={1}
restitution={0.5} // 弹性(取值范围0-1)
friction={0.5} // 表面摩擦力
linearDamping={0} // 线性速度阻尼
angularDamping={0} // 角速度阻尼
gravityScale={1} // 重力倍数
// 碰撞器生成
colliders="cuboid" // "cuboid" | "ball" | "hull" | "trimesh" | false
// 约束设置
lockTranslations={false} // 禁止所有平移
lockRotations={false} // 禁止所有旋转
enabledTranslations={[true, true, true]} // 锁定指定轴的平移
enabledRotations={[true, true, true]} // 锁定指定轴的旋转
// 休眠设置
canSleep={true}
ccd={false} // 连续碰撞检测(适用于快速移动的物体)
// 命名(用于碰撞事件)
name="player"
/>Colliders
碰撞器
Automatic Colliders
自动碰撞器
RigidBody auto-generates colliders from child meshes:
tsx
// Global default
<Physics colliders="hull">
<RigidBody>
<Torus /> {/* Gets hull collider */}
</RigidBody>
</Physics>
// Per-body override
<Physics colliders={false}>
<RigidBody colliders="cuboid">
<Box />
</RigidBody>
<RigidBody colliders="ball">
<Sphere />
</RigidBody>
</Physics>RigidBody会根据子网格自动生成碰撞器:
tsx
// 全局默认设置
<Physics colliders="hull">
<RigidBody>
<Torus /> {/* 自动生成hull类型碰撞器 */}
</RigidBody>
</Physics>
// 单个物体覆盖全局设置
<Physics colliders={false}>
<RigidBody colliders="cuboid">
<Box />
</RigidBody>
<RigidBody colliders="ball">
<Sphere />
</RigidBody>
</Physics>Collider Types
碰撞器类型
| Type | Description | Best For |
|---|---|---|
| Box shape | Boxes, crates |
| Sphere shape | Balls, spherical objects |
| Convex hull | Complex convex shapes |
| Triangle mesh | Concave/complex static geometry |
| 类型 | 描述 | 适用场景 |
|---|---|---|
| 盒状碰撞器 | 盒子、板条箱等规则物体 |
| 球状碰撞器 | 球体、圆形物体 |
| 凸包碰撞器 | 复杂的凸面形状 |
| 三角网格碰撞器 | 凹面或复杂的静态几何体 |
Manual Colliders
手动碰撞器
tsx
import {
CuboidCollider,
BallCollider,
CapsuleCollider,
CylinderCollider,
ConeCollider,
HeightfieldCollider,
TrimeshCollider,
ConvexHullCollider
} from '@react-three/rapier'
// Standalone collider (static)
<CuboidCollider position={[0, -2, 0]} args={[10, 0.5, 10]} />
// Inside RigidBody (compound collider)
<RigidBody position={[0, 5, 0]}>
<mesh>
<boxGeometry />
<meshStandardMaterial />
</mesh>
{/* Additional colliders */}
<BallCollider args={[0.5]} position={[0, 1, 0]} />
<CapsuleCollider args={[0.5, 1]} position={[0, -1, 0]} />
</RigidBody>
// Collider args reference
<CuboidCollider args={[halfWidth, halfHeight, halfDepth]} />
<BallCollider args={[radius]} />
<CapsuleCollider args={[halfHeight, radius]} />
<CylinderCollider args={[halfHeight, radius]} />
<ConeCollider args={[halfHeight, radius]} />tsx
import {
CuboidCollider,
BallCollider,
CapsuleCollider,
CylinderCollider,
ConeCollider,
HeightfieldCollider,
TrimeshCollider,
ConvexHullCollider
} from '@react-three/rapier'
// 独立碰撞器(静态)
<CuboidCollider position={[0, -2, 0]} args={[10, 0.5, 10]} />
// 嵌套在RigidBody中(复合碰撞器)
<RigidBody position={[0, 5, 0]}>
<mesh>
<boxGeometry />
<meshStandardMaterial />
</mesh>
{/* 额外添加的碰撞器 */}
<BallCollider args={[0.5]} position={[0, 1, 0]} />
<CapsuleCollider args={[0.5, 1]} position={[0, -1, 0]} />
</RigidBody>
// 碰撞器参数说明
<CuboidCollider args={[半宽, 半高, 半深]} />
<BallCollider args={[半径]} />
<CapsuleCollider args={[半高, 半径]} />
<CylinderCollider args={[半高, 半径]} />
<ConeCollider args={[半高, 半径]} />Mesh Colliders
网格碰撞器
For complex shapes:
tsx
import { MeshCollider } from '@react-three/rapier'
<RigidBody colliders={false}>
<MeshCollider type="trimesh">
<mesh geometry={complexGeometry}>
<meshStandardMaterial />
</mesh>
</MeshCollider>
</RigidBody>
// Convex hull for dynamic bodies
<RigidBody colliders={false}>
<MeshCollider type="hull">
<mesh geometry={someGeometry} />
</MeshCollider>
</RigidBody>适用于复杂形状:
tsx
import { MeshCollider } from '@react-three/rapier'
<RigidBody colliders={false}>
<MeshCollider type="trimesh">
<mesh geometry={complexGeometry}>
<meshStandardMaterial />
</mesh>
</MeshCollider>
</RigidBody>
// 动态物体使用凸包碰撞器
<RigidBody colliders={false}>
<MeshCollider type="hull">
<mesh geometry={someGeometry} />
</MeshCollider>
</RigidBody>Applying Forces
施加力
Using Refs
使用Ref
tsx
import { RigidBody, RapierRigidBody } from '@react-three/rapier'
import { useRef, useEffect } from 'react'
function ForcefulBox() {
const rigidBody = useRef<RapierRigidBody>(null)
useEffect(() => {
if (rigidBody.current) {
// One-time impulse (instantaneous)
rigidBody.current.applyImpulse({ x: 0, y: 10, z: 0 }, true)
// Continuous force (apply each frame)
rigidBody.current.addForce({ x: 0, y: 10, z: 0 }, true)
// Torque (rotation)
rigidBody.current.applyTorqueImpulse({ x: 0, y: 5, z: 0 }, true)
rigidBody.current.addTorque({ x: 0, y: 5, z: 0 }, true)
}
}, [])
return (
<RigidBody ref={rigidBody}>
<mesh>
<boxGeometry />
<meshStandardMaterial color="red" />
</mesh>
</RigidBody>
)
}tsx
import { RigidBody, RapierRigidBody } from '@react-three/rapier'
import { useRef, useEffect } from 'react'
function ForcefulBox() {
const rigidBody = useRef<RapierRigidBody>(null)
useEffect(() => {
if (rigidBody.current) {
// 一次性冲量(瞬时力)
rigidBody.current.applyImpulse({ x: 0, y: 10, z: 0 }, true)
// 持续力(每帧施加)
rigidBody.current.addForce({ x: 0, y: 10, z: 0 }, true)
// 扭矩(旋转力)
rigidBody.current.applyTorqueImpulse({ x: 0, y: 5, z: 0 }, true)
rigidBody.current.addTorque({ x: 0, y: 5, z: 0 }, true)
}
}, [])
return (
<RigidBody ref={rigidBody}>
<mesh>
<boxGeometry />
<meshStandardMaterial color="red" />
</mesh>
</RigidBody>
)
}In useFrame
在useFrame中使用
tsx
import { useFrame } from '@react-three/fiber'
function ContinuousForce() {
const rigidBody = useRef<RapierRigidBody>(null)
useFrame(() => {
if (rigidBody.current) {
// Apply force every frame
rigidBody.current.addForce({ x: 0, y: 20, z: 0 }, true)
}
})
return (
<RigidBody ref={rigidBody} gravityScale={0.5}>
<mesh>
<sphereGeometry />
<meshStandardMaterial color="blue" />
</mesh>
</RigidBody>
)
}tsx
import { useFrame } from '@react-three/fiber'
function ContinuousForce() {
const rigidBody = useRef<RapierRigidBody>(null)
useFrame(() => {
if (rigidBody.current) {
// 每帧施加力
rigidBody.current.addForce({ x: 0, y: 20, z: 0 }, true)
}
})
return (
<RigidBody ref={rigidBody} gravityScale={0.5}>
<mesh>
<sphereGeometry />
<meshStandardMaterial color="blue" />
</mesh>
</RigidBody>
)
}Getting/Setting Position
获取/设置位置
tsx
import { vec3, quat, euler } from '@react-three/rapier'
function PositionControl() {
const rigidBody = useRef<RapierRigidBody>(null)
const teleport = () => {
if (rigidBody.current) {
// Get current transform
const position = vec3(rigidBody.current.translation())
const rotation = quat(rigidBody.current.rotation())
// Set new transform
rigidBody.current.setTranslation({ x: 0, y: 10, z: 0 }, true)
rigidBody.current.setRotation({ x: 0, y: 0, z: 0, w: 1 }, true)
// Set velocities
rigidBody.current.setLinvel({ x: 0, y: 0, z: 0 }, true)
rigidBody.current.setAngvel({ x: 0, y: 0, z: 0 }, true)
}
}
return (
<RigidBody ref={rigidBody}>
<mesh onClick={teleport}>
<boxGeometry />
<meshStandardMaterial />
</mesh>
</RigidBody>
)
}tsx
import { vec3, quat, euler } from '@react-three/rapier'
function PositionControl() {
const rigidBody = useRef<RapierRigidBody>(null)
const teleport = () => {
if (rigidBody.current) {
// 获取当前变换
const position = vec3(rigidBody.current.translation())
const rotation = quat(rigidBody.current.rotation())
// 设置新的变换
rigidBody.current.setTranslation({ x: 0, y: 10, z: 0 }, true)
rigidBody.current.setRotation({ x: 0, y: 0, z: 0, w: 1 }, true)
// 设置速度
rigidBody.current.setLinvel({ x: 0, y: 0, z: 0 }, true)
rigidBody.current.setAngvel({ x: 0, y: 0, z: 0 }, true)
}
}
return (
<RigidBody ref={rigidBody}>
<mesh onClick={teleport}>
<boxGeometry />
<meshStandardMaterial />
</mesh>
</RigidBody>
)
}Collision Events
碰撞事件
On RigidBody
在RigidBody上监听
tsx
<RigidBody
name="player"
onCollisionEnter={({ manifold, target, other }) => {
console.log('Collision with', other.rigidBodyObject?.name)
console.log('Contact point', manifold.solverContactPoint(0))
}}
onCollisionExit={({ target, other }) => {
console.log('Collision ended with', other.rigidBodyObject?.name)
}}
onContactForce={({ totalForce }) => {
console.log('Contact force:', totalForce)
}}
onSleep={() => console.log('Body went to sleep')}
onWake={() => console.log('Body woke up')}
>
<mesh>
<boxGeometry />
<meshStandardMaterial />
</mesh>
</RigidBody>tsx
<RigidBody
name="player"
onCollisionEnter={({ manifold, target, other }) => {
console.log('与以下物体碰撞', other.rigidBodyObject?.name)
console.log('接触点', manifold.solverContactPoint(0))
}}
onCollisionExit={({ target, other }) => {
console.log('与以下物体的碰撞结束', other.rigidBodyObject?.name)
}}
onContactForce={({ totalForce }) => {
console.log('接触力:', totalForce)
}}
onSleep={() => console.log('物体进入休眠状态')}
onWake={() => console.log('物体被唤醒')}
>
<mesh>
<boxGeometry />
<meshStandardMaterial />
</mesh>
</RigidBody>On Colliders
在碰撞器上监听
tsx
<CuboidCollider
args={[1, 1, 1]}
onCollisionEnter={(payload) => console.log('Collider hit')}
onCollisionExit={(payload) => console.log('Collider exit')}
/>tsx
<CuboidCollider
args={[1, 1, 1]}
onCollisionEnter={(payload) => console.log('碰撞器被击中')}
onCollisionExit={(payload) => console.log('碰撞器离开碰撞')}
/>Sensors
传感器
Detect overlaps without physical collision:
tsx
<RigidBody>
{/* Visible mesh */}
<mesh>
<boxGeometry />
<meshStandardMaterial />
</mesh>
{/* Invisible sensor trigger */}
<CuboidCollider
args={[2, 2, 2]}
sensor
onIntersectionEnter={() => console.log('Entered trigger zone')}
onIntersectionExit={() => console.log('Exited trigger zone')}
/>
</RigidBody>
// Goal detection example
<RigidBody type="fixed">
<GoalPosts />
<CuboidCollider
args={[5, 5, 1]}
sensor
onIntersectionEnter={() => console.log('Goal!')}
/>
</RigidBody>检测物体重叠但不产生物理碰撞:
tsx
<RigidBody>
{/* 可见网格 */}
<mesh>
<boxGeometry />
<meshStandardMaterial />
</mesh>
{/* 不可见的传感器触发器 */}
<CuboidCollider
args={[2, 2, 2]}
sensor
onIntersectionEnter={() => console.log('进入触发区域')}
onIntersectionExit={() => console.log('离开触发区域')}
/>
</RigidBody>
// 进球检测示例
<RigidBody type="fixed">
<GoalPosts />
<CuboidCollider
args={[5, 5, 1]}
sensor
onIntersectionEnter={() => console.log('进球了!')}
/>
</RigidBody>Collision Groups
碰撞组
Control which objects can collide:
tsx
import { interactionGroups } from '@react-three/rapier'
// Group 0, interacts with groups 0, 1, 2
<CuboidCollider collisionGroups={interactionGroups(0, [0, 1, 2])} />
// Group 12, interacts with all groups
<CuboidCollider collisionGroups={interactionGroups(12)} />
// Groups 0 and 5, only interacts with group 7
<CuboidCollider collisionGroups={interactionGroups([0, 5], 7)} />
// On RigidBody (applies to all auto-generated colliders)
<RigidBody collisionGroups={interactionGroups(1, [1, 2])}>
<mesh>...</mesh>
</RigidBody>控制哪些物体之间可以发生碰撞:
tsx
import { interactionGroups } from '@react-three/rapier'
// 组0,可与组0、1、2发生碰撞
<CuboidCollider collisionGroups={interactionGroups(0, [0, 1, 2])} />
// 组12,可与所有组发生碰撞
<CuboidCollider collisionGroups={interactionGroups(12)} />
// 组0和5,仅能与组7发生碰撞
<CuboidCollider collisionGroups={interactionGroups([0, 5], 7)} />
// 在RigidBody上设置(对所有自动生成的碰撞器生效)
<RigidBody collisionGroups={interactionGroups(1, [1, 2])}>
<mesh>...</mesh>
</RigidBody>Joints
关节
Connect rigid bodies together.
用于连接多个刚体。
Fixed Joint
固定关节
Bodies don't move relative to each other:
tsx
import { useFixedJoint, RapierRigidBody } from '@react-three/rapier'
function FixedJointExample() {
const bodyA = useRef<RapierRigidBody>(null)
const bodyB = useRef<RapierRigidBody>(null)
useFixedJoint(bodyA, bodyB, [
[0, 0, 0], // Position in bodyA's local space
[0, 0, 0, 1], // Orientation in bodyA's local space (quaternion)
[0, -1, 0], // Position in bodyB's local space
[0, 0, 0, 1], // Orientation in bodyB's local space
])
return (
<>
<RigidBody ref={bodyA} position={[0, 5, 0]}>
<mesh><boxGeometry /><meshStandardMaterial color="red" /></mesh>
</RigidBody>
<RigidBody ref={bodyB} position={[0, 4, 0]}>
<mesh><boxGeometry /><meshStandardMaterial color="blue" /></mesh>
</RigidBody>
</>
)
}连接的物体之间不会发生相对移动:
tsx
import { useFixedJoint, RapierRigidBody } from '@react-three/rapier'
function FixedJointExample() {
const bodyA = useRef<RapierRigidBody>(null)
const bodyB = useRef<RapierRigidBody>(null)
useFixedJoint(bodyA, bodyB, [
[0, 0, 0], // 在bodyA局部空间中的位置
[0, 0, 0, 1], // 在bodyA局部空间中的朝向(四元数)
[0, -1, 0], // 在bodyB局部空间中的位置
[0, 0, 0, 1], // 在bodyB局部空间中的朝向
])
return (
<>
<RigidBody ref={bodyA} position={[0, 5, 0]}>
<mesh><boxGeometry /><meshStandardMaterial color="red" /></mesh>
</RigidBody>
<RigidBody ref={bodyB} position={[0, 4, 0]}>
<mesh><boxGeometry /><meshStandardMaterial color="blue" /></mesh>
</RigidBody>
</>
)
}Revolute Joint (Hinge)
旋转关节(铰链)
Rotation around one axis:
tsx
import { useRevoluteJoint } from '@react-three/rapier'
function HingeDoor() {
const frame = useRef<RapierRigidBody>(null)
const door = useRef<RapierRigidBody>(null)
useRevoluteJoint(frame, door, [
[0.5, 0, 0], // Joint position in frame's local space
[-0.5, 0, 0], // Joint position in door's local space
[0, 1, 0], // Rotation axis
])
return (
<>
<RigidBody ref={frame} type="fixed">
<mesh><boxGeometry args={[0.1, 2, 0.1]} /></mesh>
</RigidBody>
<RigidBody ref={door}>
<mesh><boxGeometry args={[1, 2, 0.1]} /></mesh>
</RigidBody>
</>
)
}绕单个轴旋转:
tsx
import { useRevoluteJoint } from '@react-three/rapier'
function HingeDoor() {
const frame = useRef<RapierRigidBody>(null)
const door = useRef<RapierRigidBody>(null)
useRevoluteJoint(frame, door, [
[0.5, 0, 0], // 在frame局部空间中的关节位置
[-0.5, 0, 0], // 在door局部空间中的关节位置
[0, 1, 0], // 旋转轴
])
return (
<>
<RigidBody ref={frame} type="fixed">
<mesh><boxGeometry args={[0.1, 2, 0.1]} /></mesh>
</RigidBody>
<RigidBody ref={door}>
<mesh><boxGeometry args={[1, 2, 0.1]} /></mesh>
</RigidBody>
</>
)
}Spherical Joint (Ball-Socket)
球关节(球窝)
Rotation in all directions:
tsx
import { useSphericalJoint } from '@react-three/rapier'
function BallJoint() {
const bodyA = useRef<RapierRigidBody>(null)
const bodyB = useRef<RapierRigidBody>(null)
useSphericalJoint(bodyA, bodyB, [
[0, -0.5, 0], // Position in bodyA's local space
[0, 0.5, 0], // Position in bodyB's local space
])
return (
<>
<RigidBody ref={bodyA} type="fixed" position={[0, 3, 0]}>
<mesh><sphereGeometry args={[0.2]} /></mesh>
</RigidBody>
<RigidBody ref={bodyB} position={[0, 2, 0]}>
<mesh><boxGeometry /></mesh>
</RigidBody>
</>
)
}可在所有方向上旋转:
tsx
import { useSphericalJoint } from '@react-three/rapier'
function BallJoint() {
const bodyA = useRef<RapierRigidBody>(null)
const bodyB = useRef<RapierRigidBody>(null)
useSphericalJoint(bodyA, bodyB, [
[0, -0.5, 0], // 在bodyA局部空间中的位置
[0, 0.5, 0], // 在bodyB局部空间中的位置
])
return (
<>
<RigidBody ref={bodyA} type="fixed" position={[0, 3, 0]}>
<mesh><sphereGeometry args={[0.2]} /></mesh>
</RigidBody>
<RigidBody ref={bodyB} position={[0, 2, 0]}>
<mesh><boxGeometry /></mesh>
</RigidBody>
</>
)
}Prismatic Joint (Slider)
棱柱关节(滑块)
Translation along one axis:
tsx
import { usePrismaticJoint } from '@react-three/rapier'
function Slider() {
const track = useRef<RapierRigidBody>(null)
const slider = useRef<RapierRigidBody>(null)
usePrismaticJoint(track, slider, [
[0, 0, 0], // Position in track's local space
[0, 0, 0], // Position in slider's local space
[1, 0, 0], // Axis of translation
])
return (
<>
<RigidBody ref={track} type="fixed">
<mesh><boxGeometry args={[5, 0.1, 0.1]} /></mesh>
</RigidBody>
<RigidBody ref={slider}>
<mesh><boxGeometry args={[0.5, 0.5, 0.5]} /></mesh>
</RigidBody>
</>
)
}沿单个轴平移:
tsx
import { usePrismaticJoint } from '@react-three/rapier'
function Slider() {
const track = useRef<RapierRigidBody>(null)
const slider = useRef<RapierRigidBody>(null)
usePrismaticJoint(track, slider, [
[0, 0, 0], // 在track局部空间中的位置
[0, 0, 0], // 在slider局部空间中的位置
[1, 0, 0], // 平移轴
])
return (
<>
<RigidBody ref={track} type="fixed">
<mesh><boxGeometry args={[5, 0.1, 0.1]} /></mesh>
</RigidBody>
<RigidBody ref={slider}>
<mesh><boxGeometry args={[0.5, 0.5, 0.5]} /></mesh>
</RigidBody>
</>
)
}Spring Joint
弹簧关节
Elastic connection:
tsx
import { useSpringJoint } from '@react-three/rapier'
function SpringConnection() {
const anchor = useRef<RapierRigidBody>(null)
const ball = useRef<RapierRigidBody>(null)
useSpringJoint(anchor, ball, [
[0, 0, 0], // Position in anchor's local space
[0, 0, 0], // Position in ball's local space
2, // Rest length
1000, // Stiffness
10, // Damping
])
return (
<>
<RigidBody ref={anchor} type="fixed" position={[0, 5, 0]}>
<mesh><sphereGeometry args={[0.1]} /></mesh>
</RigidBody>
<RigidBody ref={ball} position={[0, 3, 0]}>
<mesh><sphereGeometry args={[0.5]} /></mesh>
</RigidBody>
</>
)
}弹性连接:
tsx
import { useSpringJoint } from '@react-three/rapier'
function SpringConnection() {
const anchor = useRef<RapierRigidBody>(null)
const ball = useRef<RapierRigidBody>(null)
useSpringJoint(anchor, ball, [
[0, 0, 0], // 在anchor局部空间中的位置
[0, 0, 0], // 在ball局部空间中的位置
2, // 静止长度
1000, // 刚度
10, // 阻尼
])
return (
<>
<RigidBody ref={anchor} type="fixed" position={[0, 5, 0]}>
<mesh><sphereGeometry args={[0.1]} /></mesh>
</RigidBody>
<RigidBody ref={ball} position={[0, 3, 0]}>
<mesh><sphereGeometry args={[0.5]} /></mesh>
</RigidBody>
</>
)
}Rope Joint
绳索关节
Maximum distance constraint:
tsx
import { useRopeJoint } from '@react-three/rapier'
function RopeConnection() {
const anchor = useRef<RapierRigidBody>(null)
const weight = useRef<RapierRigidBody>(null)
useRopeJoint(anchor, weight, [
[0, 0, 0], // Position in anchor's local space
[0, 0, 0], // Position in weight's local space
3, // Max distance (rope length)
])
return (
<>
<RigidBody ref={anchor} type="fixed" position={[0, 5, 0]}>
<mesh><sphereGeometry args={[0.1]} /></mesh>
</RigidBody>
<RigidBody ref={weight} position={[0, 2, 0]}>
<mesh><sphereGeometry args={[0.5]} /></mesh>
</RigidBody>
</>
)
}最大距离约束:
tsx
import { useRopeJoint } from '@react-three/rapier'
function RopeConnection() {
const anchor = useRef<RapierRigidBody>(null)
const weight = useRef<RapierRigidBody>(null)
useRopeJoint(anchor, weight, [
[0, 0, 0], // 在anchor局部空间中的位置
[0, 0, 0], // 在weight局部空间中的位置
3, // 最大距离(绳索长度)
])
return (
<>
<RigidBody ref={anchor} type="fixed" position={[0, 5, 0]}>
<mesh><sphereGeometry args={[0.1]} /></mesh>
</RigidBody>
<RigidBody ref={weight} position={[0, 2, 0]}>
<mesh><sphereGeometry args={[0.5]} /></mesh>
</RigidBody>
</>
)
}Motorized Joints
带电机的关节
tsx
import { useRevoluteJoint } from '@react-three/rapier'
import { useFrame } from '@react-three/fiber'
function MotorizedWheel({ bodyA, bodyB }) {
const joint = useRevoluteJoint(bodyA, bodyB, [
[0, 0, 0],
[0, 0, 0],
[0, 0, 1], // Rotation axis
])
useFrame(() => {
if (joint.current) {
// Configure motor: velocity, damping
joint.current.configureMotorVelocity(10, 2)
}
})
return null
}tsx
import { useRevoluteJoint } from '@react-three/rapier'
import { useFrame } from '@react-three/fiber'
function MotorizedWheel({ bodyA, bodyB }) {
const joint = useRevoluteJoint(bodyA, bodyB, [
[0, 0, 0],
[0, 0, 0],
[0, 0, 1], // 旋转轴
])
useFrame(() => {
if (joint.current) {
// 配置电机:速度、阻尼
joint.current.configureMotorVelocity(10, 2)
}
})
return null
}Instanced Physics
实例化物理
Efficient physics for many identical objects:
tsx
import { InstancedRigidBodies, RapierRigidBody } from '@react-three/rapier'
import { useRef, useMemo } from 'react'
function InstancedBalls() {
const COUNT = 100
const rigidBodies = useRef<RapierRigidBody[]>(null)
const instances = useMemo(() => {
return Array.from({ length: COUNT }, (_, i) => ({
key: `ball-${i}`,
position: [
(Math.random() - 0.5) * 10,
Math.random() * 10 + 5,
(Math.random() - 0.5) * 10,
] as [number, number, number],
rotation: [0, 0, 0] as [number, number, number],
}))
}, [])
return (
<InstancedRigidBodies
ref={rigidBodies}
instances={instances}
colliders="ball"
>
<instancedMesh args={[undefined, undefined, COUNT]}>
<sphereGeometry args={[0.5]} />
<meshStandardMaterial color="orange" />
</instancedMesh>
</InstancedRigidBodies>
)
}高效处理大量相同物体的物理模拟:
tsx
import { InstancedRigidBodies, RapierRigidBody } from '@react-three/rapier'
import { useRef, useMemo } from 'react'
function InstancedBalls() {
const COUNT = 100
const rigidBodies = useRef<RapierRigidBody[]>(null)
const instances = useMemo(() => {
return Array.from({ length: COUNT }, (_, i) => ({
key: `ball-${i}`,
position: [
(Math.random() - 0.5) * 10,
Math.random() * 10 + 5,
(Math.random() - 0.5) * 10,
] as [number, number, number],
rotation: [0, 0, 0] as [number, number, number],
}))
}, [])
return (
<InstancedRigidBodies
ref={rigidBodies}
instances={instances}
colliders="ball"
>
<instancedMesh args={[undefined, undefined, COUNT]}>
<sphereGeometry args={[0.5]} />
<meshStandardMaterial color="orange" />
</instancedMesh>
</InstancedRigidBodies>
)
}Accessing the World
访问物理世界
tsx
import { useRapier } from '@react-three/rapier'
import { useEffect } from 'react'
function WorldAccess() {
const { world, rapier } = useRapier()
useEffect(() => {
// Change gravity
world.setGravity({ x: 0, y: -20, z: 0 })
// Iterate over bodies
world.bodies.forEach((body) => {
console.log(body.translation())
})
}, [world])
return null
}tsx
import { useRapier } from '@react-three/rapier'
import { useEffect } from 'react'
function WorldAccess() {
const { world, rapier } = useRapier()
useEffect(() => {
// 修改重力
world.setGravity({ x: 0, y: -20, z: 0 })
// 遍历所有物体
world.bodies.forEach((body) => {
console.log(body.translation())
})
}, [world])
return null
}Manual Stepping
手动步进
tsx
function ManualStep() {
const { step } = useRapier()
const advancePhysics = () => {
step(1 / 60) // Advance by one frame
}
return <button onClick={advancePhysics}>Step</button>
}tsx
function ManualStep() {
const { step } = useRapier()
const advancePhysics = () => {
step(1 / 60) // 前进一帧
}
return <button onClick={advancePhysics}>步进</button>
}World Snapshots
世界快照
Save and restore physics state:
tsx
function SnapshotSystem() {
const { world, setWorld, rapier } = useRapier()
const snapshot = useRef<Uint8Array>()
const saveState = () => {
snapshot.current = world.takeSnapshot()
}
const loadState = () => {
if (snapshot.current) {
setWorld(rapier.World.restoreSnapshot(snapshot.current))
}
}
return (
<>
<button onClick={saveState}>Save</button>
<button onClick={loadState}>Load</button>
</>
)
}保存和恢复物理状态:
tsx
function SnapshotSystem() {
const { world, setWorld, rapier } = useRapier()
const snapshot = useRef<Uint8Array>()
const saveState = () => {
snapshot.current = world.takeSnapshot()
}
const loadState = () => {
if (snapshot.current) {
setWorld(rapier.World.restoreSnapshot(snapshot.current))
}
}
return (
<>
<button onClick={saveState}>保存</button>
<button onClick={loadState}>加载</button>
</>
)
}Attractors
吸引器
From :
@react-three/rapier-addonstsx
import { Attractor } from '@react-three/rapier-addons'
// Attract nearby bodies
<Attractor
position={[0, 0, 0]}
range={10}
strength={5}
type="linear" // "static" | "linear" | "newtonian"
/>
// Repel bodies
<Attractor range={10} strength={-5} position={[5, 0, 0]} />
// Selective attraction (only affect certain groups)
<Attractor
range={10}
strength={10}
collisionGroups={interactionGroups(0, [2, 3])}
/>来自:
@react-three/rapier-addonstsx
import { Attractor } from '@react-three/rapier-addons'
// 吸引附近的物体
<Attractor
position={[0, 0, 0]}
range={10}
strength={5}
type="linear" // "static" | "linear" | "newtonian"
/>
// 排斥物体
<Attractor range={10} strength={-5} position={[5, 0, 0]} />
// 选择性吸引(仅影响指定组)
<Attractor
range={10}
strength={10}
collisionGroups={interactionGroups(0, [2, 3])}
/>Debug Visualization
调试可视化
tsx
<Physics debug>
{/* All colliders shown as wireframes */}
</Physics>
// Conditional debug
<Physics debug={process.env.NODE_ENV === 'development'}>
...
</Physics>tsx
<Physics debug>
{/* 所有碰撞器将以线框形式显示 */}
</Physics>
// 条件调试
<Physics debug={process.env.NODE_ENV === 'development'}>
...
</Physics>Performance Tips
性能优化建议
- Use appropriate collider types: and
cuboidare fastestball - Avoid for dynamic bodies: Use
trimeshinsteadhull - Enable sleeping: Bodies at rest stop computing
- Use collision groups: Reduce collision checks
- Limit active bodies: Too many dynamic bodies hurts performance
- Use instanced bodies: For many identical objects
- Fixed timestep: More stable than variable
tsx
// Performance-optimized setup
<Physics
timeStep={1/60}
colliders="cuboid"
gravity={[0, -9.81, 0]}
>
{/* Use collision groups to limit checks */}
<RigidBody collisionGroups={interactionGroups(0, [0, 1])}>
...
</RigidBody>
</Physics>- 选择合适的碰撞器类型:和
cuboid类型性能最优ball - 避免为动态物体使用:改用
trimesh类型hull - 启用休眠功能:静止的物体将停止计算
- 使用碰撞组:减少不必要的碰撞检测
- 限制活动物体数量:过多动态物体会影响性能
- 使用实例化物体:适用于大量相同的物体
- 使用固定时间步长:比可变时间步长更稳定
tsx
// 性能优化后的配置
<Physics
timeStep={1/60}
colliders="cuboid"
gravity={[0, -9.81, 0]}
>
{/* 使用碰撞组减少检测次数 */}
<RigidBody collisionGroups={interactionGroups(0, [0, 1])}>
...
</RigidBody>
</Physics>See Also
相关链接
- - R3F basics and hooks
r3f-fundamentals - - User input and controls
r3f-interaction - - Combining physics with animation
r3f-animation
- - R3F基础和钩子
r3f-fundamentals - - 用户输入和控制
r3f-interaction - - 物理与动画的结合
r3f-animation