Web oyunları için oyun fiziği
Fizik simülasyonu; düşen nesneler, zıplayan toplar, ragdoll karakterler, araçlar ve yok edilebilir ortamlarla oyunlara hayat verir. Bu rehber, web oyunlarında kullanılabilen başlıca fizik kütüphanelerini gerçek kod örnekleri ve dürüst artı/eksi değerlendirmeleriyle ele alıyor.
Genel bakış
| Motor | Boyut | Performans | Boyut | Zorluk | Yumuşak Cisimler | Araçlar | CCD | Belirlenimlilik |
|---|---|---|---|---|---|---|---|---|
| Rapier | 2D/3D | ⭐⭐⭐⭐⭐ | 1.4 MB | Orta | — | ✅ | ✅ | ✅ |
| Cannon-es | 3D | ⭐⭐⭐ | 150 KB | Kolay | — | — | — | — |
| Ammo.js | 3D | ⭐⭐⭐⭐⭐ | 1-2 MB | Zor | ✅ | ✅ | ✅ | ⚠️ |
| Jolt | 3D | ⭐⭐⭐⭐⭐ | — | Zor | ✅ | ✅ | ✅ | ✅ |
| Oimo.js | 3D | ⭐⭐⭐ | 100 KB | Kolay | — | — | — | — |
| Matter.js | 2D | ⭐⭐⭐ | 80 KB | Kolay | — | — | — | ⚠️ |
| Planck.js | 2D | ⭐⭐⭐⭐ | 120 KB | Orta | — | — | ✅ | ⚠️ |
| p2-es | 2D | ⭐⭐⭐ | 100 KB | Orta | — | — | — | — |
| Box2D WASM | 2D | ⭐⭐⭐⭐⭐ | 300 KB | Zor | — | — | ✅ | ✅ |
Açıklamalar:
- CCD = Sürekli Çarpışma Algılama (hızlı nesnelerin duvarların içinden geçmesini önler)
- ⚠️ Belirlenimlilik = Sabit zaman adımıyla mümkün olsa da platformlar arasında garanti edilmez
Hızlı öneriler
| Durumunuz | En iyi seçim |
|---|---|
| Yayına hazır 3D oyun | Rapier — en iyi performans, modern API, aktif geliştirme |
| Öğrenme / prototip | Cannon-es (3D) veya Matter.js (2D) — basit API'ler, kolay hata ayıklama |
| Araç fiziği | Rapier veya Ammo.js — ikisinde de ışın izlemeli araç denetleyicileri bulunur |
| Yumuşak cisimler, kumaşlar, halatlar | Ammo.js veya Jolt (JoltPhysics.js) — ikisi de kumaşları ve yumuşak cisimleri destekler; Jolt'un WASM portu daha aktif biçimde geliştirilmektedir |
| Hassas 2D platform oyunu | Planck.js — Box2D algoritmaları, sabit zaman adımıyla belirlenimlilik |
| En yüksek 2D performansı | Box2D WASM — tarayıcıda yerel kod hızı |
| En küçük paket boyutu | Oimo.js (3D) veya Matter.js (2D) |
Ayrıntılı karşılaştırma
3D Fizik Motorları
| Motor | Dil | Boyut | Performans | En uygun kullanım |
|---|---|---|---|---|
| Rapier | Rust/WASM | ~1.4 MB | Mükemmel | Yayına hazır oyunlar, karmaşık simülasyonlar |
| Cannon-es | JavaScript | ~150 KB | İyi | Prototipler, basit oyunlar |
| Ammo.js | C++/WASM | ~1-2 MB | Mükemmel | AAA özellikleri, yumuşak cisimler, araçlar |
| Jolt | C++/WASM | WASM | Mükemmel | Aktif bakımla sunulan AAA özellikleri |
| Oimo.js | JavaScript | ~100 KB | İyi | Basit oyunlar, hızlı prototipler |
2D Fizik Motorları
| Motor | Dil | Boyut | Performans | En uygun kullanım |
|---|---|---|---|---|
| Matter.js | JavaScript | ~80 KB | İyi | Görsel oyunlar, prototipler |
| Planck.js | JavaScript | ~120 KB | İyi | Platform oyunları, hassas fizik |
| p2-es | JavaScript | ~100 KB | İyi | Kısıtlamalar, mekanizmalar |
| Box2D WASM | C++/WASM | ~300 KB | Mükemmel | Çok sayıda fizik cismi |
3D Fizik Motorları
Rapier — Modern seçim
Rapier, JavaScript/WASM bağlayıcılarına sahip bir Rust fizik motorudur. 2024 sürümüne kıyasla 2-5 kat hız artışı sunarak 2025-2026 döneminde web oyunları için en yüksek performanslı seçenek hâline gelmiştir.
Kurulum:
npm install @dimforge/rapier3d
# Or with SIMD (faster, requires modern browsers):
npm install @dimforge/rapier3d-simdTemel kurulum:
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)Oyun döngüsü entegrasyonu:
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)
}Çarpışma algılama:
// 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)
}Eklemler:
// 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)Araç denetleyicisi:
// 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)
}Artıları:
- En iyi performans (WASM + SIMD)
- Mükemmel çarpışma algılama doğruluğu
- Platformlar arası belirlenimlilik (aynı girdiler = aynı çıktılar)
- Aktif geliştirme, modern API
- Sürekli çarpışma algılama (nesneler yüzeylerin içinden geçmez)
- Yerleşik karakter ve araç denetleyicileri
Eksileri:
- Daha büyük paket boyutu (~1.4 MB)
- Eşzamansız başlatma gerektirir
- Saf JS alternatiflerinden daha karmaşık API
- WASM'de hata ayıklamak zor olabilir
Cannon-es — Basit ve etkili
Cannon-es, Cannon.js'in bakımı sürdürülen çatallanmış sürümüdür. Saf JavaScript ile yazılmıştır; anlaşılması kolaydır ve öğrenme ile prototip geliştirme için idealdir.
Kurulum:
npm install cannon-esTemel kurulum:
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)Oyun döngüsü:
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)
}Çarpışma olayları:
sphereBody.addEventListener('collide', (event) => {
const contact = event.contact
const impactVelocity = contact.getImpactVelocityAlongNormal()
if (Math.abs(impactVelocity) > 5) {
console.log('Hard impact!')
}
})Kısıtlamalar:
// 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)Artıları:
- Saf JavaScript, WASM karmaşıklığı yok
- Küçük paket boyutu (~150 KB)
- Öğrenmesi ve hata ayıklaması kolay
- Her yerde çalışır
- Güçlü Three.js entegrasyonu
- İyi dokümantasyon
Eksileri:
- WASM alternatiflerinden daha yavaş
- Çok sayıda cisimle (>100) zorlanır
- Sınırlı üçgen örgü desteği
- Yerleşik CCD yoktur (nesneler yüzeylerin içinden geçebilir)
- Geliştirme etkinliği daha düşük
Ammo.js — Bullet Physics'in tüm gücü
Ammo.js, WebAssembly'ye derlenmiş Bullet Physics motorudur. Yumuşak cisimler, araçlar ve gelişmiş kısıtlamalar dâhil en kapsamlı özellik setini sunar.
Kurulum:
npm install ammo.js
# Or use from CDNTemel kurulum:
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
}Yumuşak cisimler (kumaş):
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
}Araç fiziği:
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
}Artıları:
- En kapsamlı özellik seti
- Yumuşak cisimler, kumaşlar, halatlar
- Gelişmiş araç fiziği
- Savaş koşullarında kendini kanıtlamış teknoloji (birçok AAA oyunda kullanılır)
- Son derece özelleştirilebilir Eksileri:
- Karmaşık ve ayrıntılı API
- Büyük paket boyutu
- Bellek yönetimi gerektirir (nesneleri yok etme)
- Zorlu öğrenme süreci
- Dağınık dokümantasyon
- Platformlar arasında deterministik değildir (dikkatli yapılandırmayla yalnızca aynı cihazda)
Jolt — modern, AAA kalitesinde 3D fizik
Jolt Physics, Horizon Forbidden West gibi oyunların arkasındaki motordur; JoltPhysics.js ise onu bir WASM portu olarak tarayıcıya getirir. Rapier ile Ammo.js arasında güçlü bir orta yoldur: Rapier'den daha fazla özellik (yumuşak cisimler, kumaş, tekerlekli araç kontrolcüsü) sunarken Ammo.js'de bulunmayan aktif bakıma sahiptir. npm paketi jolt-physics'tir; React Three Fiber (@react-three/jolt) ve Babylon.js için hazır entegrasyonlar da bulunur.
npm install jolt-physicsRapier gibi, C++ arayüzünü yansıtan ayrıntılı ama eksiksiz bir API'ye sahip, eşzamansız başlatılan bir WASM çözümüdür. Bullet sınıfı özellikler (yumuşak cisimler, araçlar) istediğiniz ancak Ammo.js yerine bakımı sürdürülen modern bir kod tabanı tercih ettiğiniz durumlarda Jolt'u kullanın.
Oimo.js — Hafif ve hızlı
Oimo.js hafif bir 3D fizik motorudur. Gelişmiş özelliklere ihtiyaç duymadığınız basit oyunlar için idealdir.
Kurulum:
npm install oimoTemel kurulum:
import * as OIMO from 'oimo'
const world = new OIMO.World({
timestep: 1/60,
iterations: 8,
broadphase: 2, // 1: kaba kuvvet, 2: süpür ve buda, 3: hacim ağacı
worldscale: 1,
random: true,
gravity: [0, -9.8, 0]
})
// Zemini oluştur
world.add({
type: 'box',
size: [100, 1, 100],
pos: [0, -0.5, 0],
move: false
})
// Düşen küreyi oluştur
const sphere = world.add({
type: 'sphere',
size: [1],
pos: [0, 10, 0],
move: true,
density: 1,
friction: 0.4,
restitution: 0.2
})Oyun döngüsü:
function animate() {
world.step()
// Konumu/dönüşü al
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)
}Artıları:
- Çok küçük (~100 KB)
- Basit API
- Temel sahnelerde iyi performans
- Yerleşik Babylon.js desteği
Eksileri:
- Sınırlı şekiller (yalnızca temel geometriler)
- Yumuşak cisim desteği yok
- Yetersiz dokümantasyon
- Daha az aktif geliştirme
- Sınırlı eklem seçenekleri
2D Fizik Motorları
Matter.js — Güzel ve sezgisel
Matter.js, mükemmel render ve hata ayıklama araçlarına sahip, özellik bakımından zengin bir 2D fizik motorudur.
Kurulum:
npm install matter-jsTemel kurulum:
import Matter from 'matter-js'
const { Engine, Render, World, Bodies, Runner } = Matter
// Motoru oluştur
const engine = Engine.create()
// Render aracını oluştur (isteğe bağlı, hata ayıklama için harika)
const render = Render.create({
element: document.body,
engine: engine,
options: {
width: 800,
height: 600,
wireframes: false
}
})
// Cisimleri oluştur
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
})
// Dünyaya ekle
World.add(engine.world, [ground, box, circle])
// Çalıştır
Render.run(render)
Runner.run(Runner.create(), engine)Çarpışma olayları:
Matter.Events.on(engine, 'collisionStart', (event) => {
event.pairs.forEach(pair => {
console.log('Şunlar arasında çarpışma:', pair.bodyA.label, pair.bodyB.label)
})
})Kısıtlamalar:
// Sabitleme kısıtlaması
const pin = Matter.Constraint.create({
pointA: { x: 400, y: 100 },
bodyB: box,
stiffness: 0.9
})
// Cisimler arasında yay
const spring = Matter.Constraint.create({
bodyA: box,
bodyB: circle,
stiffness: 0.01,
length: 100
})
World.add(engine.world, [pin, spring])Fare etkileşimi:
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)Artıları:
- Güzel varsayılan render
- Öğrenme/prototipleme için harika
- Sezgisel API
- İyi dokümantasyon
- Aktif topluluk
- Varsayılan olarak deterministik — Matter.Runner artık sabit ve deterministik bir zaman adımı kullanıyor (sabit olmayan zaman adımı v0.20.0'da kaldırıldı)
Eksileri:
- CCD yok (hızlı nesneler diğer cisimlerin içinden geçebilir; bunu azaltmak için alt adımlama kullanın)
- Çok sayıda cisimde performans sorunları
- WASM seçeneği yok
- Karmaşık simülasyonlarda sınırlı hassasiyet
Planck.js — JavaScript için Box2D
Planck.js, Box2D'nin JavaScript ile baştan yazılmış eksiksiz bir sürümüdür. Savaşta sınanmış fizik sunar ve sabit zaman adımı kullanıldığında deterministiktir.
Kurulum:
npm install planckTemel kurulum:
import { World, Vec2, Box, Circle, Edge } from 'planck'
// Dünyayı oluştur
const world = new World({
gravity: Vec2(0, -10)
})
// Zemini oluştur
const ground = world.createBody()
ground.createFixture({
shape: Edge(Vec2(-40, 0), Vec2(40, 0))
})
// Dinamik kutuyu oluştur
const box = world.createBody({
type: 'dynamic',
position: Vec2(0, 10)
})
box.createFixture({
shape: Box(1, 1),
density: 1,
friction: 0.3,
restitution: 0.5
})Sabit zaman adımlı oyun döngüsü:
const TIMESTEP = 1 / 60
const VELOCITY_ITERATIONS = 8
const POSITION_ITERATIONS = 3
function gameLoop() {
world.step(TIMESTEP, VELOCITY_ITERATIONS, POSITION_ITERATIONS)
// Tüm cisimleri dolaş
for (let body = world.getBodyList(); body; body = body.getNext()) {
const pos = body.getPosition()
const angle = body.getAngle()
// Sprite'larınızı güncelleyin...
}
requestAnimationFrame(gameLoop)
}Çarpışma geri çağrıları:
world.on('begin-contact', (contact) => {
const fixtureA = contact.getFixtureA()
const fixtureB = contact.getFixtureB()
console.log('Temas başladı')
})
world.on('end-contact', (contact) => {
console.log('Temas sona erdi')
})
world.on('pre-solve', (contact, oldManifold) => {
// Temas burada devre dışı bırakılabilir
// contact.setEnabled(false)
})Eklemler:
import { RevoluteJoint, DistanceJoint, PrismaticJoint } from 'planck'
// Döner eklem (menteşe)
const joint = world.createJoint(RevoluteJoint({
bodyA: ground,
bodyB: box,
localAnchorA: Vec2(0, 5),
localAnchorB: Vec2(-1, 0),
enableMotor: true,
maxMotorTorque: 1000,
motorSpeed: 2
}))
// Mesafe eklemi (yay)
world.createJoint(DistanceJoint({
bodyA: boxA,
bodyB: boxB,
localAnchorA: Vec2(0, 0),
localAnchorB: Vec2(0, 0),
length: 5,
stiffness: 10,
damping: 0.5
}))Artıları:
- Sabit zaman adımıyla deterministik
- İyi belgelenmiş (Box2D dokümanları geçerlidir)
- İyi performans
- TypeScript desteği
- Küçük paket boyutu
Eksileri:
- CCD eklemleri işlemez; bu nedenle eklemlerle bağlanan hızlı nesneler esneyebilir
- Matter.js'e göre daha zorlu öğrenme süreci
- Yerleşik render aracı yok
- Box2D'ye özgü ayrıntılar (birim ölçeği önemlidir)
p2-es — Esnek 2D fizik
p2-es, p2.js'in bakımı sürdürülen çatallanmış sürümüdür. Karmaşık kısıtlamalara ve mekanizmalara ihtiyaç duyan oyunlar için harikadır.
Kurulum:
npm install p2-esTemel kurulum:
import * as p2 from 'p2-es'
const world = new p2.World({
gravity: [0, -9.81]
})
// Zemin
const groundBody = new p2.Body({
mass: 0, // statik
position: [0, -1]
})
groundBody.addShape(new p2.Plane())
world.addBody(groundBody)
// Dinamik daire
const circleBody = new p2.Body({
mass: 1,
position: [0, 5]
})
circleBody.addShape(new p2.Circle({ radius: 0.5 }))
world.addBody(circleBody)Gelişmiş kısıtlamalar:
// Dişli kısıtlaması
const gear = new p2.GearConstraint(bodyA, bodyB, {
ratio: 2 // bodyB iki kat hızlı döner
})
world.addConstraint(gear)
// Prizmatik kısıtlama (kaydırıcı)
const prismatic = new p2.PrismaticConstraint(bodyA, bodyB, {
localAnchorA: [0, 0],
localAnchorB: [0, 0],
localAxisA: [1, 0],
disableRotationalLock: false
})
world.addConstraint(prismatic)
// Kilit kısıtlaması (kaynak)
const lock = new p2.LockConstraint(bodyA, bodyB)
world.addConstraint(lock)Temas malzemeleri:
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)
// Şekillere uygula
iceBody.shapes[0].material = ice
rubberBody.shapes[0].material = rubberArtıları:
- Zengin kısıtlama sistemi
- Temas malzemeleri
- Mekanizmalar/makineler için uygun
- Uyuyan cisimler (performans)
- ES modülleri, tree-shaking desteği
Eksileri:
- Yerleşik render aracı yok
- Bazı şekil çiftleri desteklenmiyor
- Alternatiflerden daha az aktif
- Dokümantasyon eksiklikleri
Performans ipuçları
1. Sabit zaman adımı kullanın
const TIMESTEP = 1 / 60
let accumulator = 0
function gameLoop(deltaTime) {
accumulator += deltaTime
while (accumulator >= TIMESTEP) {
world.step(TIMESTEP)
accumulator -= TIMESTEP
}
// Akıcı render için enterpolasyon yap
const alpha = accumulator / TIMESTEP
// lerp(previousState, currentState, alpha)
}2. Etkin olmayan cisimleri uyutun
Çoğu motor uyku özelliğini destekler. Etkinleştirin:
// Cannon-es
world.allowSleep = true
body.allowSleep = true
body.sleepSpeedLimit = 0.1
body.sleepTimeLimit = 1
// Rapier
bodyDesc.setCanSleep(true)3. Basit çarpışma şekilleri kullanın
// İYİ: Basit temel geometriler
const sphere = new CANNON.Sphere(1)
const box = new CANNON.Box(new CANNON.Vec3(1, 1, 1))
// KAÇININ: Dinamik cisimler için karmaşık üçgen ağ
const trimesh = new CANNON.Trimesh(vertices, indices) // Yavaş!4. Çözücü yinelemelerini azaltın (dikkatlice)
// Cannon-es
world.solver.iterations = 5 // Varsayılan 10
// Rapier - dünya oluşturulurken yapılandırılır5. Fiziği bir Web Worker içinde çalıştırın
// main.js
const worker = new Worker('physics-worker.js')
worker.postMessage({ type: 'step', deltaTime: 1/60 })
worker.onmessage = (e) => {
const { positions, rotations } = e.data
// Render nesnelerini güncelle
}
// 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())
})
}
}Hangi durumda hangisini kullanmalı?
| Senaryo | Önerilen | Neden |
|---|---|---|
| Üretime hazır 3D oyun | Rapier | En iyi performans, modern API, araçlar |
| 3D oyun prototipi | Cannon-es | Basit, hata ayıklaması kolay |
| Yumuşak cisimler, kumaş, halatlar | Ammo.js veya Jolt | Her ikisi de kumaş/yumuşak cisimleri destekler; Jolt'un WASM portunun (JoltPhysics.js) bakımı daha aktif biçimde sürdürülür |
| Basit 3D tarayıcı oyunu | Oimo.js | Küçük, yeterli |
| 2D game jam | Matter.js | Hızlı kurulum, yerleşik render |
| Hassas platform oyunu | Planck.js | Sabit zaman adımı determinizmi, iyi dokümantasyon |
| Karmaşık 2D mekanizmalar | p2-es | Zengin kısıtlama sistemi |
| Performans açısından kritik 2D | Box2D WASM | En hızlı 2D seçeneği, gerçek CCD |
Render araçlarıyla entegrasyon
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() // fizik cismi -> mesh
function createPhysicsBox(x, y, z) {
// Fizik
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) {
// Fizik
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
})
})Yaygın tuzaklar
Ölçek önemlidir
Fizik motorları gerçek dünya ölçeğiyle (1 birim = 1 metre) en iyi şekilde çalışır. Piksel koordinatlarını doğrudan kullanmayın.
// KÖTÜ: Piksel konumlarını kullanmak
const body = Bodies.circle(400, 300, 50) // 50 piksel yarıçap mı?
// İYİ: Bir ölçek faktörü kullanın
const SCALE = 50 // metre başına 50 piksel
const body = Bodies.circle(8, 6, 1) // 1 metre yarıçap
// Ardından render sırasında SCALE ile çarpınWASM motorlarında bellek sızıntıları
Cisimleri kaldırırken her zaman temizleyin:
// Rapier
world.removeRigidBody(body)
// Ammo.js
physicsWorld.removeRigidBody(body)
Ammo.destroy(body)
Ammo.destroy(shape)
Ammo.destroy(motionState)Tünelleme (nesnelerin birbirinin içinden geçmesi)
Hızlı hareket eden nesneler ince duvarların içinden geçebilir. Çözümler:
// Rapier: CCD'yi etkinleştir
const bodyDesc = RAPIER.RigidBodyDesc.dynamic().setCcdEnabled(true)
// Cannon-es: Daha küçük zaman adımları veya daha kalın duvarlar kullan
world.step(1/120) // 60 yerine 120 Hzİlgili
- Oyun geliştiricileri için WebGL temelleri
- Oyun mantığı için Web Worker'lar — fizik hesaplamalarını bir Worker iş parçacığına aktarın
- Hızlı yüklenen bir web oyunu yayınlayın
- Canvas 2D oyun döngüsü — her fizik motorunun kullandığı sabit zaman adımı kalıbı
- 2026'da Web Oyunları Teknoloji Yığını — fizik motorlarının genel teknoloji yığınındaki yeri
- Web Oyun Motorları Karşılaştırması — yerleşik fizik desteğine sahip motorlar
Harici Kaynaklar
- Rapier belgeleri — resmi Rapier fizik belgeleri ve örnekleri
- Cannon-es belgeleri — API referansı ve kılavuzlar
- Matter.js belgeleri — eksiksiz API referansı
- Planck.js belgeleri — JavaScript için Box2D