Oyun geliştirme · Three + fizik köprüsü
Çarpışma Tepkisi Köprüsü: Olaylar ve Response Hook’ları
Fizik motoru temas üretir; oyun tepki politikasını yazar. Bu sayfa collision event yüzeyini, filtre hook’larını ve Three görseline yansımayı anlatır. Tam impulse çözümü veya fizik ders kitabı değildir; köprü katmanı «ne oldu, ne yapacağız?» sorusuna cevap verir.
Holodepth bu yaprağı constraint matematiği veya geniş ECS event bus mimarisi olarak yazmaz. Motor yüzeyi ve senkron Rapier / Cannon yüzeyi yaprağında mühürlendi. Pointer isabeti ile fizik teması ayrıdır; picking politikası R3F · Pointer & picking yaprağındadır. Ağ üzerinden fizik replikasyonu ve netcode Hexovian izleğindedir. glTF collision extension şeması bu kapıda yoktur.
Sayfayı bitirdiğinizde şu cümleyi kurabilmelisiniz: «Motor temas üretir; oyun hook ile tepki verir. start / stay / end ayrımı yazılır. Ses, skor ve hasar köprüde; impulse motorun işi. Picking ile fizik karıştırılmaz. Sonraki kapı sabit timestep performansıdır.»
Bu ayrımın en büyük faydası hata ayıklarken görülür: «çarpışma çalışmıyor» şikâyeti geldiğinde önce filtre ve faz politikasına bakılır, motorun impulse çözümüne değil — çoğu zaman sorun motorda değil, hook'un hangi çiftleri dinlediğinde saklıdır.
Bu sayfanın sınırı · komşu konularla ayrım
Bu sayfa Three + fizikte çarpışma tepkisi köprüsünü anlatır:
- Collision / contact event yüzeyi
- Response hook ve oyun mantığı ayrımı
- Filtre, katman, tekrarlı temas politikası
- Görsel / ses / state tepkisi (hafif)
Bilinçli olarak dışarıda bırakılan konular:
- World + body kurulumu → Rapier / Cannon yüzeyi
- Sabit timestep, accumulator → Sabit timestep & performans
- Pointer picking → R3F · Pointer & picking
- Impulse çözümü, friction formülü, constraint Jacobian
- Netcode, sunucu doğrulamalı fizik, rollback → Hexovian
- glTF şema ve asset pipeline
Kısa ayrım: Bu sayfa «temas oldu, hook ne yapar?» · Motor yüzeyi «world nasıl kurulur?» · Picking «ışın isabeti» · Hexovian «replike fizik ve motor içi».
Tepki köprüsü: motor ≠ oyun kuralı
Rapier ve Cannon çarpışmayı çözer: gövdeler birbirini iter, sürtünme uygular, temas noktası üretir. Oyun kuralları (puan, hasar, ses, görev ilerlemesi) bu çözümün üstünde yaşar. Holodepth: impulse hesabını yeniden yazmayın; hook’ta ürün politikasını çalıştırın.
Köprü katmanı «collision response» derken motorun fiziksel tepkisini değil, oyunun algıladığı tepkiyi kasteder: hangi çiftler için callback çalışır, hangi state güncellenir.
Bu ikisini karıştırmanın somut belirtisi: hook içinde
body.applyImpulse gibi çağrılarla motoru «düzeltmeye»
çalışmak. Motor zaten fiziksel tepkiyi hesaplamıştır; hook'un görevi bu
tepkiye oyun anlamı eklemektir, fiziği tekrar çözmek değildir.
Motor itişi çözer; oyun anlamı hook’ta doğar.
Olay modeli: start, stay, end
Çoğu motor yüzeyinde temas üç fazda düşünülür: begin / start (ilk temas), stay / during (süren temas), end / exit (ayrılma). API isimleri sürüme göre değişir; Holodepth ürün sözlüğünde bu üç fazı sabit tutun.
Stay fazında her fizik adımında callback çağırmak ses ve partikül için pahalıdır. Holodepth: stay’i seyrekleştirin veya yalnızca begin/end ile sınırlayın; «her adımda hasar» bilinçli politika olmalıdır.
Faz isimlerini kendi ürün sözlüğünüzde sabitlemek, motor değiştiğinde (Cannon'dan Rapier'e geçiş gibi) hook çağıran kodun değişmeden kalmasını sağlar — yalnızca adaptör katmanı motorun kendi isimlendirmesini bu üç faza çevirir.
Response hook sözleşmesi
Hook imzası tipik olarak iki gövde kimliği, temas noktası veya normal
taşır. Holodepth minimumu: onCollisionBegin(aId, bId),
onCollisionEnd(aId, bId). İçeride switch veya sözlük ile
«oyuncu + pickup», «düşman + mermi» eşleşmeleri çözülür.
Hook içinde ağır iş (pathfinding, tam sahne rebuild) yapmayın; kuyruk veya sonraki oyun fazına erteleyin. Fizik adımı sırasında uzun JS çarpışma adımlarını bozar.
Eşleşme çözümü için çift yönlü kontrol gerekir: (a, b) çifti
«oyuncu + pickup» olabileceği gibi (b, a) sırasıyla da
gelebilir. Holodepth: sözlük anahtarını iki tip adının sıralı birleşimiyle
normalize edin, motor hangi sırayla çağırırsa çağırsın aynı işleyiciye
düşsün.
Oyun mantığı: ses, skor, görsel
Tepki köprüsü Three tarafına hafif dokunuş yapar: materyal flash, kısa scale pulse, ses tetikleme. Transform’u hook’ta zorla itmek motor çözümüyle çatışır; görsel geri bildirim tercih edilir.
Entity lite yaprağındaki kimlik sözlüğü burada kullanılır: hook’ta mesh
aramak yerine id ile state güncelleyin.
Ses tetiklemesi özellikle dikkat gerektirir: aynı temas birden fazla kez begin sinyali üretirse (idempotency eksikse) aynı ses efekti üst üste binerek gürültü kirliliği yaratır. Holodepth: ses çağrısını begin fazının tam bir kez tetiklendiği garantiye bağlayın.
Picking vs fizik teması
Kullanıcı tıklaması Raycaster / R3F pointer ile isabet üretir; fizik motoru teması ayrı kanaldır. «Tıklanan mesh hasar alır» kuralı picking hook’unda yazılır; collision hook’unda değil. İstisna: raycast ile atış yönü üretip kinematic mermi spawn etmek; yine de temas motor olayına bağlanır.
Görünmez fizik collider, picking isabetini etkilemez (raycast katmanı ayrı). Ürün politikası: etkileşim mesh’i ile fizik hacmi bilinçli ayrılsın.
Bu iki kanalı tek bir olay işleyicisinde birleştirmeye çalışmak («hem tıklama hem çarpışma aynı fonksiyonu çağırsın») kısa vadede kod tekrarını azaltır ama uzun vadede hangi kanaldan geldiğini ayırt etmek için ek bayrak parametreleri gerektirir — iki ayrı, ince hook genelde daha okunaklı kalır.
Filtre, katman ve tekrar politikası
Tüm çiftler için hook çağırmak ölçeklenmez. Collision groups / masks
veya hook girişinde erken return ile «kim kiminle konuşur»
tablosu yazılır. Aynı çift için begin’in her adımda tekrarlanması
«çift skor» üretir; idempotent begin veya aktif temas seti kullanın.
| Çift | Hook | Not |
|---|---|---|
| player + pickup | begin → envanter | end gerekmez |
| player + hazard | stay seyrek → hasar | i-frame politikası |
| dekor + dekor | yok | mask ile kapat |
Filtre tablosunu büyük oyunlarda katman sayısı arttıkça (oyuncu, düşman, mermi, dekor, tetikleyici hacim) bir matris olarak tutmak faydalıdır: satır ve sütun kesişimi «bu ikisi birbirini görür mü» sorusuna tek bakışta cevap verir.
Güvenli tepki: simüle hook’lar
Örnekler eğitim amaçlıdır; motor event API’sini sürümünüzde doğrulayın. Hook içinde kullanıcı string’inden gövde tipi türetmeyin.
Simüle · begin/end registry ve response dispatch.
// Eğitim: motor event isimleri sürüme göre değişir — yüzey örnektir.
const activePairs = new Set()
function pairKey(a, b) {
return a < b ? `${a}|${b}` : `${b}|${a}`
}
const RULES = [
{ tags: ['player', 'pickup'], onBegin(a, b, ctx) { ctx.collectPickup(b) } },
{ tags: ['player', 'hazard'], onStay(a, b, ctx) { ctx.applyDamage(1) } },
]
function tagsFor(id, registry) {
return registry.get(id)?.tags ?? []
}
function matchRule(tagsA, tagsB) {
const sa = new Set(tagsA)
const sb = new Set(tagsB)
return RULES.find((r) => r.tags.every((t) => sa.has(t) || sb.has(t)))
}
export function onCollisionBegin(idA, idB, registry, ctx) {
const key = pairKey(idA, idB)
if (activePairs.has(key)) return
activePairs.add(key)
const rule = matchRule(tagsFor(idA, registry), tagsFor(idB, registry))
rule?.onBegin?.(idA, idB, ctx)
}
export function onCollisionEnd(idA, idB) {
activePairs.delete(pairKey(idA, idB))
}
/** Motor callback'inden çağrılır — impulse hesabı burada yok */
export function bridgePhysicsContact(event, registry, ctx) {
const a = event.bodyA?.userData?.id ?? event.colliderA?.id
const b = event.bodyB?.userData?.id ?? event.colliderB?.id
if (!a || !b) return
if (event.type === 'start') onCollisionBegin(a, b, registry, ctx)
if (event.type === 'end') onCollisionEnd(a, b)
}
Simüle · Rapier event listener yüzeyi (pseudo).
import { bridgePhysicsContact } from './collisionBridge.js'
// Rapier: EventQueue + drainContacts — sürümde isimleri doğrulayın
export function wireRapierCollisions(world, eventQueue, registry, ctx) {
// world.step(eventQueue) sonrası:
eventQueue.drainCollisionEvents((handle1, handle2, started) => {
bridgePhysicsContact(
{
type: started ? 'start' : 'end',
colliderA: { id: registry.idFromHandle(handle1) },
colliderB: { id: registry.idFromHandle(handle2) },
},
registry,
ctx
)
})
}
// Cannon: addEventListener('collide', ...) benzeri yüzey
export function wireCannonCollisions(world, registry, ctx) {
world.addEventListener?.('collide', (e) => {
bridgePhysicsContact(
{
type: 'start',
bodyA: e.bodyA,
bodyB: e.bodyB,
},
registry,
ctx
)
})
}
Tepki tuzakları
Bu tuzakların ortak noktası, motorun «bozuk» olduğu yanılgısıdır; kök neden neredeyse her zaman hook tarafında bir eksik politikadır.
- Her stay’de skor: begin idempotency yok.
- Hook’ta transform itme: motor çözümüyle savaş.
- Picking karışımı: tıklama = çarpışma sanmak.
- Filtre yok: dekor çiftleri CPU yer.
- Ağır iş adım içinde: fizik frame şişer.
- Sunucu güveni: client collision ile yetki (Hexovian).
- Impulse yeniden yazımı: motor işi tekrarı.
Üç tepki profili
Üç profil de aynı begin/stay/end sözleşmesini paylaşır; ayrışan şey hangi fazın dinlendiği ve hook'un ne kadar sık tetiklendiğidir.
-
01 · Tepki
Pickup toplama
- Faz
- begin only
- Hook
- envanter + despawn
- Risk
- Çift begin
-
02 · Tepki
Hazard alanı
- Faz
- stay seyrek
- Hook
- hasar + i-frame
- Risk
- Her adım hasar
-
03 · Tepki
Fizik + ses
- Faz
- begin
- Hook
- one-shot SFX
- Risk
- stay spam ses
Holodepth perspektifleri
Hook ince tutulur
Tepki köprüsü ince kalır; ağır sistemler başka faz veya Hexovian netcode katmanına taşınır.
Idempotency varsayılan olsun
Her begin çağrısının en fazla bir kez etki üretmesi, çift skor ve çift ses gibi hataların en yaygın kaynağını kapatır.
Teşhis maddeleri
- begin / stay / end politikası yazılı mı?
- Aktif temas seti çift skoru engelliyor mu?
- Hook fizik adımını şişiriyor mu?
- Picking ile karışmış mı?
- Mask / filtre var mı?
- Netcode beklentisi Hexovian’a mı kaymış?
Holodepth içgörüsü
«Çarpışma yanlış» çoğu zaman motor değil; hook her çifti dinliyor veya picking ile karışmıştır. Önce filtre ve faz politikasını okuyun.
Sonraki kapı: sabit timestep & performans
Tepki hook’ları oturunca kalan risk variable timestep ve kare bütçesidir. Sonraki sayfa Sabit timestep & performans accumulator ve Platform FPS bağlantısını anlatır.
Erişilebilirlik ve geri bildirim
Yalnızca fizik temasına bağlı kritik bilgi (hasar, görev) için alternatif geri bildirim (DOM, ses, haptic politika) düşünün. Holodepth: çarpışma hook’u görsel flash tek başına yeterli sayılmasın; state UI’da da okunur olsun.