वेब गेम्स के लिए गेम फिजिक्स
फिजिक्स सिमुलेशन गेम्स में जान डाल देता है, गिरती हुई चीजें, उछलती गेंदें, ragdolls, गाड़ियां, और टूटने वाले environments. यह गाइड वेब गेम्स के लिए उपलब्ध प्रमुख फिजिक्स लाइब्रेरियों को कवर करती है, असली कोड उदाहरणों और ईमानदार फायदे/नुकसान के साथ।
एक नजर में
| Engine | Dimension | Performance | Size | Difficulty | Soft Bodies | Vehicles | CCD | Determinism |
|---|---|---|---|---|---|---|---|---|
| Rapier | 2D/3D | ⭐⭐⭐⭐⭐ | 1.4 MB | मध्यम | — | ✅ | ✅ | ✅ |
| Cannon-es | 3D | ⭐⭐⭐ | 150 KB | आसान | — | — | — | — |
| Ammo.js | 3D | ⭐⭐⭐⭐⭐ | 1-2 MB | कठिन | ✅ | ✅ | ✅ | ⚠️ |
| Jolt | 3D | ⭐⭐⭐⭐⭐ | — | कठिन | ✅ | ✅ | ✅ | ✅ |
| Oimo.js | 3D | ⭐⭐⭐ | 100 KB | आसान | — | — | — | — |
| Matter.js | 2D | ⭐⭐⭐ | 80 KB | आसान | — | — | — | ⚠️ |
| Planck.js | 2D | ⭐⭐⭐⭐ | 120 KB | मध्यम | — | — | ✅ | ⚠️ |
| p2-es | 2D | ⭐⭐⭐ | 100 KB | मध्यम | — | — | — | — |
| Box2D WASM | 2D | ⭐⭐⭐⭐⭐ | 300 KB | कठिन | — | — | ✅ | ✅ |
लेजेंड:
- CCD = Continuous Collision Detection (तेज चीजों को दीवारों के आर-पार निकलने से रोकता है)
- ⚠️ Determinism = fixed timestep के साथ संभव है, पर cross-platform गारंटीड नहीं
झटपट सिफारिशें
| आपकी स्थिति | सबसे अच्छा विकल्प |
|---|---|
| Production 3D game | Rapier — सबसे अच्छी परफॉर्मेंस, आधुनिक API, सक्रिय डेवलपमेंट |
| सीखना / prototype | Cannon-es (3D) या Matter.js (2D) — सरल API, आसान डीबगिंग |
| Vehicle physics | Rapier या Ammo.js — दोनों में ray-cast vehicle controllers हैं |
| Soft bodies, cloth, ropes | Ammo.js या Jolt (JoltPhysics.js) — दोनों cloth और soft bodies सपोर्ट करते हैं; Jolt का WASM port ज्यादा सक्रिय रूप से मेंटेन होता है |
| सटीक 2D platformer | Planck.js — Box2D algorithms, fixed timestep के साथ deterministic |
| अधिकतम 2D performance | Box2D WASM — browser में native speed |
| सबसे छोटा bundle size | Oimo.js (3D) या Matter.js (2D) |
विस्तृत तुलना
3D Physics Engines
| Engine | Language | Size | Performance | किसके लिए सबसे अच्छा |
|---|---|---|---|---|
| Rapier | Rust/WASM | ~1.4 MB | बेहतरीन | Production games, जटिल simulations |
| Cannon-es | JavaScript | ~150 KB | अच्छा | Prototypes, सरल games |
| Ammo.js | C++/WASM | ~1-2 MB | बेहतरीन | AAA features, soft bodies, vehicles |
| Jolt | C++/WASM | WASM | बेहतरीन | सक्रिय मेंटेनेंस के साथ AAA features |
| Oimo.js | JavaScript | ~100 KB | अच्छा | सरल games, झटपट prototypes |
2D Physics Engines
| Engine | Language | Size | Performance | किसके लिए सबसे अच्छा |
|---|---|---|---|---|
| Matter.js | JavaScript | ~80 KB | अच्छा | Visual games, prototypes |
| Planck.js | JavaScript | ~120 KB | अच्छा | Platformers, सटीक physics |
| p2-es | JavaScript | ~100 KB | अच्छा | Constraints, mechanisms |
| Box2D WASM | C++/WASM | ~300 KB | बेहतरीन | ज्यादा body counts |
3D Physics Engines
Rapier — आधुनिक विकल्प
Rapier एक Rust फिजिक्स इंजन है जिसमें JavaScript/WASM bindings हैं। 2025-2026 में वेब गेम्स के लिए यह सबसे परफॉर्मेंट विकल्प है, अपने 2024 वर्जन की तुलना में 2-5x speed सुधार के साथ।
Install:
npm install @dimforge/rapier3d
# Or with SIMD (faster, requires modern browsers):
npm install @dimforge/rapier3d-simdबेसिक सेटअप:
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:
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:
// 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:
// 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:
// 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:
npm install cannon-esबेसिक सेटअप:
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:
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:
sphereBody.addEventListener('collide', (event) => {
const contact = event.contact
const impactVelocity = contact.getImpactVelocityAlongNormal()
if (Math.abs(impactVelocity) > 5) {
console.log('Hard impact!')
}
})Constraints:
// 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:
npm install ammo.js
# Or use from CDNबेसिक सेटअप:
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):
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:
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 मौजूद हैं।
npm install jolt-physicsRapier की तरह, यह 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:
npm install oimoबेसिक सेटअप:
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:
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:
npm install matter-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:
Matter.Events.on(engine, 'collisionStart', (event) => {
event.pairs.forEach(pair => {
console.log('Collision between:', pair.bodyA.label, pair.bodyB.label)
})
})Constraints:
// 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:
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:
npm install planckबेसिक सेटअप:
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:
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:
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:
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:
npm install p2-esबेसिक सेटअप:
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:
// 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:
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 इस्तेमाल करें
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 करें:
// Cannon-es
world.allowSleep = true
body.allowSleep = true
body.sleepSpeedLimit = 0.1
body.sleepTimeLimit = 1
// Rapier
bodyDesc.setCanSleep(true)3. सरल collision shapes इस्तेमाल करें
// 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 कम करें (सावधानी से)
// Cannon-es
world.solver.iterations = 5 // Default 10
// Rapier - configured at world creation5. physics को Web Worker में चलाएं
// 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 game | Rapier | सबसे अच्छी परफॉर्मेंस, आधुनिक API, vehicles |
| 3D game prototype | Cannon-es | सरल, debuggable |
| Soft bodies, cloth, ropes | Ammo.js या Jolt | दोनों cloth/soft bodies सपोर्ट करते हैं; Jolt का WASM port (JoltPhysics.js) ज्यादा सक्रिय रूप से मेंटेन होता है |
| सरल 3D browser game | Oimo.js | छोटा, पर्याप्त |
| 2D game jam | Matter.js | तेज सेटअप, बिल्ट-इन rendering |
| सटीक platformer | Planck.js | Fixed timestep determinism, अच्छी तरह documented |
| जटिल 2D mechanisms | p2-es | समृद्ध constraint system |
| Performance-critical 2D | Box2D WASM | सबसे तेज 2D विकल्प, असली CCD |
Renderers के साथ integration
Three.js + Rapier
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
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 को सीधे इस्तेमाल न करें।
// 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 renderingWASM engines के साथ memory leaks
bodies को हटाते समय हमेशा उन्हें साफ करें:
// Rapier
world.removeRigidBody(body)
// Ammo.js
physicsWorld.removeRigidBody(body)
Ammo.destroy(body)
Ammo.destroy(shape)
Ammo.destroy(motionState)Tunneling (चीजों का एक-दूसरे के आर-पार निकलना)
तेज चलती चीजें पतली दीवारों के आर-पार tunnel कर सकती हैं। समाधान:
// 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संबंधित
- गेम डेवलपर्स के लिए WebGL fundamentals
- गेम logic के लिए Web Workers — physics को Worker thread पर offload करें
- एक वेब गेम भेजें जो तेजी से लोड हो
- Canvas 2D game loop — हर फिजिक्स इंजन में इस्तेमाल होने वाला fixed timestep पैटर्न
- 2026 में Web Games Tech Stack — फिजिक्स engines बड़े stack में कैसे फिट होते हैं
- Web Game Engines की तुलना — बिल्ट-इन physics सपोर्ट वाले engines
बाहरी संसाधन
- Rapier documentation — आधिकारिक Rapier physics docs और उदाहरण
- Cannon-es documentation — API reference और गाइड
- Matter.js documentation — पूरा API reference
- Planck.js documentation — JavaScript के लिए Box2D