Skip to content

वेब गेम्स के लिए गेम फिजिक्स

फिजिक्स सिमुलेशन गेम्स में जान डाल देता है, गिरती हुई चीजें, उछलती गेंदें, ragdolls, गाड़ियां, और टूटने वाले environments. यह गाइड वेब गेम्स के लिए उपलब्ध प्रमुख फिजिक्स लाइब्रेरियों को कवर करती है, असली कोड उदाहरणों और ईमानदार फायदे/नुकसान के साथ।

एक नजर में

EngineDimensionPerformanceSizeDifficultySoft BodiesVehiclesCCDDeterminism
Rapier2D/3D⭐⭐⭐⭐⭐1.4 MBमध्यम
Cannon-es3D⭐⭐⭐150 KBआसान
Ammo.js3D⭐⭐⭐⭐⭐1-2 MBकठिन⚠️
Jolt3D⭐⭐⭐⭐⭐कठिन
Oimo.js3D⭐⭐⭐100 KBआसान
Matter.js2D⭐⭐⭐80 KBआसान⚠️
Planck.js2D⭐⭐⭐⭐120 KBमध्यम⚠️
p2-es2D⭐⭐⭐100 KBमध्यम
Box2D WASM2D⭐⭐⭐⭐⭐300 KBकठिन

लेजेंड:

  • CCD = Continuous Collision Detection (तेज चीजों को दीवारों के आर-पार निकलने से रोकता है)
  • ⚠️ Determinism = fixed timestep के साथ संभव है, पर cross-platform गारंटीड नहीं

झटपट सिफारिशें

आपकी स्थितिसबसे अच्छा विकल्प
Production 3D gameRapier — सबसे अच्छी परफॉर्मेंस, आधुनिक API, सक्रिय डेवलपमेंट
सीखना / prototypeCannon-es (3D) या Matter.js (2D) — सरल API, आसान डीबगिंग
Vehicle physicsRapier या Ammo.js — दोनों में ray-cast vehicle controllers हैं
Soft bodies, cloth, ropesAmmo.js या Jolt (JoltPhysics.js) — दोनों cloth और soft bodies सपोर्ट करते हैं; Jolt का WASM port ज्यादा सक्रिय रूप से मेंटेन होता है
सटीक 2D platformerPlanck.js — Box2D algorithms, fixed timestep के साथ deterministic
अधिकतम 2D performanceBox2D WASM — browser में native speed
सबसे छोटा bundle sizeOimo.js (3D) या Matter.js (2D)

विस्तृत तुलना

3D Physics Engines

EngineLanguageSizePerformanceकिसके लिए सबसे अच्छा
RapierRust/WASM~1.4 MBबेहतरीनProduction games, जटिल simulations
Cannon-esJavaScript~150 KBअच्छाPrototypes, सरल games
Ammo.jsC++/WASM~1-2 MBबेहतरीनAAA features, soft bodies, vehicles
JoltC++/WASMWASMबेहतरीनसक्रिय मेंटेनेंस के साथ AAA features
Oimo.jsJavaScript~100 KBअच्छासरल games, झटपट prototypes

2D Physics Engines

EngineLanguageSizePerformanceकिसके लिए सबसे अच्छा
Matter.jsJavaScript~80 KBअच्छाVisual games, prototypes
Planck.jsJavaScript~120 KBअच्छाPlatformers, सटीक physics
p2-esJavaScript~100 KBअच्छाConstraints, mechanisms
Box2D WASMC++/WASM~300 KBबेहतरीनज्यादा body counts

3D Physics Engines

Rapier — आधुनिक विकल्प

Rapier एक Rust फिजिक्स इंजन है जिसमें JavaScript/WASM bindings हैं। 2025-2026 में वेब गेम्स के लिए यह सबसे परफॉर्मेंट विकल्प है, अपने 2024 वर्जन की तुलना में 2-5x speed सुधार के साथ।

Install:

bash
npm install @dimforge/rapier3d
# Or with SIMD (faster, requires modern browsers):
npm install @dimforge/rapier3d-simd

बेसिक सेटअप:

js
import RAPIER from '@dimforge/rapier3d'

// Initialize (async required for WASM)
await RAPIER.init()

// Create world with gravity
const gravity = { x: 0, y: -9.81, z: 0 }
const world = new RAPIER.World(gravity)

// Create ground (static body)
const groundDesc = RAPIER.RigidBodyDesc.fixed()
const groundBody = world.createRigidBody(groundDesc)
const groundCollider = RAPIER.ColliderDesc.cuboid(50, 0.1, 50)
world.createCollider(groundCollider, groundBody)

// Create falling box (dynamic body)
const boxDesc = RAPIER.RigidBodyDesc.dynamic()
  .setTranslation(0, 10, 0)
const boxBody = world.createRigidBody(boxDesc)
const boxCollider = RAPIER.ColliderDesc.cuboid(0.5, 0.5, 0.5)
  .setDensity(1.0)
  .setRestitution(0.5)
world.createCollider(boxCollider, boxBody)

Game loop integration:

js
const FIXED_TIMESTEP = 1 / 60

function physicsStep() {
  world.step()
}

function gameLoop() {
  physicsStep()
  
  // Sync render objects with physics
  const position = boxBody.translation()
  const rotation = boxBody.rotation()
  
  // Update your Three.js/Babylon mesh
  mesh.position.set(position.x, position.y, position.z)
  mesh.quaternion.set(rotation.x, rotation.y, rotation.z, rotation.w)
  
  requestAnimationFrame(gameLoop)
}

Collision detection:

js
// Event-based collision detection
world.contactPairsWith(boxCollider, (otherCollider) => {
  console.log('Box is touching:', otherCollider)
})

// Ray casting
const ray = new RAPIER.Ray({ x: 0, y: 10, z: 0 }, { x: 0, y: -1, z: 0 })
const hit = world.castRay(ray, 100, true)
if (hit) {
  const hitPoint = ray.pointAt(hit.timeOfImpact)
  console.log('Hit at:', hitPoint)
}

Joints:

js
// Create a hinge joint (door)
const jointData = RAPIER.JointData.revolute(
  { x: 0, y: 0, z: 0 },  // Anchor on body1
  { x: -1, y: 0, z: 0 }, // Anchor on body2
  { x: 0, y: 1, z: 0 }   // Rotation axis
)
world.createImpulseJoint(jointData, body1, body2, true)

Vehicle controller:

js
// Create chassis rigid body
const chassisDesc = RAPIER.RigidBodyDesc.dynamic()
  .setTranslation(0, 2, 0)
const chassis = world.createRigidBody(chassisDesc)
const chassisCollider = RAPIER.ColliderDesc.cuboid(1, 0.5, 2)
  .setDensity(100)
world.createCollider(chassisCollider, chassis)

// Create vehicle controller
const vehicle = world.createVehicleController(chassis)

// Add wheels (front-left, front-right, rear-left, rear-right)
const suspensionRestLength = 0.3
const wheelRadius = 0.4

// Front wheels (steering)
vehicle.addWheel(
  { x: -0.8, y: 0, z: 1.2 },   // Connection point
  { x: 0, y: -1, z: 0 },       // Suspension direction
  { x: -1, y: 0, z: 0 },       // Axle direction
  suspensionRestLength,
  wheelRadius
)
vehicle.addWheel(
  { x: 0.8, y: 0, z: 1.2 },
  { x: 0, y: -1, z: 0 },
  { x: -1, y: 0, z: 0 },
  suspensionRestLength,
  wheelRadius
)

// Rear wheels (drive)
vehicle.addWheel(
  { x: -0.8, y: 0, z: -1.2 },
  { x: 0, y: -1, z: 0 },
  { x: -1, y: 0, z: 0 },
  suspensionRestLength,
  wheelRadius
)
vehicle.addWheel(
  { x: 0.8, y: 0, z: -1.2 },
  { x: 0, y: -1, z: 0 },
  { x: -1, y: 0, z: 0 },
  suspensionRestLength,
  wheelRadius
)

// Configure suspension for all wheels
for (let i = 0; i < 4; i++) {
  vehicle.setWheelSuspensionStiffness(i, 30)
  vehicle.setWheelSuspensionCompression(i, 4.4)
  vehicle.setWheelSuspensionRelaxation(i, 2.3)
  vehicle.setWheelMaxSuspensionTravel(i, 0.5)
  vehicle.setWheelFrictionSlip(i, 2)
}

// In game loop
function updateVehicle(steering, engineForce, brakeForce) {
  // Steering (front wheels only)
  vehicle.setWheelSteering(0, steering)
  vehicle.setWheelSteering(1, steering)
  
  // Engine (rear wheels)
  vehicle.setWheelEngineForce(2, engineForce)
  vehicle.setWheelEngineForce(3, engineForce)
  
  // Brakes (all wheels)
  for (let i = 0; i < 4; i++) {
    vehicle.setWheelBrake(i, brakeForce)
  }
  
  // Update vehicle physics
  vehicle.updateVehicle(world.timestep)
}

फायदे:

  • सबसे अच्छी परफॉर्मेंस (WASM + SIMD)
  • बेहतरीन collision detection सटीकता
  • Cross-platform determinism (same inputs = same outputs)
  • सक्रिय डेवलपमेंट, आधुनिक API
  • Continuous collision detection (कोई tunneling नहीं)
  • Character controller और vehicle controller बिल्ट-इन

नुकसान:

  • बड़ा bundle size (~1.4 MB)
  • Async initialization जरूरी
  • शुद्ध JS विकल्पों से ज्यादा जटिल API
  • WASM debugging थोड़ा मुश्किल हो सकता है

Cannon-es — सरल और प्रभावी

Cannon-es, Cannon.js का मेंटेन किया गया fork है। शुद्ध JavaScript, समझने में आसान, सीखने और prototypes के लिए शानदार।

Install:

bash
npm install cannon-es

बेसिक सेटअप:

js
import * as CANNON from 'cannon-es'

// Create world
const world = new CANNON.World({
  gravity: new CANNON.Vec3(0, -9.81, 0)
})

// Ground
const groundBody = new CANNON.Body({
  type: CANNON.Body.STATIC,
  shape: new CANNON.Plane()
})
groundBody.quaternion.setFromEuler(-Math.PI / 2, 0, 0)
world.addBody(groundBody)

// Falling sphere
const sphereBody = new CANNON.Body({
  mass: 1,
  shape: new CANNON.Sphere(0.5),
  position: new CANNON.Vec3(0, 10, 0)
})
sphereBody.linearDamping = 0.1
world.addBody(sphereBody)

Game loop:

js
const TIMESTEP = 1 / 60

function animate() {
  world.step(TIMESTEP)
  
  // Sync with Three.js
  mesh.position.copy(sphereBody.position)
  mesh.quaternion.copy(sphereBody.quaternion)
  
  requestAnimationFrame(animate)
}

Collision events:

js
sphereBody.addEventListener('collide', (event) => {
  const contact = event.contact
  const impactVelocity = contact.getImpactVelocityAlongNormal()
  
  if (Math.abs(impactVelocity) > 5) {
    console.log('Hard impact!')
  }
})

Constraints:

js
// Distance constraint (rope-like)
const constraint = new CANNON.DistanceConstraint(
  bodyA, bodyB, 
  2 // distance
)
world.addConstraint(constraint)

// Hinge constraint
const hinge = new CANNON.HingeConstraint(bodyA, bodyB, {
  pivotA: new CANNON.Vec3(1, 0, 0),
  axisA: new CANNON.Vec3(0, 1, 0),
  pivotB: new CANNON.Vec3(-1, 0, 0),
  axisB: new CANNON.Vec3(0, 1, 0)
})
world.addConstraint(hinge)

फायदे:

  • शुद्ध JavaScript, कोई WASM जटिलता नहीं
  • छोटा bundle size (~150 KB)
  • सीखने और डीबग करने में आसान
  • हर जगह काम करता है
  • शानदार Three.js integration
  • अच्छा documentation

नुकसान:

  • WASM विकल्पों से धीमा
  • ज्यादा bodies (>100) के साथ जूझता है
  • सीमित trimesh सपोर्ट
  • कोई बिल्ट-इन CCD नहीं (tunneling संभव)
  • कम सक्रिय डेवलपमेंट

Ammo.js — पूरी Bullet Physics ताकत

Ammo.js, WebAssembly में compile किया गया Bullet Physics इंजन है। अधिकतम features, जिसमें soft bodies, vehicles, और advanced constraints शामिल हैं।

Install:

bash
npm install ammo.js
# Or use from CDN

बेसिक सेटअप:

js
import Ammo from 'ammo.js'

let physicsWorld

async function initPhysics() {
  await Ammo()
  
  const collisionConfig = new Ammo.btDefaultCollisionConfiguration()
  const dispatcher = new Ammo.btCollisionDispatcher(collisionConfig)
  const broadphase = new Ammo.btDbvtBroadphase()
  const solver = new Ammo.btSequentialImpulseConstraintSolver()
  
  physicsWorld = new Ammo.btDiscreteDynamicsWorld(
    dispatcher, broadphase, solver, collisionConfig
  )
  physicsWorld.setGravity(new Ammo.btVector3(0, -9.81, 0))
}

function createBox(mass, width, height, depth, x, y, z) {
  const transform = new Ammo.btTransform()
  transform.setIdentity()
  transform.setOrigin(new Ammo.btVector3(x, y, z))
  
  const motionState = new Ammo.btDefaultMotionState(transform)
  const shape = new Ammo.btBoxShape(
    new Ammo.btVector3(width / 2, height / 2, depth / 2)
  )
  
  const localInertia = new Ammo.btVector3(0, 0, 0)
  if (mass > 0) {
    shape.calculateLocalInertia(mass, localInertia)
  }
  
  const rbInfo = new Ammo.btRigidBodyConstructionInfo(
    mass, motionState, shape, localInertia
  )
  const body = new Ammo.btRigidBody(rbInfo)
  
  physicsWorld.addRigidBody(body)
  return body
}

Soft bodies (cloth):

js
function createCloth(width, height, segments) {
  const softBodyHelpers = new Ammo.btSoftBodyHelpers()
  
  const corner00 = new Ammo.btVector3(-width/2, height, 0)
  const corner10 = new Ammo.btVector3(width/2, height, 0)
  const corner01 = new Ammo.btVector3(-width/2, 0, 0)
  const corner11 = new Ammo.btVector3(width/2, 0, 0)
  
  const softBody = softBodyHelpers.CreatePatch(
    physicsWorld.getWorldInfo(),
    corner00, corner10, corner01, corner11,
    segments, segments,
    0, true
  )
  
  const sbConfig = softBody.get_m_cfg()
  sbConfig.set_viterations(10)
  sbConfig.set_piterations(10)
  
  softBody.setTotalMass(0.9, false)
  Ammo.castObject(softBody, Ammo.btCollisionObject)
    .getCollisionShape().setMargin(0.05)
  
  physicsWorld.addSoftBody(softBody, 1, -1)
  return softBody
}

Vehicle physics:

js
function createVehicle(chassisBody) {
  const tuning = new Ammo.btVehicleTuning()
  const rayCaster = new Ammo.btDefaultVehicleRaycaster(physicsWorld)
  const vehicle = new Ammo.btRaycastVehicle(tuning, chassisBody, rayCaster)
  
  vehicle.setCoordinateSystem(0, 1, 2)
  physicsWorld.addAction(vehicle)
  
  // Add wheels
  const wheelRadius = 0.4
  const wheelWidth = 0.3
  const suspensionRestLength = 0.3
  
  const wheelDirectionCS = new Ammo.btVector3(0, -1, 0)
  const wheelAxleCS = new Ammo.btVector3(-1, 0, 0)
  
  function addWheel(isFront, pos) {
    const wheelInfo = vehicle.addWheel(
      pos, wheelDirectionCS, wheelAxleCS,
      suspensionRestLength, wheelRadius, tuning, isFront
    )
    wheelInfo.set_m_suspensionStiffness(20)
    wheelInfo.set_m_wheelsDampingRelaxation(2.3)
    wheelInfo.set_m_wheelsDampingCompression(4.4)
    wheelInfo.set_m_frictionSlip(1000)
    wheelInfo.set_m_rollInfluence(0.1)
  }
  
  addWheel(true, new Ammo.btVector3(1, 0, 1.5))   // Front left
  addWheel(true, new Ammo.btVector3(-1, 0, 1.5))  // Front right
  addWheel(false, new Ammo.btVector3(1, 0, -1.5)) // Rear left
  addWheel(false, new Ammo.btVector3(-1, 0, -1.5))// Rear right
  
  return vehicle
}

फायदे:

  • सबसे पूरा feature set
  • Soft bodies, cloth, ropes
  • Advanced vehicle physics
  • खूब आजमाया हुआ (कई AAA games में इस्तेमाल)
  • खूब configurable

नुकसान:

  • जटिल, लंबा-चौड़ा API
  • बड़ा bundle size
  • Memory management जरूरी (objects को destroy करना)
  • सीखने की कठिन प्रक्रिया
  • बिखरा हुआ documentation
  • Cross-platform deterministic नहीं (केवल same-device, ध्यान से config करने पर)

Jolt — आधुनिक AAA-स्तर की 3D physics

Jolt Physics, Horizon Forbidden West जैसे games के पीछे का इंजन है, और JoltPhysics.js इसे WASM port के रूप में browser में लाता है। यह Rapier और Ammo.js के बीच एक मजबूत बीच का रास्ता है: Rapier से ज्यादा features (soft bodies, cloth, एक wheeled vehicle controller) के साथ सक्रिय मेंटेनेंस जो Ammo.js में नहीं है। npm package jolt-physics है, और React Three Fiber (@react-three/jolt) तथा Babylon.js के लिए तैयार integrations मौजूद हैं।

bash
npm install jolt-physics

Rapier की तरह, यह async-initialized WASM है जिसका API लंबा-चौड़ा-पर-पूरा है और C++ interface की नकल करता है। Jolt को तब चुनें जब आपको Bullet-class features (soft bodies, vehicles) चाहिए पर Ammo.js के बजाय एक मेंटेन किया गया, आधुनिक codebase चाहिए।


Oimo.js — हल्का और तेज

Oimo.js एक हल्का 3D फिजिक्स इंजन है। उन सरल games के लिए शानदार जहां आपको advanced features की जरूरत नहीं।

Install:

bash
npm install oimo

बेसिक सेटअप:

js
import * as OIMO from 'oimo'

const world = new OIMO.World({
  timestep: 1/60,
  iterations: 8,
  broadphase: 2, // 1: brute, 2: sweep & prune, 3: volume tree
  worldscale: 1,
  random: true,
  gravity: [0, -9.8, 0]
})

// Create ground
world.add({
  type: 'box',
  size: [100, 1, 100],
  pos: [0, -0.5, 0],
  move: false
})

// Create falling sphere
const sphere = world.add({
  type: 'sphere',
  size: [1],
  pos: [0, 10, 0],
  move: true,
  density: 1,
  friction: 0.4,
  restitution: 0.2
})

Game loop:

js
function animate() {
  world.step()
  
  // Get position/rotation
  const pos = sphere.getPosition()
  const rot = sphere.getQuaternion()
  
  mesh.position.set(pos.x, pos.y, pos.z)
  mesh.quaternion.set(rot.x, rot.y, rot.z, rot.w)
  
  requestAnimationFrame(animate)
}

फायदे:

  • बहुत छोटा (~100 KB)
  • सरल API
  • बेसिक scenes के लिए अच्छी परफॉर्मेंस
  • बिल्ट-इन Babylon.js सपोर्ट

नुकसान:

  • सीमित shapes (केवल primitives)
  • कोई soft bodies नहीं
  • कम documentation
  • कम सक्रिय डेवलपमेंट
  • सीमित joint विकल्प

2D Physics Engines

Matter.js — सुंदर और सहज

Matter.js एक feature-rich 2D फिजिक्स इंजन है जिसमें बेहतरीन rendering और debug tools हैं।

Install:

bash
npm install matter-js

बेसिक सेटअप:

js
import Matter from 'matter-js'

const { Engine, Render, World, Bodies, Runner } = Matter

// Create engine
const engine = Engine.create()

// Create renderer (optional, great for debugging)
const render = Render.create({
  element: document.body,
  engine: engine,
  options: {
    width: 800,
    height: 600,
    wireframes: false
  }
})

// Create bodies
const ground = Bodies.rectangle(400, 580, 810, 60, { 
  isStatic: true 
})

const box = Bodies.rectangle(400, 200, 80, 80, {
  restitution: 0.8,
  friction: 0.5
})

const circle = Bodies.circle(300, 100, 40, {
  restitution: 0.9
})

// Add to world
World.add(engine.world, [ground, box, circle])

// Run
Render.run(render)
Runner.run(Runner.create(), engine)

Collision events:

js
Matter.Events.on(engine, 'collisionStart', (event) => {
  event.pairs.forEach(pair => {
    console.log('Collision between:', pair.bodyA.label, pair.bodyB.label)
  })
})

Constraints:

js
// Pin constraint
const pin = Matter.Constraint.create({
  pointA: { x: 400, y: 100 },
  bodyB: box,
  stiffness: 0.9
})

// Spring between bodies
const spring = Matter.Constraint.create({
  bodyA: box,
  bodyB: circle,
  stiffness: 0.01,
  length: 100
})

World.add(engine.world, [pin, spring])

Mouse interaction:

js
const mouse = Matter.Mouse.create(render.canvas)
const mouseConstraint = Matter.MouseConstraint.create(engine, {
  mouse: mouse,
  constraint: {
    stiffness: 0.2,
    render: { visible: false }
  }
})
World.add(engine.world, mouseConstraint)

फायदे:

  • सुंदर default rendering
  • सीखने/prototyping के लिए शानदार
  • सहज API
  • अच्छा documentation
  • सक्रिय community
  • डिफॉल्ट रूप से deterministic — Matter.Runner अब एक fixed deterministic timestep इस्तेमाल करता है (non-fixed timestep को v0.20.0 में हटा दिया गया था)

नुकसान:

  • कोई CCD नहीं (तेज चीजें tunnel कर सकती हैं — इसे कम करने के लिए substepping इस्तेमाल करें)
  • ज्यादा bodies के साथ परफॉर्मेंस की दिक्कतें
  • कोई WASM विकल्प नहीं
  • जटिल simulations के लिए सीमित सटीकता

Planck.js — JavaScript के लिए Box2D

Planck.js, JavaScript में Box2D का पूरा rewrite है। खूब आजमाया हुआ physics, fixed timestep इस्तेमाल करने पर deterministic.

Install:

bash
npm install planck

बेसिक सेटअप:

js
import { World, Vec2, Box, Circle, Edge } from 'planck'

// Create world
const world = new World({
  gravity: Vec2(0, -10)
})

// Create ground
const ground = world.createBody()
ground.createFixture({
  shape: Edge(Vec2(-40, 0), Vec2(40, 0))
})

// Create dynamic box
const box = world.createBody({
  type: 'dynamic',
  position: Vec2(0, 10)
})
box.createFixture({
  shape: Box(1, 1),
  density: 1,
  friction: 0.3,
  restitution: 0.5
})

Fixed timestep game loop:

js
const TIMESTEP = 1 / 60
const VELOCITY_ITERATIONS = 8
const POSITION_ITERATIONS = 3

function gameLoop() {
  world.step(TIMESTEP, VELOCITY_ITERATIONS, POSITION_ITERATIONS)
  
  // Iterate all bodies
  for (let body = world.getBodyList(); body; body = body.getNext()) {
    const pos = body.getPosition()
    const angle = body.getAngle()
    // Update your sprites...
  }
  
  requestAnimationFrame(gameLoop)
}

Collision callbacks:

js
world.on('begin-contact', (contact) => {
  const fixtureA = contact.getFixtureA()
  const fixtureB = contact.getFixtureB()
  console.log('Contact started')
})

world.on('end-contact', (contact) => {
  console.log('Contact ended')
})

world.on('pre-solve', (contact, oldManifold) => {
  // Can disable contact here
  // contact.setEnabled(false)
})

Joints:

js
import { RevoluteJoint, DistanceJoint, PrismaticJoint } from 'planck'

// Revolute joint (hinge)
const joint = world.createJoint(RevoluteJoint({
  bodyA: ground,
  bodyB: box,
  localAnchorA: Vec2(0, 5),
  localAnchorB: Vec2(-1, 0),
  enableMotor: true,
  maxMotorTorque: 1000,
  motorSpeed: 2
}))

// Distance joint (spring)
world.createJoint(DistanceJoint({
  bodyA: boxA,
  bodyB: boxB,
  localAnchorA: Vec2(0, 0),
  localAnchorB: Vec2(0, 0),
  length: 5,
  stiffness: 10,
  damping: 0.5
}))

फायदे:

  • fixed timestep के साथ deterministic
  • अच्छी तरह documented (Box2D docs लागू होते हैं)
  • अच्छी परफॉर्मेंस
  • TypeScript सपोर्ट
  • छोटा bundle size

नुकसान:

  • CCD joints को handle नहीं करता, इसलिए joints से जुड़ी तेज चीजें खिंच सकती हैं
  • Matter.js से सीखने की ज्यादा कठिन प्रक्रिया
  • कोई बिल्ट-इन renderer नहीं
  • Box2D की खासियतें (unit scale मायने रखता है)

p2-es — लचीली 2D physics

p2-es, p2.js का मेंटेन किया गया fork है। उन games के लिए शानदार जिन्हें जटिल constraints और mechanisms चाहिए।

Install:

bash
npm install p2-es

बेसिक सेटअप:

js
import * as p2 from 'p2-es'

const world = new p2.World({
  gravity: [0, -9.81]
})

// Ground
const groundBody = new p2.Body({
  mass: 0, // static
  position: [0, -1]
})
groundBody.addShape(new p2.Plane())
world.addBody(groundBody)

// Dynamic circle
const circleBody = new p2.Body({
  mass: 1,
  position: [0, 5]
})
circleBody.addShape(new p2.Circle({ radius: 0.5 }))
world.addBody(circleBody)

Advanced constraints:

js
// Gear constraint
const gear = new p2.GearConstraint(bodyA, bodyB, {
  ratio: 2 // bodyB rotates twice as fast
})
world.addConstraint(gear)

// Prismatic constraint (slider)
const prismatic = new p2.PrismaticConstraint(bodyA, bodyB, {
  localAnchorA: [0, 0],
  localAnchorB: [0, 0],
  localAxisA: [1, 0],
  disableRotationalLock: false
})
world.addConstraint(prismatic)

// Lock constraint (weld)
const lock = new p2.LockConstraint(bodyA, bodyB)
world.addConstraint(lock)

Contact materials:

js
const ice = new p2.Material()
const rubber = new p2.Material()

const iceRubber = new p2.ContactMaterial(ice, rubber, {
  friction: 0.1,
  restitution: 0.9
})
world.addContactMaterial(iceRubber)

// Apply to shapes
iceBody.shapes[0].material = ice
rubberBody.shapes[0].material = rubber

फायदे:

  • समृद्ध constraint system
  • Contact materials
  • mechanisms/machines के लिए अच्छा
  • Sleeping bodies (परफॉर्मेंस)
  • ES modules, tree-shakable

नुकसान:

  • कोई बिल्ट-इन renderer नहीं
  • कुछ shape pairs असमर्थित
  • विकल्पों से कम सक्रिय
  • documentation में कमियां

परफॉर्मेंस के टिप्स

1. fixed timestep इस्तेमाल करें

js
const TIMESTEP = 1 / 60
let accumulator = 0

function gameLoop(deltaTime) {
  accumulator += deltaTime
  
  while (accumulator >= TIMESTEP) {
    world.step(TIMESTEP)
    accumulator -= TIMESTEP
  }
  
  // Interpolate for smooth rendering
  const alpha = accumulator / TIMESTEP
  // lerp(previousState, currentState, alpha)
}

2. निष्क्रिय bodies को sleep कराएं

ज्यादातर engines sleeping सपोर्ट करते हैं। इसे enable करें:

js
// Cannon-es
world.allowSleep = true
body.allowSleep = true
body.sleepSpeedLimit = 0.1
body.sleepTimeLimit = 1

// Rapier
bodyDesc.setCanSleep(true)

3. सरल collision shapes इस्तेमाल करें

js
// GOOD: Simple primitives
const sphere = new CANNON.Sphere(1)
const box = new CANNON.Box(new CANNON.Vec3(1, 1, 1))

// AVOID: Complex trimesh for dynamic bodies
const trimesh = new CANNON.Trimesh(vertices, indices) // Slow!

4. solver iterations कम करें (सावधानी से)

js
// Cannon-es
world.solver.iterations = 5 // Default 10

// Rapier - configured at world creation

5. physics को Web Worker में चलाएं

js
// main.js
const worker = new Worker('physics-worker.js')

worker.postMessage({ type: 'step', deltaTime: 1/60 })
worker.onmessage = (e) => {
  const { positions, rotations } = e.data
  // Update render objects
}

// physics-worker.js
importScripts('cannon-es.js')
const world = new CANNON.World()

onmessage = (e) => {
  if (e.data.type === 'step') {
    world.step(e.data.deltaTime)
    postMessage({
      positions: bodies.map(b => b.position.toArray()),
      rotations: bodies.map(b => b.quaternion.toArray())
    })
  }
}

क्या कब इस्तेमाल करें

स्थितिसिफारिशक्यों
Production 3D gameRapierसबसे अच्छी परफॉर्मेंस, आधुनिक API, vehicles
3D game prototypeCannon-esसरल, debuggable
Soft bodies, cloth, ropesAmmo.js या Joltदोनों cloth/soft bodies सपोर्ट करते हैं; Jolt का WASM port (JoltPhysics.js) ज्यादा सक्रिय रूप से मेंटेन होता है
सरल 3D browser gameOimo.jsछोटा, पर्याप्त
2D game jamMatter.jsतेज सेटअप, बिल्ट-इन rendering
सटीक platformerPlanck.jsFixed timestep determinism, अच्छी तरह documented
जटिल 2D mechanismsp2-esसमृद्ध constraint system
Performance-critical 2DBox2D WASMसबसे तेज 2D विकल्प, असली CCD

Renderers के साथ integration

Three.js + Rapier

js
import * as THREE from 'three'
import RAPIER from '@dimforge/rapier3d'

await RAPIER.init()

const scene = new THREE.Scene()
const world = new RAPIER.World({ x: 0, y: -9.81, z: 0 })

const bodies = new Map() // physics body -> mesh

function createPhysicsBox(x, y, z) {
  // Physics
  const bodyDesc = RAPIER.RigidBodyDesc.dynamic().setTranslation(x, y, z)
  const body = world.createRigidBody(bodyDesc)
  const collider = RAPIER.ColliderDesc.cuboid(0.5, 0.5, 0.5)
  world.createCollider(collider, body)
  
  // Render
  const mesh = new THREE.Mesh(
    new THREE.BoxGeometry(1, 1, 1),
    new THREE.MeshStandardMaterial({ color: 0x00ff00 })
  )
  scene.add(mesh)
  
  bodies.set(body, mesh)
  return body
}

function syncPhysics() {
  bodies.forEach((mesh, body) => {
    const pos = body.translation()
    const rot = body.rotation()
    mesh.position.set(pos.x, pos.y, pos.z)
    mesh.quaternion.set(rot.x, rot.y, rot.z, rot.w)
  })
}

PixiJS + Matter.js

js
import * as PIXI from 'pixi.js'
import Matter from 'matter-js'

const app = new PIXI.Application({ width: 800, height: 600 })
document.body.appendChild(app.view)

const engine = Matter.Engine.create()
const bodies = new Map()

function createPhysicsSprite(texture, x, y, width, height) {
  // Physics
  const body = Matter.Bodies.rectangle(x, y, width, height)
  Matter.World.add(engine.world, body)
  
  // Render
  const sprite = new PIXI.Sprite(texture)
  sprite.anchor.set(0.5)
  app.stage.addChild(sprite)
  
  bodies.set(body, sprite)
  return body
}

app.ticker.add(() => {
  Matter.Engine.update(engine, 1000 / 60)
  
  bodies.forEach((sprite, body) => {
    sprite.position.set(body.position.x, body.position.y)
    sprite.rotation = body.angle
  })
})

आम गलतियां

Scale मायने रखता है

फिजिक्स engines real-world scale (1 unit = 1 meter) के साथ सबसे अच्छा काम करते हैं। pixel coordinates को सीधे इस्तेमाल न करें।

js
// BAD: Using pixel positions
const body = Bodies.circle(400, 300, 50) // 50 pixel radius?

// GOOD: Use a scale factor
const SCALE = 50 // 50 pixels per meter
const body = Bodies.circle(8, 6, 1) // 1 meter radius
// Then multiply by SCALE when rendering

WASM engines के साथ memory leaks

bodies को हटाते समय हमेशा उन्हें साफ करें:

js
// Rapier
world.removeRigidBody(body)

// Ammo.js
physicsWorld.removeRigidBody(body)
Ammo.destroy(body)
Ammo.destroy(shape)
Ammo.destroy(motionState)

Tunneling (चीजों का एक-दूसरे के आर-पार निकलना)

तेज चलती चीजें पतली दीवारों के आर-पार tunnel कर सकती हैं। समाधान:

js
// Rapier: Enable CCD
const bodyDesc = RAPIER.RigidBodyDesc.dynamic().setCcdEnabled(true)

// Cannon-es: Use smaller timesteps or thicker walls
world.step(1/120) // 120 Hz instead of 60

संबंधित

बाहरी संसाधन