holodepth

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:

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.

Çarpışma çifti · ürün notu
Ç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.

  1. Her stay’de skor: begin idempotency yok.
  2. Hook’ta transform itme: motor çözümüyle savaş.
  3. Picking karışımı: tıklama = çarpışma sanmak.
  4. Filtre yok: dekor çiftleri CPU yer.
  5. Ağır iş adım içinde: fizik frame şişer.
  6. Sunucu güveni: client collision ile yetki (Hexovian).
  7. 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

  1. begin / stay / end politikası yazılı mı?
  2. Aktif temas seti çift skoru engelliyor mu?
  3. Hook fizik adımını şişiriyor mu?
  4. Picking ile karışmış mı?
  5. Mask / filtre var mı?
  6. 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.