holodepth

Three.js · Motor · Fizik entegrasyonu

Fizik motoru entegrasyonu: iki dünyanın birleşimi

Entegrasyon, ekranda gördüğünüz görsel sahne ile arka planda koşan simülasyon katmanını aynı hikâyede tutma işidir. Biri çizimi üretir; diğeri kütleyi, hızı ve teması hesaplar — köprü kurulmadığında ise mesh uçar, gömülür veya “titreyerek” takılır.

Bu sayfa, o köprünün mimarisini anlatır: dünya kurulumundan gövde eşlemesine, kare başına adım–senkron sırasına ve “her modele tam fizik” tuzağından kaçınmaya kadar. Ayrıntılı kesişim testi sözleşmesi için Intersection test sayfasına; zamanlama ve güncelleme ritmi için Performans ve güncelleme döngüsü sayfasına bakın — burada tekrar etmek yerine fizik tarafının işletim modelini sabitliyoruz.

Görsel evren ve fiziksel evren

Pratikte iki paralel model taşırsınız. Görsel evren (ör. Three.js / Babylon.js), mesh’in silueti, materyal ve doku örneklemesiyle ilgilenir; kamera ve ışık burada yaşar. Fiziksel evren (Cannon-es, Ammo.js, Rapier vb.) ise kütle, ivme, sürtünme, eklem ve temas cevabını üretir — çoğu zaman sahne grafiğinizle birebir aynı ağaçta durmaz.

Entegrasyon, bu ikisini özdeş değil, eşlenik kılar: bir sandalyenin görseli karmaşık olabilir; simülasyonda onu taşıyan gövde yalnızca bir kutu veya küre olabilir. Bu ayrım kasıtlıdır; 6 numaralı bölümde nedenini netleştiriyoruz.

Kurulum boru hattı: dünya, gövde, adım

Üçlü, üst üste binen “tek işlev” değil; üç ayrı sorumluluktur: biri paylaşılan kuralları (world), biri sahneye giren aktör kaydını (body), biri de zamanı ilerleten çağrı yuvasını (step) taşır. İsimler kütüphaneden kütüphaneye kayar; karışıklık genelde hepsini tek “kur ve unut” yardımcısında topladığınızda başlar.

Dünya: yasalar ve politika

Dünya nesnesi, tüm gövdelerin ortak evrenidir. Yerçekimi vektörü ve genel sürtünme burada durur; ayrıca çoğu motorda çözücü yinelemeleri, CCD (ince ve hızlı nesnelerde tünelleme riskine karşı), uyku eşikleri ve geniş faz (broad-phase) ailesi gibi politika alanları da bu katmanda toplanır. Zaman adımı (fixed veya değişken Δt) burada “yürürlükteki birim” olarak seçilir: sabit adım tekrarlanabilir testlerde elverişlidir; değişken adım kare süresi dalgalanmasına uyum sağlar, fakat enerji ve stabilite tarafında daha sık ince ayar ister.

Gövde: kütle, şekil, pivot

Her anlamlı nesne için simülasyonda bir kayıt açılır; çoğu motor bunu rigid body olarak adlandırır. Bu kayıt yalnızca bir çizim değildir: kütle ve eylemsizlik tensörü (inertia) burada atanır; birim ölçek veya eksen dönüşümü yanlışsa “uçan karton” veya “çiviye çarpan tank” gibi sahte tepkiler üretir. Aynı gövdeye bir veya birden fazla şekil (kutu, küre, kapsül, düşük çözünürlüklü gövde) eklenir; görsel mesh ile birebir örtüşmek zorunda değildir — pivot ve model uzayı uyumsuzluğu çoğu “yerde kayma” hissini bu aşamada üretir (basit kabuk disiplinine 6 numaralı bölümde döneceğiz).

Adım: zamanı tüketen yuva

Step, yapılandırılmış dünyayı tanımlı Δt kadar ilerletir; çıktı olarak yeni durum vektörleri (konum, hız, dönüş, uyku durumu, temas listesi vb.) üretir. Burada henüz “ekranda ne çizildiği” sorusu yok; yalnızca simülasyonun iç durumu güncellenir. Bu çıktının görsel ağaca hangi sırada ve hangi düğüme yazılacağı ayrı bir sözleşmedir — okuma–yazma sırasını Senkronizasyon köprüsü bölümünde netleştiriyoruz. Şimdilik şu kural yeterli: adım, köprüden önce çalışmalıdır; aksi halde bir önceki karenin hayalet sonuçları sahneye taşınabilir.

Motor seçimi: hafiflik, güç, WASM

Holodepth omurgasında “tek doğru motor” yoktur; seçim, yük profili ile doğruluk ihtiyacı arasında kurulan dengeyle belirlenir. Burada amaç, kare başına süre tablosu kopyalamak değil; hangi gizli faturaların ürün ömrüne yazılacağını görmektir — entegrasyon boru hattının kendisi 2 numaralı bölümde motordan bağımsız kalır.

Hafif hat: Cannon.js / cannon-es

Saf JavaScript paketleri, genelde soğuk başlangıç ve hata ayıklama döngüsünde en düşük sürtünmeyi verir: ek derleme yok, sürüm yükseltmesi tipik bir npm işidir. Özellik seti sınırlıdır; fakat prototip, eğitim içeriği ve “birkaç dinamik kutu” sınıfı üretimlerde bu sınırlılık çoğu zaman özellik gibidir — karmaşıklık, doğruluk hatasına dönüşmeden önce kesilir.

Ağır ama dolu dolu: Ammo.js

Bullet tabanlı port, Emscripten hattının getirdiği ikili boyut ve bellek davranışıyla gelir: indirme ve ayrıştırma maliyeti yükselebilir; karşılığında eklem, araç süspansiyonu, yumuşak cisim gibi “fizik laboratuvarı” ihtiyaçları için geniş bir çözücü yelpazesi sunar. Burada kritik soru “sahne hızlı mı?” değil, hangi özellikleri taşımadan ürünü kapatamıyorum? sorusudur.

Performans omurgası: Rapier ve WASM

Rust çekirdeği + WebAssembly, çoğu projede çalışma zamanı verimliliği ve iş parçacığı hikâyesiyle öne çıkar; fakat paketleme, sürüm eşlemesi ve tarayıcı bellek tavanları gibi konular “ilk günden” plana yazılmalıdır. Worker’a taşıma fikri caziptir; o zaman köprü ve serileştirme maliyeti devreye girer — yine de ağır sahnede bu ek iş, ana iş parçacığını kurtarmaya değebilir.

Ölçüt kartı: kare süresi tek sütun değil

Aynı sahneyi iki motorla kıyaslayıp yalnızca FPS’e bakmak, dağıtım gerçekliğini gizleyebilir. Tartıya şunları da koyun: gzip / brotli sonrası indirme, ilk kareye kadar geçen ayrıştırma, WASM örneği başına bellek eğrisi, sürüm pinleme disiplini ve ekip içinde “kim bu hatayı taşıyacak?” sorusu. İyi seçim, çoğu zaman en hızlı kütüphane değil; en az sürpriz üreten kütüphanedir.

Rigid body türleri: statik, dinamik, kinematik

Rigid body türü, görsel mesh’ten bağımsız bir kuvvet–tepki sözleşmesidir: motor, bu etikete göre ivme denklemini, uyku durumunu ve çarpışma yanıtını dallandırır. Kütle ve şekil ataması Gövde: kütle, şekil, pivot başlığında ele alındı; burada yalnızca davranış modunun anlamı ve birbirini nasıl ittiği özetlenir.

Statik (static)

Konumu dünya uzayında sabit kabul edilen engeller: zemin, duvar, ray. Simülasyon adımı onu serbest nesne gibi ilerletmez; buna karşılık çarpışma geometrisi olarak kalır ve dinamik gövdelerin temas çözümüne girer. “Hareket etmeyen her şey statiktir” kuralı yanıltıcıdır: animasyonla oynanan bir duvar, statik etiketiyle çelişir; doğru adres genelde kinematik olur.

Dinamik (dynamic)

Kuvvet, impuls ve sürtünmenin tam tabi olduğu moddur; oyun fiziğinin “oynanabilir” kısmı burada yaşar. Hızlar sıfıra yaklaştığında motorlar çoğu zaman uyku (sleep) durumuna alır — bu, hem maliyeti düşürür hem de mikro titremeyi keser; fakat yanlış eşiklerde nesne “donmuş” gibi görünebilir.

Kinematik (kinematic)

Hareket programı sizden gelir; motor içinden kuvvetle sürülmez. Hareketli platform, kapı, asansör veya “fiziği biz yazıyoruz, motor sadece çarpıştırıyor” dediğiniz oyuncu iskeletleri bu sınıfta sık düşer. Çoğu motorda kinematik gövde, dinamik gövdeleri iter; kendi kütle denklemi dinamikteki gibi çözülmez — bu yüzden “hem kodla hareket ettir hem tam çarpışma tepkisi al” beklentisi her zaman karşılanmayabilir; motor dokümantasyonundaki temas kuralını kontrol etmek gerekir.

Sık karıştırılan eşleme

Animasyonlu ama “fiziği olmayan” nesneyi statik sanmak, içinden geçiş veya titreşim üretir. Ağır bir dinamik nesneyi, ince bir kinematik panelin altına koymak, panelin hızına bağlı olarak üst üste binme (tunneling) riskini artırır — bu risk dünya tarafında CCD ile yönetilir; fakat önce doğru mod etiketini seçmek gerekir. Görsel–fizik kabuk ayrımının maliyet boyutu 6 numaralı bölümde ele alınır.

Senkronizasyon köprüsü

Bölüm 2 adımın ürettiği iç durumu, Bölüm 4 gövde rollerini tanımladı; burada ise köprü sırası vardır: aynı kare içinde veriyi kimin yazdığı ve okuyanın bunu nasıl yorumladığı. Amaç, mesh’i simülasyona geri besleyerek “çift otorite” yaratmamaktır.

Tek yönlü otorite

Simülasyon, o kare için doğru dönüşümü üretir; görsel taraf bunu yalnızca görüntüler. Aynı karede önce mesh’i oynatıp sonra fizik çıktısını üstüne yazarsanız sıra tutarlılığı bozulur; tersi, fizikten önce animasyon uygulayıp sonra adım çalıştırmak, temas kuvvetlerinin yanlış geometriye bakmasına yol açabilir. Kural: tek yazıcı (simülasyon) dönüşümü belirler; animasyon veya oyun mantığı, ya adım öncesinde kuvvet/impuls olarak girer ya da adım sonrasında — ama aynı transform alanında yarışmaz.

Kare içi şablon

Aşağıdaki üç satır “minimal doğru” sıradır; motor sarmalayıcılarınız farklı isimlerle bölebilir, fakat neden–sonra ilişkisi korunmalıdır.

  1. Fizik adımı: Dünya, tanımlı Δt ile ilerletilir; çıktı artık “bir sonraki anın” durumudur — bu aşamada WebGL çizmiyorsunuz.
  2. Konum kopyası: Gövdenin dünya uzayındaki pozisyonu, hedef mesh veya onu taşıyan üst düğüme yazılır. Hiyerarşide birden fazla Object3D varsa, kopyayı hangi düğümün taşıyacağını önceden sabitleyin; aksi halde ebeveyn–çocuk dönüşüm zinciri “sürüklenmiş” gibi görünür.
  3. Yönelim kopyası: Çoğu motor kuaterniyon tutar; görsel tarafta da aynı temsil tercih edilir. Euler’e çeviriyorsanız eksen sırası (order) üretim boyunca tek tip kalmalıdır — buradaki “sallanma” çoğu zaman fizik hatası değil, dönüşüm sözleşmesi hatasıdır.

Dünya mı, yerel mi?

Fizik motorları tipik olarak dünya uzayında durum döndürür; sahne grafiğinizde ise nesne yerel pivot etrafında gruplanmış olabilir. Köprü, bazen yalnızca “pozisyonu kopyala”dan ibaret değildir: gerekirse üst düğümün ters dönüşümüyle yerel bileşenlere ayırır veya tam tersi — önemli olan, her karede aynı matematiksel uzayı seçmektir. Bir gövdeyi birden fazla görsel parçaya böldüyseniz, kopyayı yalnızca kök mesh’e değil, ortak bir “fizik taşıyıcı” düğümüne bağlamak sık görülen çözümdür.

Çoklu adım, tek okuma

Bir karede birden fazla fizik alt adımı çalıştırıyorsanız, görsel güncellemeyi genelde son adımın sonucuna bağlarsınız; ara adımlarda mesh pozisyonunu titretmek hem maliyeti artırır hem de kamera takibinde “arızi zıplama” üretir. Ara durumu yumuşatmak interpolasyon ister; bu da gecikme ve bellek arasında ayrı bir denge ister.

Kare süresi ve sabit adım

requestAnimationFrame ile sabit Δt dünyasını eşleştirmek, biriktirme (accumulator), faz atlama ve görsel interpolasyon gibi başlıkları gerektirir; bunların ayrıntılı haritası Performans ve güncelleme döngüsü rehberindedir. Bu sayfada sabitlenen tek cümle şudur: önce simülasyon zamanı, sonra görüntüleme zamanı — ikisini aynı sayaçla ölçmeyi unutmayın; aksi halde “fizik stabil, ekran titrek” ayrışması kaçınılmazdır.

Her yüzeye tam fizik mi?

“Model ne kadar detaylıysa, çarpıştırıcı da o kadar detaylı olmalı” varsayımı yaygındır; üretimde ise genelde tersi geçerlidir. Her ek gövde ve karmaşık mesh teması, CPU maliyetini artırır; darboğaz bazen matematikte değil, aday küme şişmesinde oluşur.

Pratik kural: görsel karmaşıklıktan bağımsız olarak, simülasyonda temsilci geometriyi kutu, küre, kapsül veya düşük çözünürlüklü gövde ile tutun. Yüksek çözünürlüklü görsel ile basit collider ayrımı, hem performansı hem de hata ayıklanabilirliği iyileştirir — oyuncunun “görsel olarak değdi ama saymadı” hissi, çoğu zaman bu kabukta ince ayar meselesidir.

Holodepth teknik notu

Fizik entegrasyonu, raycaster tabanlı seçimden farklı bir sözleşmedir: seçim genelde anlık bir sorgu iken, simülasyon kareler arası süreklilik ister. İkisini aynı veri yapısına bağlarken kimlik ve katmanları karıştırmamak, üretim hatalarını azaltır.