holodepth

HTML5 Canvas · Oyun mantığı ve çarpışma

Daire çarpışması: yuvarlak vuruş ve mesafe sınaması

Daire ( disk ), merkez (x, y) ve yarıçap r ile tanımlanır; çarpışma testi düzlemde mesafeye indirgenir. Canvas 2D prototiplerde top, mermi, tekerlek silueti ve dönen sprite için yuvarlak primer çarpışma modeli sık seçilir — dikdörtgen kutu ile köşe artefaktları istemiyorsanız daire, görsel ile mantıksal sınırın hizalanmasını kolaylaştırır. Aynı düzlemde eksen hizalı kutular AABB çarpışması sayfasında; bu sayfa daire–daire ve daire–kutu köprüsünü sabitler.

Hareket entegrasyonu ve sürtünme Hız ve hareket ile; zaman adımı Delta time ile birlikte düşünülmelidir — çarpışma geometrisini çözdükten sonra hız güncellemesi ayrı katmandır — Temel fizik ve Oyun durumu mantığı komşu başlıklardadır.

Özet: daire temsili

Alan Anlam Canvas notu
x, y Merkez ( dünya veya ekran öncesi uzay) Çizimde arc merkezi ile aynı olmalıdır
r Yarıçap ( bitmap / dünya birimi) lineWidth yarı genişliği dışarı taşmaz; kontur gereksinimi ayrı hesap
Kare mesafe karşılaştırması Math.sqrt maliyetini sınamada sıkça kaçının

Temsil: merkez, yarıçap ve tuval birimi

Üretimde küçük bir nesne { x, y, r } sözleşmesi tutun; yarıçap ile çapı ( 2r ) karıştırmak çok yaygın bir hatadır. Sprite çizimi merkezde değil köşede başlıyorsa, sanat kökenli offset’i tek import fonksiyonunda merkeze taşıyın — AABB · kutu temsilindeki çıpa uyarısı burada da geçerlidir.

Yuvarlak «görsel» keskin piksel hatlı olabilir; çarpışma dairesi ile görünen piksel sınırı birebir olmak zorunda değildir — oyun tasarımı gevşek veya sıkı vuruş alanı seçer. Debug için yarı saydam halka çizmek AABB’deki gibi değerlidir.

ctx.arc(x, y, r, …) ile doldurma, merkez ve yarıçapı doğrudan kullanır; ince kontur çiziyorsanız yarıçapın yarısı kadar dışarı taşan hattı çarpışmaya dahil etmek isteyip istemediğinize karar verin — çoğu oyunda çarpışma yine merkez / r üzerinden kalır, kontur salt görseldir.

Daire–daire: kare mesafe ve örtüşme

İki dairenin merkezleri dx, dy ile ayrılmış olsun; örtüşme için dx² + dy² ≤ (r₁ + r₂)² yeterlidir. Karşılaştırmadan önce kare kök almıyorsanız hem dal sayısı hem de sayısal profil iyileşir — yön vektörü gerektiğinde tek sefer Math.hypot(dx, dy) veya Math.sqrt çağrısı yapılır.

Kenar teması ( tam değme ) için < veya seçimi AABB sayfasındaki gibi takım kararıdır. Yumuşak çarpışma ( soft contact ) eklemek için eşiği küçük bir epsilon ile genişletmek titremeyi azaltabilir; epsilon politikasını tek yerde toplayın.

Bir daire diğerinin tamamını kapsıyorsa yine aynı eşitsizlik geçerlidir; ekstra «içerde mi» dalı gerekmez — gövde büyüklüğü farklı olduğunda tam içerme için ayrıca d + rküçük ≤ rbüyük ( merkez mesafesi + küçük yarıçap ) gibi kural tabanlı testler yazılabilir.

/** { x, y, r } daireleri. Örtüşme yoksa mesafe karesi hesaplanmaz (isteğe bağlı erken çıkış). */
function overlapsCircles(a, b) {
  const dx = b.x - a.x;
  const dy = b.y - a.y;
  const pr = a.r + b.r;
  return dx * dx + dy * dy <= pr * pr;
}

Ayırma: normal, merkez çakışması ve gömülülük

Örtüşme derinliği yaklaşık olarak pen = r₁ + r₂ - d ( d merkez mesafesidir ). Birim normal, merkezlerden biri diğerine doğru: nx = dx / d, ny = dy / d. Eşit kütlede her daireyi yarı gömülülük kadar itmek simetrik çözümdür; bir gövde kinematikse tüm itme diğerine gider. Kütle oranı biliniyorsa itişleri ters kütleyle orantılı paylaştırmak ( inverse mass ağırlığı ) daha tutarlıdır — örnek kod ( circle-separate-equal.js ) basit eşitlik varsayar; üretimde oranı parametre yapın.

Konum düzeltmesi ile hız düzeltmesini karıştırmayın: önce gömülülüğü kapatın, sonra normal yönde hız bileşenini güncelleyin — aynı karede ikisini birbirine bağlı çözmek titreşim üretir.

d === 0 veya çok küçük olduğunda yön belirsizdir — rastgele eksen veya son kare hız vektörü ile kırılma kullanın; aksi halde NaN yayılır. Bu özel durum üretimde sık görülür ( üst üste spawn, snap ). Örnekteki sabit eksen ( (1, 0) ) deterministik ve ucuzdur; deterministik olmayan oyunlarda spawn sırasını kaydırmak bazen daha doğal çözüm olur.

/**
 * Eşit kütle: örtüşmeyi iki yarıya böler. Örtüşme yoksa aynı nesneleri döner.
 */
function separateCirclesEqualMass(a, b) {
  const dx = b.x - a.x;
  const dy = b.y - a.y;
  const minD = a.r + b.r;
  const d2 = dx * dx + dy * dy;
  if (d2 >= minD * minD) return { a, b };

  let d = Math.sqrt(d2);
  let nx = dx;
  let ny = dy;
  if (d < 1e-8) {
    nx = 1;
    ny = 0;
    d = 1;
  } else {
    nx /= d;
    ny /= d;
  }

  const pen = minD - d;
  const half = pen * 0.5;
  return {
    a: { ...a, x: a.x - nx * half, y: a.y - ny * half },
    b: { ...b, x: b.x + nx * half, y: b.y + ny * half },
  };
}

Daire–AABB: kutuya en yakın nokta

Eksen hizalı kutunun içinde veya kenarında, daire merkezine en yakın nokta clamp ile bulunur; merkez bu noktaya olan vektörün uzunluğu r’den küçük veya eşitse çakışma vardır. Bu, daire–dikdörtgen testinin standart «ucuz» yoludur; köşede doğru çalışır. Merkez kutunun içindeyse en yakın nokta merkezin kendisidir; sınamada mesafe sıfırdır ve çarpışma örtüşme yarıçapıyla belirlenir — «içte takılı kaldım» durumu başka bir şeydir ( genelde ayırma eksikliği ).

Kutu temsilini AABB · çakışma sınaması ile aynı min/max sözleşmesinde tutun. Sıfır kalınlıkta çizgi duvarlar için kutu yüksekliği / genişliği sıfıra düşerse test bozulur; çarpışma hacmini en az birkaç dünya birimi kalınlıkta tutmak pratikte daha dayanıklıdır.

Oyuncu daire, dünya karo kutularıyla etkileşiyorsa önce karo uzayından AABB üretin, sonra bu testi uygulayın — geometri katmanı tek kalır. Yalnız örtüşme yeterliyse aşağıdaki fonksiyon kafi; kutudan «iterek çıkarma» istiyorsanız en yakın yüzey normallerine göre daireyi dışarı kaydıran küçük bir adım ( AABB çözünürlük düşüncesinin dairesel karşılığı ) ek katmandır.

function clamp(v, lo, hi) {
  return Math.max(lo, Math.min(hi, v));
}

/** box: { minX, minY, maxX, maxY }. c: { x, y, r } */
function overlapsCircleAABB(c, box) {
  const qx = clamp(c.x, box.minX, box.maxX);
  const qy = clamp(c.y, box.minY, box.maxY);
  const dx = c.x - qx;
  const dy = c.y - qy;
  return dx * dx + dy * dy <= c.r * c.r;
}

Hız, teğet ve sürtünme: geometri sonrası

Çarpışma düzleminde normal n bilindiğinde, hızın normal bileşeni yansıtma veya sünekliğe ( restitution ) tabidir; teğet bileşen sürtünme ile süzülür. Bu sayfa skaler çarpışma ve konum düzeltmesine odaklanır; vektör cebiri detayı Temel fizik ve entegrasyon Hız ve hareket ile birleştirilir — önce iç içe geçmeyi çözün, sonra hızı güncelleyin. Çok düşük göreli hızda sürtünme tam kaydırmayı durdurabilir; istifleme ( stacking ) sorunları çoğu zaman çözüm sırası ve epsilon ile ilgilidir, saf geometri ile çözülmez.

Çoklu çarpışmada daireler zincir oluşturduğunda tek geçişlik ayırma yeterli olmayabilir; AABB · çözünürlük bölümündeki iterasyon ve sıra uyarısı burada da geçerlidir. Oyuncu–top–duvar üçgeninde önce duvarı çözüp sonra top–oyuncu sırası vermek, beklenmedik sıçramayı azaltır — tamamen tasarım kararı.

Ölçek, tünel ve geniş faz

Hızlı mermi ince duvarı tek karede atlayabilir; daire testi örnekleme anlarında yapılıyorsa tünel riski AABB ile aynı sınıftadır — Delta time küçültme, alt adım veya süpürme ürün kararıdır — AABB · tünel ile hizalı düşünün. Bazı projelerde yalnız kritik gövdeler ( oyuncu ) için sabit alt adım, mermiler için süpürme / genişleştirilmiş hit kullanılır — karma strateji kabul edilebilir.

Çok daire için ikili döngü O(n²)’dir; karo ızgarası veya uzamsal hash ile adayları düşürün — politika AABB · geniş faz bölümündeki gibi seçilir. Daire–daire çiftleri için geniş faz yine aynı hücre listelerinden beslenebilir; kutu ve daireyi aynı dünya hücre anahtarına düşürmek boru hattını sade tutar.

Anti-kalıplar: çap ve kök maliyeti

Çap / yarıçap karışıklığı: İki katı mesafe ile kıyaslama yapmak sürekli yanlış pozitif / negatif üretir. Çözüm: tek modülde r tanımı ve testlerde hep r₁ + r₂.

Her çiftte sqrt: Önce kare mesafe ile eleme; yön için tek kök. Örtüşme yoksa kök çağırmayın — özellikle geniş fazdan sonra dar liste için fark edilir.

d ≈ 0 yok saymak: Üst üste spawn’da normal üretilemez; korumalı eksen seçin veya spawn noktasını birbirinden uzaklaştırın.

Merkez hizasız sprite: Çizimle çarpışma farklı çıpa kullanıyor; tek import’ta merkeze taşıyın ( Temsil ).

Önce hız, sonra konum: Gövde hâlâ iç içeyken impuls uygulamak enerji biriktirir; sırayı tersine çevirin ( Geometri sonrası ).

Bu sayfanın sınırı

Elips, sürekli çarpışma tespiti ( GJK ) veya fizik motoru entegrasyonu burada işlenmez. Amaç: Canvas 2D’de daire ve basit kutu etkileşimini doğru kurmaktır.

  • { x, y, r } sözleşmesi tüm modüllerde aynı mı?
  • Kare mesafe önce, kök sonra politika uygulanıyor mu?
  • Merkez çakışması için korumalı normal var mı?
  • Hız güncellemesi ayırma sonrası mı geliyor?
  • n² çift listesi büyüdü mü; geniş faz planlandı mı?
  • Daire–kutu için sıfır kalınlık «çizgi duvar» kutusu üretilmedi mi?