T: tema · Z: kodu büyüt · S: konuşmacı notları · F: tam ekran · Esc: genel bakış · ⌂ ders haritası
OYUN GEOMETRİSİ VE FİZİĞİ · G5
Fizik Simülasyonunun Temeli
Euler, sabit zaman adımı, zıplama hissi, Verlet ve kısıtlar, itme — “bir sonraki karede bu nesne nerede olacak?”
PRESS ▶ TO START

Bu derste oyunun haritası 🗺️

  1. Hareketi adımlamak — konum, hız, ivme; iki satırın sırası neden her şeyi değiştirir (Euler)
  2. Sabit zaman adımı — aynı oyun her makinede aynı oynansın: biriktirici döngü
  3. Zıplama hissi — yerçekimini tasarımcı seçer; değişken yükseklik, çakal süresi, tampon
  4. Verlet ve kısıtlar — ip, kumaş, bez bebek (ragdoll): hız saklamadan simülasyon
  5. İtme — çarpışmadan sonra kim, ne hızla, nereye gider?
  6. Her adımda — elle izleme → canlı demo → kod (JavaScript / Python / C#) → gerçek oyun kaynak kodu
🎮 Bu dersin sonunda…

kendi hareket ve zıplama kodunuzu kare hızından bağımsız çalışacak şekilde yazabilecek; ip ve kumaş gibi kısıtlı sistemleri simüle edebilecek; Box2D, Quake III, Celeste ve Hitman’ın bu işleri nasıl yaptığını kod düzeyinde açıklayabileceksiniz.

🤔 ISINMA: AYNI OYUN, FARKLI BİLGİSAYAR, FARKLI ZIPLAMA

Quake III Arena’da (1999) usta oyuncular oyunu saniyede tam 125 kareye kilitlerdi. Çünkü 125 karede karakter, başka kare hızlarında çıkamadığı yerlere zıplayabiliyordu. Bir oyunda zıplama yüksekliği neden kare hızına bağlı olsun?

  • Bilgisayar zamanı sürekli akıtamaz; dünyayı adım adım ilerletir. Her adım küçük bir tahmindir: “hız bu kadar olduğuna göre şu kadar ilerlemişimdir.”
  • Adımın büyüklüğü tahminin hatasını belirler. Adım, karenin süresine eşitse hata da makineden makineye değişir.
  • Quake III’te adım tam milisaniyeye, hız tam sayıya yuvarlanıyordu; yuvarlama hatası bazı kare hızlarında oyuncunun lehine birikiyordu.
  • Bu sunum o tahminin nasıl yapıldığını, nerede yanıldığını ve iyi oyunların bunu nasıl dizginlediğini anlatıyor. Sonunda zıplamanın neden bazı oyunlarda “tam oturduğunu” da göreceğiz.

🎯 Bu konu için bilmeniz gerekenler

Yeşil = gerekli · kesikli = faydalı ama şart değil. Tıklayınca ilgili sunuma gider.

Neden?G3’te “çarpıştılar mı?” sorusunu cevapladık ve karakteri duvardan dışarı ittik. Burada bir adım geri gidiyoruz: nesneler iki çarpışma arasında nasıl hareket ediyor, çarpışınca hızları ne oluyor?
🧰 Kodlamadan korkuyorsanızBu sunumda türev ve integral işareti yok. Gereken tek fikir: “hız, konumun saniyedeki değişimidir; ivme, hızın saniyedeki değişimidir.” Bütün formüller toplama ve çarpma; hepsini sayılarla elle izleyeceğiz.

Mini sözlük

integration · integratorintegral alma · integratörHız ve ivmeden bir sonraki konumu hesaplama işi · bunu yapan yöntem
explicit / semi-implicit Euleraçık / yarı-örtük EulerKonumu eski hızla / yeni hızla güncelleyen iki adımlama yöntemi
timestep (dt) · fixed timestepzaman adımı · sabit zaman adımıSimülasyonun bir adımda ilerlettiği süre · bu sürenin kare hızından bağımsız, sabit tutulması
accumulatorbiriktiriciHenüz simüle edilmemiş gerçek zamanı tutan değişken
coyote time · jump bufferingçakal süresi · zıplama tamponuKenardan çıktıktan kısa süre sonra zıplayabilme · yere değmeden az önce basılan tuşun hatırlanması
Verlet integrationVerlet integraliHızı saklamadan, şimdiki ve önceki konumdan adımlama
constraint · relaxationkısıt · gevşetme“Bu iki nokta arası 20 piksel kalsın” gibi bir kural · kısıtları sırayla, tekrar tekrar düzeltme
ragdollbez bebekBayılan/ölen karakterin eklemli parçacık sistemi olarak yere yığılması
impulse · restitutionitme (impuls) · sekme katsayısıÇarpışmada hıza bir anda uygulanan değişiklik · çarpışmadan sonra ayrılma hızının yaklaşma hızına oranı
LEVEL 0
Hareketi adımlamak
konum, hız, ivme ve iki satırın sırası

Problem: bir sonraki karede top nerede?

🎮 Her oyun, her karede
  • Mario yürüyor, mermi uçuyor, araba kayıyor, top sekiyor. Hepsinin bir konumu ve bir hızı var.
  • Hız: konum saniyede ne kadar değişiyor? (piksel/sn)
  • İvme: hız saniyede ne kadar değişiyor? (piksel/sn²) Yerçekimi, motor, sürtünme, yay: hepsi ivme üretir.
  • Oyun döngüsü her karede aynı soruyu sorar: “son kareden beri dt saniye geçti; şimdi neredeyim?”
Neden formülü yazıp geçmiyoruz?

Okul fiziğinde serbest düşme için hazır formül var: y = v₀·t − ½·g·t². Oyunda işe yaramaz, çünkü:

  • Oyuncu her an tuşa basabilir; kuvvetler önceden bilinmez.
  • Çarpışmalar hareketi ortasından keser.
  • Yay, ip, rüzgâr, sürtünme: kuvvet konuma ve hıza bağlıysa hazır formül çoğu zaman yoktur.

Çare: zamanı küçük dilimlere böl; her dilimde “hız ve ivme bu dilim boyunca sabit” varsay. Bu varsayıma dayalı adımlamaya Euler yöntemi denir (Leonhard Euler, 1768).

Çizimle: aynı yay, iki sıralama, enerjiye ne oluyor?

01234024681012adımenerjigerçek enerji: hep 0,5açık Euler: her adımda artıyoryarı-örtük: salınıyor, büyümüyor0,6250,7810,9771,221

Bir yayın ucundaki kütle. Gerçekte enerjisi hiç değişmez (yeşil kesikli çizgi). Kırmızı: önce konumu, sonra hızı güncelleyen açık Euler; enerji her adımda büyüyor ve simülasyon sonunda “patlıyor”. Mavi: iki satırın yeri değişmiş hâli; enerji salınıyor ama kaçmıyor. Sonraki slayt ilk dört adımı sayılarla veriyor.

Fikir: iki satır, iki farklı sıra

Bir adımda iki güncelleme var. Hangisi önce?

açık (explicit) Euleryarı-örtük (semi-implicit) Euler
1. satırx ← x + v·dtv ← v + a·dt
2. satırv ← v + a·dtx ← x + v·dt
konum hangi hızla?eski hızlayeni hızla
enerjiadım adım artarsalınır, büyümez
  • Fark tek bir şey: konum güncellenirken hız değişkeninde adımın başındaki değer mi var, sonundaki mi?
  • Maliyet aynı: ikisi de iki çarpma, iki toplama.
  • Sonuç çok farklı: sağdaki sürüm yay, yörünge, sarkaç gibi “geri dönen” hareketlerde kararlı kalır.
Elle izleme: yay (ivme = −x), dt = 0,5

Başlangıç: x = 1, v = 0. Enerji = (x² + v²) / 2 = 0,500. Gerçekte enerji hiç değişmez.

adımaçık: x, venerjiyarı-örtük: x, venerji
11,00 · −0,500,6250,75 · −0,500,406
20,75 · −1,000,7810,31 · −0,880,432
30,25 · −1,380,977−0,20 · −1,030,552
4−0,44 · −1,501,221−0,67 · −0,930,655

Açık Euler’de enerji her adımda tam 1 + dt² = 1,25 katına çıkar: 20 adımda 87 kat. Yarı-örtük sürümde 200 adım boyunca 0,400 ile 0,667 arasında gidip gelir; hiç büyümez.

Canlı: üç yöntem, bir gezegen 🪐

  1. Ekranı okuyun: ortada sarı güneş; kesikli çember gerçek yörünge. Üç gezegen aynı yerden aynı hızla başlar: kırmızı açık Euler, yeşil yarı-örtük Euler, mavi Verlet (4. bölümde). Sağda her birinin toplam enerjisi ve başlangıca göre yüzde farkı.
  2. dt = 0,05 (başlangıç): kırmızı her turda biraz daha dışarı açılıyor. Enerjisi sürekli artı yönde büyüyor.
  3. dt = 0,005: üçü de çembere yapışık görünür; ama kırmızının yüzdesi yine yavaş yavaş artar. Hata küçüldü, yönü değişmedi.
  4. dt = 0,2: kırmızı birkaç turda “uçtu gitti”. Yeşil ve mavi köşeli ama kapalı bir yörüngede kalır; yüzdeleri artı-eksi salınır.
  5. Soru: yeşil ile kırmızının kodları arasında kaç satır fark var? (Sıfır. Aynı iki satır, ters sırada.)
🔎 DEMODA DENEYİN
  1. Ekranı okuyun: ortada sarı güneş; kesikli çember gerçek yörünge. Üç gezegen aynı yerden aynı hızla başlar: kırmızı açık Euler, yeşil yarı-örtük Euler, mavi Verlet (4. bölümde). Sağda her birinin toplam enerjisi ve başlangıca göre yüzde farkı.
  2. dt = 0,05 (başlangıç): kırmızı her turda biraz daha dışarı açılıyor. Enerjisi sürekli artı yönde büyüyor.
  3. dt = 0,005: üçü de çembere yapışık görünür; ama kırmızının yüzdesi yine yavaş yavaş artar. Hata küçüldü, yönü değişmedi.
  4. dt = 0,2: kırmızı birkaç turda “uçtu gitti”. Yeşil ve mavi köşeli ama kapalı bir yörüngede kalır; yüzdeleri artı-eksi salınır.
  5. Soru: yeşil ile kırmızının kodları arasında kaç satır fark var? (Sıfır. Aynı iki satır, ters sırada.)

🔗 Başka gözle: etkileşimli anlatımlar ve yazılar

Aynı konuyu başka bir anlatımla, kendi elinizle oynayarak görmek çok işe yarar. Kartlar yeni sekmede açılır; çoğu İngilizce ama görsel ve etkileşimli.

Kod: bir hareket adımı

🎒 Kodu okuyalım

ifadene demekneden böyle
p.vy += ay * dthız, ivme × süre kadar değişirİvme 900 piksel/sn² ve dt = 1/60 sn ise hız bu adımda 15 piksel/sn artar.
p.y += p.vy * dtkonum, hız × süre kadar değişirBu satır hız satırından sonra geldiği için yeni hızı kullanır: yarı-örtük Euler.
deltaTime / 1000son karenin süresi (saniye)p5.js milisaniye verir. Birimi saniyeye çevirmezseniz her şey 1000 kat hızlı olur.
* dt (her yerde)hız ve ivme saniye başınadır, kare başına değilp.x += 2 yazarsanız top 144 Hz ekranda 60 Hz ekrandakinin 2,4 katı hızla gider.
Python: acik_euleraynı iki satır, ters sıraKonum eski hızla ilerler. Yay örneğinde çıktı slayttaki tablonun kırmızı sütunu.
ÖnermeBir Euler adımının hatası dt² ile, belli bir süre boyunca biriken toplam hata dt ile orantılıdır: adımı yarıya indirmek hatayı yarıya indirir ama işi iki katına çıkarır. Açık Euler’de bu hata yay benzeri sistemlerde hep enerji ekleme yönündedir; yarı-örtük Euler’de enerji gerçek değerin çevresinde sınırlı kalır (dt, salınım periyoduna göre küçük oldukça).

Oyunlarda: Box2D’nin iki satırı

Kaynak: Erin Catto, Box2D v2.4.1, b2_island.cpp. Unity’nin 2B fiziği ve binlerce bağımsız oyun (Angry Birds, Limbo, Hollow Knight’ın motoru…) bu kütüphaneyi kullanır.

🎮 Satır satır
  • v += h * … * (… gravity + b->m_force): önce hız. Parantezin içi “kuvvet”, m_invMass = 1/kütle; çarpımları ivme.
  • c += h * v: sonra konum, yeni hızla. Yani slayttaki yeşil sütun: yarı-örtük Euler.
  • İki satırın arasında çarpışmalar ve eklemler çözülür: motor önce hızları “yasal” hâle getirir, konumu ondan sonra ilerletir. Bu yüzden sıra başka türlü olamaz.
  • v *= 1 / (1 + h * damping): sönüm. Hızı her adım biraz kısar; çarpan asla eksiye düşmediği için büyük dt’de bile patlamaz.
  • Box2D belgeleri: adım olarak 1/60 sn önerilir ve “zaman adımını kare hızınıza bağlamayın” denir. Neden? Sıradaki bölüm.

🌱 Isınma kodu: bir hareket adımı

İki satır: v = v + a * dt; ve x = x + v * dt;, sonra return [x, v];. Sırayı ters yazarsanız (önce konum) ilk test “beklenen 0,1; sizinki 0” diye uyarır: o, açık Euler’dir. Örnek: x = 0, v = 0, a = 10, dt = 0,1 → hız 1, konum 0,1.
Quiz 1 · hareket
Bir uzay oyununda gezegenlerin yörüngeleri dakikalar içinde yavaş yavaş genişliyor ve gezegenler sonunda sistemden kopuyor. Kodda konum += hiz * dt; hiz += ivme * dt; yazıyor. En ucuz düzeltme?
Bu açık Euler: her adımda sisteme biraz enerji ekler, yörünge sarmal çizer. Satırların yerini değiştirince (önce hız, sonra konum) yarı-örtük Euler olur ve enerji sınırlı kalır; maliyet aynı. dt’yi küçültmek kopmayı yalnızca geciktirir. Sönüm (0,99) kopmayı önleyebilir ama bu kez yörüngeler içeri doğru çöker.
LEVEL 1
Sabit zaman adımı
aynı oyun her makinede aynı oynansın

Problem: zıplama yüksekliği kare hızına bağlı

🎮 dt = “kare ne kadar sürdüyse”

En yaygın ilk çözüm: her karede, o karenin süresi kadar tek bir adım at. Hızlı makinede küçük adımlar, yavaş makinede büyük adımlar.

Zıplama: ilk hız 10 m/sn, yerçekimi 20 m/sn². Gerçek tepe yüksekliği v₀² / 2g = 2,50 m.

kare hızıdtyarı-örtük Euleraçık Euler
10 FPS100 ms2,00 m3,00 m
30 FPS33 ms2,33 m2,67 m
60 FPS17 ms2,42 m2,58 m
144 FPS7 ms2,47 m2,53 m

Hata tam olarak v₀·dt / 2: adım büyüdükçe tepe yarı-örtük sürümde alçalır, açık sürümde yükselir.

Sonuç: oyun herkeste farklı
  • Seviye tasarımcısı platformu 2,4 metreye koydu. 60 karede çıkılıyor, 30 karede çıkılamıyor.
  • Kare hızı oyun içinde de oynar: patlama anında kare 50 ms sürer, karakter o an farklı zıplar.
  • Çarpışma: büyük adımda hızlı nesne ince duvarı atlar (G3: tünelleme). Duvarın tutup tutmayacağı kare hızına bağlı olur.
  • Yaylar ve eklemler büyük adımda patlar: bir takılma, bütün yığını havaya uçurur.
  • Ağ oyunu ve tekrar kaydı (Y18): aynı girdiler iki makinede aynı sonucu vermez.

Sabit ivmede ½·a·dt² terimini ekleyerek tepeyi düzeltebilirsiniz; ama sürtünme, yay ve çarpışma girince o da yetmez. Kalıcı çözüm adımı sabitlemek.

Çizimle: kareler düzensiz, fizik adımları eşit

kareler(süre değişken)fizik adımları(hep 16,7 ms)20 mskare 120 mskare 240 mstakılma10 mskare 45 mskare 5adım 1adım 2adım 3adım 4adım 5kasada11,7 msbu karede 1 adımbu karede 1 adımbu karede 2 adım1 adım0 adım95 ms’lik gerçek zamanda 5 eşit fizik adımı atıldı (83,3 ms); artan 11,7 ms bir sonraki kareye devrediyor

Üst sıra ekran kareleri: kimi 20 ms sürüyor, kimi takılıp 40 ms. Alt sıra fizik: her zaman 16,7 ms’lik eşit adımlar. Her kare, o ana kadar birikmiş sürenin içine kaç tam adım sığıyorsa o kadar adım atar; artan süre kasada bekler. Sonuç: oyun her makinede aynı oynanır.

Fikir: zamanı biriktir, eşit dilimlerle harca

  • Fizik her zaman aynı adımla ilerler: örneğin DT = 1/60 sn. Kare ne kadar sürerse sürsün.
  • Bir biriktirici tutulur: “gerçekte geçmiş ama henüz simüle edilmemiş” süre.
  • Her karede: karenin süresini biriktiriciye ekle; içinde kaç tam DT varsa o kadar fizik adımı at; kalanı sonraki kareye devret.
  • Yavaş makine karede 2–3 adım atar; hızlı makine bazı karelerde hiç atmaz. İkisi de aynı sürede aynı sayıda, aynı büyüklükte adım atmış olur.
  • Benzetme: saatlik ücretle çalışan biri. Dakikalar birikir; her tam saat dolduğunda bir ödeme yapılır, artan dakikalar ertesi güne kalır.
Elle izleme: DT = 16,7 ms (1/60 sn)
kare süresibiriken (önce)atılan adımkalan
20 ms0 + 20 = 20,013,3
20 ms3,3 + 20 = 23,316,7
40 ms (takılma)6,7 + 40 = 46,7213,3
10 ms13,3 + 10 = 23,316,7
5 ms6,7 + 5 = 11,7011,7

95 ms’de 5 adım (83,3 ms) atıldı; 11,7 ms kasada bekliyor. Takılan karede fizik iki adım attı: adım büyümedi, sayısı arttı.

Kalan ne işe yarar? alfa = kalan / DT (burada 11,7 / 16,7 = 0,70): çizimi son iki fizik durumunun %70’inden yaparsanız hareket, fizik 60 Hz’te kalsa bile 144 Hz ekranda pürüzsüz görünür.

Canlı: değişken adım mı, sabit adım mı? ⏱️

  1. Ekranı okuyun: iki top aynı ilk hızla zıplıyor. Soldaki (kırmızı) her karede kare süresi kadar tek adım atıyor; sağdaki (yeşil) biriktiriciyle hep 1/120 sn’lik adımlar atıyor. Sarı kesikli çizgi 2,4 m’deki platform; gri çizgi gerçek tepe (2,50 m). Altlarında son zıplamanın tepe yüksekliği.
  2. 60 FPS (başlangıç): ikisi de platforma çıkıyor (sol 2,42; sağ 2,46).
  3. Kare hızını 30 yapın: sol 2,33’e düşer, “platforma ÇIKAMAZ”. Sağ aynı: 2,46. 10 yapın: sol 2,00; sağ yine 2,46 (yalnızca görüntüsü kesik kesik).
  4. Yöntemi açık Euler yapın: hata bu kez ters yönde; yavaş makinede sol top 3 metreye çıkar. Oyuncular bunu hemen bulur ve kare hızını bilerek düşürür.
  5. ⚡ takılma düğmesi tek bir 300 ms’lik kare gönderir: sol top bir anda ışınlanır (ve yere gömülüp zıplamayı kaçırabilir); sağ top 36 küçük adım atıp yoluna devam eder.
🔎 DEMODA DENEYİN
  1. Ekranı okuyun: iki top aynı ilk hızla zıplıyor. Soldaki (kırmızı) her karede kare süresi kadar tek adım atıyor; sağdaki (yeşil) biriktiriciyle hep 1/120 sn’lik adımlar atıyor. Sarı kesikli çizgi 2,4 m’deki platform; gri çizgi gerçek tepe (2,50 m). Altlarında son zıplamanın tepe yüksekliği.
  2. 60 FPS (başlangıç): ikisi de platforma çıkıyor (sol 2,42; sağ 2,46).
  3. Kare hızını 30 yapın: sol 2,33’e düşer, “platforma ÇIKAMAZ”. Sağ aynı: 2,46. 10 yapın: sol 2,00; sağ yine 2,46 (yalnızca görüntüsü kesik kesik).
  4. Yöntemi açık Euler yapın: hata bu kez ters yönde; yavaş makinede sol top 3 metreye çıkar. Oyuncular bunu hemen bulur ve kare hızını bilerek düşürür.
  5. ⚡ takılma düğmesi tek bir 300 ms’lik kare gönderir: sol top bir anda ışınlanır (ve yere gömülüp zıplamayı kaçırabilir); sağ top 36 küçük adım atıp yoluna devam eder.

Kod: sabit adımlı oyun döngüsü

🎒 Kodu okuyalım

ifadene demekneden böyle
biriken += karegerçek zamanı kasaya koyFizik gerçek zamanı doğrudan görmez; yalnızca kasadan DT’lik dilimler çeker.
while (biriken >= DT)kasada tam bir adımlık süre varsaif değil while: uzun karede birden çok adım atılmalı.
fizikAdimi(simdiki, DT)fizik hep aynı sayıyla çağrılırSabit DT = her makinede aynı hata = aynı zıplama. Aynı girdiler aynı sonucu verir (tekrar kaydı, ağ oyunu).
if (kare > 0.25) kare = 0.25tek karede en çok 0,25 sn simüle etÖlüm sarmalı: fizik adımı DT’den uzun sürerse her kare daha çok adım ister, kare daha da uzar… Kırpınca oyun yavaşlar ama kilitlenmez.
alfa = biriken / DTiki fizik adımı arasında neredeyiz? (0–1)Çizim, son iki durumun karışımı: onceki + (simdiki − onceki) · alfa. Bedeli: görüntü en çok bir fizik adımı kadar geriden gelir.
Unity: Update / FixedUpdatekare başına / fizik adımı başınaTuş basışını Update’te yakalayın: 144 FPS’te bazı karelerde FixedUpdate hiç çalışmaz, basış kaçar.
ÖnermeSabit adımda t saniyelik oyun, kare hızından bağımsız olarak tam ⌊t / DT⌋ fizik adımı içerir. Fiziğin maliyeti kare hızıyla değil, yalnızca DT ile belirlenir; çizim hızı ile simülasyon hızı birbirinden ayrılır.

Oyunlarda: Quake III ve “125 FPS” zıplaması

Kaynak: id Software, Quake III Arena, bg_pmove.c (GPL). Yorum satırları özgün koddan.

🎮 Ne oluyor?
  • Yorumdaki niyet açık: “kare hızına bağlı davranışı önlemek için” uzun hareketi parçalara böl. Ama parça sabit değil: en çok 66 ms, yoksa kare ne kadarsa o.
  • Adım süresi tam milisaniye: 125 karede 8 ms, 90 karede 11 ms. Her adımın sonunda hız, ağ trafiğinden tasarruf için tam sayıya yuvarlanıyor.
  • Yerçekimi 800 birim/sn²: 8 ms’lik adımda hızdan 6,4 birim düşer; yuvarlama her adımda bu sayının kesrini değiştirir. Hangi yöne ve ne kadar yuvarlandığı adım süresine bağlı: bazı kare hızlarında oyuncu birkaç birim daha yükseğe çıkar.
  • Oyuncular 125’i (ve 333’ü) buldu; bazı zıplamalar yalnızca bu hızlarda yapılabiliyordu. Sonraki yamalarda pmove_fixed geldi: herkes için aynı adım (varsayılan 8 ms).
  • Ders: “dt ile çarptım, kare hızından bağımsız oldu” demek yetmez. Adım değişkense sonuç da değişkendir.

Motorlar hangi adımı kullanıyor?

motor / oyunfizik adımınot
Unity0,02 sn (50 Hz) — Time.fixedDeltaTimeFixedUpdate bu adımla çağrılır; “Maximum Allowed Timestep” ölüm sarmalına karşı kırpma.
Godot60 Hz — _physics_processProje ayarı: “physics ticks per second”. 4.3’ten beri yerleşik ara değerli çizim seçeneği.
Unrealvarsayılan: kare süresi (değişken)İstenirse “substepping” ve sabit adımlı “async physics” açılır.
Box2D1/60 sn önerilirBelgelerdeki öneri: hız için 8, konum için 3 çözücü tekrarı.
Celeste60 Hz, sabitOyunun bütün mantığı kare sayısıyla tutarlı; hız koşucuları (speedrun) kare kare çalışabiliyor.
Quake IIIdeğişken (en çok 66 ms) → pmove_fixed ile 8 msÖnceki slayt.
Dövüş oyunları60 Hz, kare kilitliHamleler “kare” ile ölçülür (“bu yumruk 3 karede çıkar”); ağ kodu (rollback) aynı girdinin aynı sonucu vermesine dayanır.

Ortak nokta: ciddi her fizik döngüsü ya adımı sabitler ya da üst sınır koyar. Bazı oyunlarda fizik karelere kilitlidir ve 60 karenin üstüne çıkınca nesneler tuhaf davranır; “kare sınırını açınca oyun bozuluyor” şikâyetlerinin kaynağı çoğu zaman budur.

🧪 Canlı laboratuvar: biriktirici

Başlangıç kodu her karede bir adım atar: kareler 7 ms ile 100 ms arasında değiştiği için mavi top yeşil halkanın gerisinde kalır ve tepe yüksekliği tutmaz. Üç satır: biriken += kare; · const n = Math.floor(biriken / DT); · return [n, biriken - n * DT];. Örnek: biriken 12, kare 16, DT 20 → toplam 28 → [1, 8].
Quiz 2 · zaman adımı
Fizik bulmaca oyununuz sizin 144 Hz ekranlı bilgisayarınızda sorunsuz. Bir oyuncunun eski dizüstünde (25–30 FPS) üst üste dizili kutular titreyip dağılıyor. En olası sebep ve çözüm?
Yavaş makinede adım 35–40 ms’ye çıkıyor; yığınlar ve eklemler büyük adımda kararsızlaşır. Sabit adımla o makine karede iki-üç küçük adım atar ve simülasyon sizdekiyle aynı davranır. Grafik kalitesini düşürmek belirtiyi hafifletebilir ama oyun yine makineye bağlı kalır.
LEVEL 2
Zıplama hissi
yerçekimini fizik kitabı değil, tasarımcı seçer

Problem: “gerçekçi” zıplama kötü hissettirir

İlk akla gelen: g = 9,8 ve bir “zıplama kuvveti”
  • İki sayı deneme yanılmayla ayarlanır: yercekimi = 9.8, ziplamaGucu = 5… Birini değiştirince hem yükseklik hem süre değişir; hiçbiri doğrudan “hissi” anlatmaz.
  • Gerçek dünyada 1 metrelik bir zıplama yaklaşık 0,9 saniye sürer. Oyunda bu ağır çekim gibidir: karakter havada asılı kalır, kontrol gecikir.
  • Seviye tasarımcısının sorusu başka: “karakter kaç karo yükseğe ve kaç karo uzağa zıplıyor?” Boşlukları ve platformları buna göre dizer.
🎮 Oyunlardaki zıplamalar
  • Mario kendi boyunun 4–5 katı yükseğe zıplar ve bunu yarım saniyeden kısa sürede yapar: yerçekimi dünyanınkinin katlarca fazlasıdır.
  • Tuşa kısa basınca alçak, uzun basınca yüksek zıplar: gerçek dünyada karşılığı yok.
  • Yükselişten daha hızlı düşer: yay simetrik değildir.
  • Platformdan çıktıktan sonra bile kısa bir an zıplayabilir.

Hiçbiri fizik değil; hepsi tasarım. Ama hepsi az önceki iki satırlık Euler adımının üstüne kurulur.

Çizimle: üç zıplama, üç his

012340 sn0,4 sn0,8 sn1,2 sn1,6 snyükseklik(karo)h = 4, t = 0,4 sn→ g = 50, v₀ = 20 (standart)h = 4, t = 0,8 sn→ g = 12,5, v₀ = 10 (“ay”: süzülüyor)h = 2, t = 0,2 sn→ g = 100, v₀ = 20 (“çevik”)

Tasarımcı “ne kadar yükseğe” ve “tepeye kaç saniyede” diye iki sayı seçer; yerçekimi ve ilk hız bunlardan hesaplanır. Aynı yüksekliğe iki kat sürede çıkan mor eğri ay yürüyüşü gibi süzülür; kırmızı eğri kısa ve serttir.

Fikir: yüksekliği ve süreyi seç; gerisini hesapla

Tasarımcı iki şey söyler: tepe yüksekliği h ve tepeye varış süresi t. Bize yerçekimi g ve ilk hız v₀ lazım.

  • Tepede hız sıfırdır. Hız her saniye g kadar azaldığına göre: v₀ = g · t.
  • Hız v₀’dan 0’a düzgün azalır; ortalaması v₀ / 2. Gidilen yol: h = (v₀ / 2) · t.
  • İkinciden: v₀ = 2h / t. Birincide yerine koy: g = 2h / t².

$$g = \frac{2h}{t^2} \qquad v_0 = \frac{2h}{t}$$

Yatay hız v ise tepe, kalkıştan v · t uzaktadır: tasarımcı “tepe 3 karo ileride olsun” da diyebilir (t = 3 / v). Bu yöntemi Kyle Pittman GDC 2016’daki “Building a Better Jump” konuşmasında anlattı (Videolar).

Elle hesap
istekg = 2h / t²v₀ = 2h / t
h = 4 karo, t = 0,4 sn8 / 0,16 = 508 / 0,4 = 20
h = 4 karo, t = 0,8 sn (“ay”)8 / 0,64 = 12,58 / 0,8 = 10
h = 2 karo, t = 0,2 sn (“çevik”)4 / 0,04 = 1004 / 0,2 = 20
h = 1 m, t = 0,45 sn (dünya)2 / 0,2 = 9,92 / 0,45 = 4,4

Aynı yükseklik, yarı süre → yerçekimi dört katı. “Ağır” ya da “uçucu” his, yükseklikten çok süreyle ayarlanır.

Kontrol: v₀ = 20, g = 50 → tepe = v₀² / 2g = 400 / 100 = 4 ✓

Dört küçük numara: oyuncuya hak verdirmek

numarakuralneden iyi hissettirir
Değişken yükseklikYükselirken tuş bırakılırsa yukarı hızı kes (örneğin yarıya indir).Kısa basış = küçük zıplama. Oyuncu yüksekliği parmağıyla ayarlar. (Super Mario Bros., 1985)
Hızlı düşüşHız aşağı döndüğünde yerçekimini 1,5–3 katına çıkar.Yükseliş “süzülür”, iniş “oturur”. Havada asılı kalma hissi kaybolur.
Çakal süresi (coyote time)Yerden ayrıldıktan sonra kısa bir süre (≈ 0,1 sn) zıplamaya izin ver.Oyuncu kenarın en ucunda basmaya çalışır ve çoğu zaman 2–3 kare geç kalır. Bunu affetmezseniz oyun “tuşu yuttu” diye suçlanır.
Zıplama tamponu (jump buffering)Tuş havadayken basıldıysa bunu kısa bir süre (≈ 0,1 sn) hatırla; yere değince zıpla.Oyuncu yere değmeden 2–3 kare erken basar. Tamponla art arda zıplamalar akıcı olur.

“Çakal süresi” adı çizgi filmlerden: uçurumdan çıkan çakal, aşağı bakana kadar düşmez. İkisi de aynı kalıp: bir geri sayım. Biri “hâlâ zıplayabilir”i, öbürü “zıplamak istiyor”u birkaç kare uzatır; ikisi de açıkken zıplama gerçekleşir.

60 karede 0,1 saniye = 6 kare

İnsan tepki süresi ≈ 200 ms; iyi bir oyuncunun zamanlama hatası bile ±2–3 kare. Bu numaralar oyunu kolaylaştırmaz; oyuncunun niyetiyle oyunun gördüğü arasındaki farkı kapatır.

Canlı: zıplama laboratuvarı 🕹️

  1. Ekranı okuyun: A / D yürü, W ya da boşluk zıpla. Mor noktalar son zıplamanın yayı. Üst satırda sürgülerden hesaplanan yerçekimi ve ilk hız; ikinci satırda sayaçlar. Karakter sarı olduğunda yerde değilsiniz ama çakal süresi açık.
  2. W’ye kısa ve uzun basın: iki farklı yay. “Değişken yükseklik” kutusunu kapatın: her basış aynı yükseklik.
  3. Tepeye süre sürgüsünü 0,15’ten 0,8’e çekin (yükseklik aynı): çevik bir böcekten ay yürüyüşüne. Üstteki g değerinin nasıl değiştiğine bakın.
  4. Düşüş çarpanı 1 → 3: yayın sağ yarısı dikleşir. 1’de yay simetrik bir parabol.
  5. Çakal süresi: ilk platformun sağ ucundan koşarak çıkın ve biraz geç zıplayın; “çakal süresi kurtardı” sayacı artar. Kutuyu kapatıp aynı boşluğu 5 kez deneyin: kaç kez düştünüz?
  6. Tampon: zıplayın ve yere inmeden hemen önce yeniden basın: karakter değdiği an tekrar zıplar. Kutuyu kapatın: aynı basış boşa gider.
🔎 DEMODA DENEYİN
  1. Ekranı okuyun: A / D yürü, W ya da boşluk zıpla. Mor noktalar son zıplamanın yayı. Üst satırda sürgülerden hesaplanan yerçekimi ve ilk hız; ikinci satırda sayaçlar. Karakter sarı olduğunda yerde değilsiniz ama çakal süresi açık.
  2. W’ye kısa ve uzun basın: iki farklı yay. “Değişken yükseklik” kutusunu kapatın: her basış aynı yükseklik.
  3. Tepeye süre sürgüsünü 0,15’ten 0,8’e çekin (yükseklik aynı): çevik bir böcekten ay yürüyüşüne. Üstteki g değerinin nasıl değiştiğine bakın.
  4. Düşüş çarpanı 1 → 3: yayın sağ yarısı dikleşir. 1’de yay simetrik bir parabol.
  5. Çakal süresi: ilk platformun sağ ucundan koşarak çıkın ve biraz geç zıplayın; “çakal süresi kurtardı” sayacı artar. Kutuyu kapatıp aynı boşluğu 5 kez deneyin: kaç kez düştünüz?
  6. Tampon: zıplayın ve yere inmeden hemen önce yeniden basın: karakter değdiği an tekrar zıplar. Kutuyu kapatın: aynı basış boşa gider.

Kod: tasarımcı dostu zıplama (1/2)

Kod: tasarımcı dostu zıplama (2/2)

🎒 Kodu okuyalım

ifadene demekneden böyle
G = 2 * H / (T * T)yerçekimi iki tasarım sayısından türetilirKodda “sihirli” 9,8 yok. Tasarımcı H’yi değiştirince zıplama süresi aynı kalır.
tampon = TAMPON (tuşa basınca)zıplamayı hemen yapma; isteği 0,12 sn hatırlaBasış anında karakter havada olabilir. İstek, yere değene kadar bekler.
cakal = o.yerde ? CAKAL : cakal - dtyerdeyken sayaç dolu; havada azalırYerden ayrıldıktan 0,1 sn sonrasına kadar cakal > 0: “hâlâ zıplayabilir”.
tampon > 0 && cakal > 0“istiyor” ve “yapabilir”İki sayaç da açıksa zıpla ve ikisini de sıfırla; yoksa tek basışla iki kez zıplanır.
o.vy *= 0.5 (tuş bırakılınca)yukarı hızı yarıya indirHızı sıfırlamak yayı köşeli yapar; yarıya indirmek yumuşak bir alçak zıplama verir. o.zipliyor bayrağı bunun bir kez olmasını sağlar.
o.vy > 0 ? G * DUSUS : Gaşağı giderken daha güçlü yerçekimip5.js’te y aşağı artar: vy > 0 düşüş demek. Unity’de işaretler ters.
ÖnermeBu zıplamanın tepe yüksekliği, tuş basılı tutulursa tam h, tepeye varış süresi tam t’dir (sabit adımda adım hatası kadar sapmayla: h − v₀·DT/2). İniş süresi t / √(düşüş çarpanı): çarpan 1,8 ise iniş, çıkışın %75’i kadar sürer.

Oyunlarda: Celeste’in zıplama sabitleri

Kaynak: Celeste’in geliştiricilerinin yayımladığı Player.cs (Kaynaklar). Karo 8 piksel; oyun 60 Hz sabit adımla çalışır.

🎮 Satır satır
  • JumpGraceTime = 0.1f: az önce yazdığımız çakal süresi; aynı sayı, aynı geri sayım (yerdeyken doldur, havada azalt).
  • if (jumpGraceTimer > 0) Jump();: zıplama “yerde mi?” diye değil, “sayaç açık mı?” diye sorar.
  • VarJumpTime = .2f: tuş basılıyken hız 0,2 sn boyunca −105’te tutulur; bırakınca yerçekimi devralır. Değişken yüksekliğin başka bir yazımı.
  • Tepe noktasında yarım yerçekimi: hız 40’ın altındayken ve tuş basılıyken mult = .5f. Karakter tepede bir an “asılı kalır”; oyuncuya havada yön verecek zaman kazandırır.
  • MaxFall = 160f: düşme hızının tavanı. Hız sınırsız artmaz; uzun düşüşlerde kontrol kaybolmaz ve tünelleme (G3) riski azalır.
  • Hesap: yerçekimi 900 piksel/sn² = 112 karo/sn²; zıplama hızı 13 karo/sn. Dünya ile karşılaştırın: 9,8 m/sn².

🌱 Isınma kodu: zıplama parametreleri

Slayttaki iki formül: return [2 * h / (t * t), 2 * h / t];. Örnek: h = 4, t = 0,4 → [50, 20]. Sonra kendinize sorun: oyununuzda karakter kaç karo zıplamalı ve bu kaç saniye sürmeli? Demodaki sürgülerle bulun, sayıları buraya yazın.
Quiz 3 · zıplama hissi
Platform oyununuzu deneyen oyuncular “kenardan zıplarken tuş bazen çalışmıyor” diyor. Kayıtlara bakıyorsunuz: tuşa, karakter platformdan çıktıktan 2–4 kare sonra basılmış. Doğru düzeltme?
Tuş çalışıyor; oyuncu 30–60 ms geç kalmış ve oyun “artık yerde değilsin” demiş. 0,1 saniyelik çakal süresi bu gecikmeyi affeder ve oyuncu farkına bile varmaz. Çarpışma kutusunu genişletmek benzer bir etki verir ama karakter havada duruyor gibi görünür ve duvarlarda tuhaflık çıkarır.

🎬 Ara: daha iyi bir zıplama

Math for Game Programmers: Building a Better Jump
GDC (Kyle Pittman) · 25 dk 43 sn · YouTube’da aç

Zıplama laboratuvarındaki fikrin kaynağı: yerçekimini ve ilk hızı “ne kadar yükseğe, ne kadar uzağa” sorularından türetmek, inişte daha güçlü yerçekimi, değişken zıplama yüksekliği. Platform oyunu yapan herkesin izlemesi gereken konuşma.

🤔 İzlerkenVideoda yükseklik ve süreden yerçekimi nasıl hesaplanıyor? İnişte yerçekimini artırmak hissi nasıl değiştiriyor? Bizim demodaki hangi kaydırıcılar bu formüllere karşılık geliyor?
LEVEL 3
Verlet ve kısıtlar
ip, kumaş, bez bebek

Problem: birbirine bağlı parçalar

🎮 Tek tek nesneler yetmez
  • İp, zincir, kanca: her halka bir öncekine bağlı.
  • Pelerin, bayrak, saç: yüzlerce nokta, komşularına bağlı.
  • Bez bebek (ragdoll): vurulan düşman merdivenden yuvarlanıyor; kol omuza, ön kol dirseğe bağlı.
  • Köprü, kule, yumuşak cisim: World of Goo, Cut the Rope, Poly Bridge.
  • Ortak kural: “bu iki nokta arasındaki uzaklık sabit kalsın.”
İlk akla gelen: aralarına yay koy

İki noktayı bir yayla bağla: uzarsa geri çeken, kısalırsa iten kuvvet (Hooke yasası). Kuvvet → ivme → Euler.

  • Yay yumuşaksa ip lastik gibi esner.
  • Yay sertse kuvvet çok büyür; bir adımda nokta dengenin öbür tarafına daha uzağa fırlar, sonraki adımda daha da uzağa… Simülasyon patlar.
  • Sert yay için dt’yi çok küçültmek gerekir: kare başına onlarca adım.

1. bölümdeki enerji tablosunun aynısı: sert yay + büyük adım = büyüyen hata.

Başka bir yol: kuvvetle uğraşma. Noktaları serbestçe yürüt, sonra kuralı çiğneyenleri doğrudan yerine koy.

Çizimle: Verlet, sayı doğrusunda

012341,00,90,80,70,6011,92,73,44Her adımda “bir önceki adımda gittiğim yol kadar daha git, ivme kadar (0,1) eksilt”: 1 → 0,9 → 0,8 → 0,7 → 0,6hız hiçbir yerde saklanmıyor; okların boyu hızın kendisi

Sonraki slayttaki tablonun resmi: nokta 0’dan başlıyor; ilk adımda 1 birim gitmiş. Her yeni adım, bir öncekinden 0,1 kısa.

Fikir 1: Verlet — hızı saklama, önceki konumu sakla

  • Her nokta iki şey tutar: şimdiki konum x ve önceki konum xö.
  • Hız ayrıca saklanmaz: bir adımda gidilen yol (x − xö) hızın kendisidir.
  • Yeni konum = şimdiki + (aynı yol bir daha) + ivmenin katkısı:

$$x_{yeni} = x + (x - x_{ö}) + a \cdot dt^2$$

  • Büyük kazanç: bir noktayı elle bir yere taşırsanız (duvardan çıkarmak, ipi kısaltmak) hızı kendiliğinden düzelir, çünkü hız zaten konumlardan okunuyor.
  • Euler’de konumu elle değiştirip hızı unutursanız nokta eski hızıyla duvara yeniden dalar.
  • Yöntem 1791’de Delambre’nin, 1960’larda Loup Verlet’nin molekül simülasyonlarında kullandığı yöntemdir; oyunlara 2001’de girdi (birazdan).
Elle izleme: a = −10, dt = 0,1 (a·dt² = −0,1)
adımöncekişimdikiyeni = 2·şimdiki − önceki − 0,1gidilen yol
1012 − 0 − 0,1 = 1,90,9
211,93,8 − 1 − 0,1 = 2,70,8
31,92,75,4 − 1,9 − 0,1 = 3,40,7
42,73,46,8 − 2,7 − 0,1 = 4,00,6

Gidilen yol her adımda 0,1 azalıyor: yukarı atılmış bir top. Hızı hiç yazmadık; yine de “hız 9, 8, 7, 6” (yol / dt) davranışı çıktı.

Çizimle: uzamış bağı düzeltmek

Önce: bağ 5 birim, olması gereken 3hata = 5 − 3 = 2Sonra: iki uç birer birim yaklaştıuzaklık tam 3 ✓p₁ (0, 0)p₂ (3, 4)d = 5p₁ (0,6, 0,8)p₂ (2,4, 3,2)d = 3

Kural: iki nokta arası 3 birim olmalı. Bağ 5 birime uzamış; fazlalık 2. İki uç, aralarındaki doğru boyunca birer birim birbirine çekilir (yeşil oklar). İp, kumaş ve bez bebek simülasyonlarının tamamı bu tek düzeltmenin binlerce kez tekrarıdır.

Fikir 2: kısıt — kuralı çiğneyeni yerine koy

Uzaklık kısıtı: p₁ ile p₂ arası boy kadar olmalı. Şimdiki uzaklık d ise hata = d − boy. İki ucu, aralarındaki doğru boyunca, hatanın yarısı kadar birbirine yaklaştır (hata eksiyse uzaklaştır).

Elle hesap: p₁ = (0, 0), p₂ = (3, 4), boy = 3
  • Fark = (3, 4); d = 5; hata = 2 (bağ 2 birim uzun).
  • Oran = hata / d = 0,4; her ucun payı yarısı: 0,2.
  • p₁ ← p₁ + (3, 4) · 0,2 = (0,6, 0,8)
  • p₂ ← p₂ − (3, 4) · 0,2 = (2,4, 3,2)
  • Kontrol: fark (1,8, 2,4) → uzaklık 3 ✓

Uçlardan biri çiviliyse hatanın tamamını öbürü üstlenir.

Bir kısıtı düzeltmek komşusunu bozar → tekrarla

Bir doğru üzerinde üç nokta: A = 0 (çivili), B = 1, C = 3. Her bağın boyu 1. Önce A–B, sonra B–C düzeltiliyor:

tekrarA–B’den sonra BB–C’den sonra B, CA–B’nin boyu
111,5 · 2,51,5 (bozuldu)
211,25 · 2,251,25
311,125 · 2,1251,125
411,063 · 2,0631,063
…→ 1 · 2→ 1

Hata her tekrarda yarıya iniyor. Bu “sırayla düzelt, birkaç kez tekrarla” yöntemine gevşetme (relaxation) denir. Az tekrar = esnek ip; çok tekrar = sert ip. Sertlik bir döngü sayısı oldu ve hiçbir değerde patlamıyor.

Canlı: ip ve kumaş 🧵

  1. Ekranı okuyun: kırmızı noktalar çivili. Bağlar gri; boyundan uzamış bağ kırmızıya döner. Altta nokta ve bağ sayısı ile en çok gerilen bağın yüzdesi (%100 = tam boyunda).
  2. Fareyle bir noktayı tutup sürükleyin ve bırakın: ip savrulur. Kodda “fırlatma hızı” diye bir şey yok; hız, son iki konumun farkından kendiliğinden çıkıyor.
  3. Kısıt tekrarı = 1: ip kendi ağırlığıyla uzar (alttaki yüzde %130’ları bulur, bağlar kızarır). 30: çelik halat gibi; yüzde %100’e yapışır.
  4. Nesneyi kumaş yapın (153 nokta, 280 bağ) ve 💨 rüzgâra basın. Tekrarı 2’ye indirin: kumaş lastikleşir ama patlamaz.
  5. Kumaşı bir köşesinden tutup hızla çekin: en kötü durumda bile birkaç karede toparlanır. Aynı deneyi sert yaylarla yapsaydık simülasyon sonsuza giderdi.
🔎 DEMODA DENEYİN
  1. Ekranı okuyun: kırmızı noktalar çivili. Bağlar gri; boyundan uzamış bağ kırmızıya döner. Altta nokta ve bağ sayısı ile en çok gerilen bağın yüzdesi (%100 = tam boyunda).
  2. Fareyle bir noktayı tutup sürükleyin ve bırakın: ip savrulur. Kodda “fırlatma hızı” diye bir şey yok; hız, son iki konumun farkından kendiliğinden çıkıyor.
  3. Kısıt tekrarı = 1: ip kendi ağırlığıyla uzar (alttaki yüzde %130’ları bulur, bağlar kızarır). 30: çelik halat gibi; yüzde %100’e yapışır.
  4. Nesneyi kumaş yapın (153 nokta, 280 bağ) ve 💨 rüzgâra basın. Tekrarı 2’ye indirin: kumaş lastikleşir ama patlamaz.
  5. Kumaşı bir köşesinden tutup hızla çekin: en kötü durumda bile birkaç karede toparlanır. Aynı deneyi sert yaylarla yapsaydık simülasyon sonsuza giderdi.

Kod: Verlet adımı ve uzaklık kısıtı (1/2)

Kod: Verlet adımı ve uzaklık kısıtı (2/2)

🎒 Kodu okuyalım

ifadene demekneden böyle
vx = p.x - p.oxson adımda gidilen yolBu, hız × dt’dir. Ayrı bir hız değişkeni yok; tutarsız kalması da mümkün değil.
p.y += vy + YERCEKIMI * dt * dtaynı yolu bir daha git + ivmenin katkısıİvme dt² ile çarpılır (hızda dt, konumda bir dt daha). Sürtünme isterseniz vx, vy’yi 0,99 ile çarpın.
oran = (d - bag.boy) / dbağ, boyunun yüzde kaçı kadar hatalı?d’ye bölmek fark vektörünü birim boya indirir; ayrıca normalleştirme gerekmez. d = 0 ise bölme hatası: iki nokta üst üsteyse atlayın.
pPay, qPayhatanın kime ne kadarı düşüyorİki uç serbestse yarı yarıya; biri çiviliyse hepsi öbürüne. Farklı kütlelerde pay ters kütleyle orantılı.
for (k < TEKRAR)gevşetme döngüsüSertlik ayarı. Maliyet doğrudan bununla çarpılır.
çivili noktalar1. aşamada atlanır, kısıtta payı 0Fareyle tutulan nokta da “o an çivili” sayılır: konumu fareye yazılır, gerisini kısıtlar halleder.
ÖnermeBir karenin maliyeti nokta sayısı + tekrar × bağ sayısı’dır. 17 × 9 kumaş (153 nokta, 280 bağ), 8 tekrar: 153 + 2.240 ≈ 2.400 küçük işlem. Zincirde hata her tekrarda sabit bir oranla küçülür; ama uzun zincirde düzeltmenin bir uçtan öbürüne taşınması daha çok tekrar ister.

Oyunlarda: Hitman’ın bez bebekleri

Kaynak: Thomas Jakobsen (IO Interactive), “Advanced Character Physics”, Game Developers Conference 2001. Oyun fiziğinin en çok okunan yazılarından.

🎮 2000 yılında, tek işlemci çekirdeğinde
  • Hitman: Codename 47, vurulan karakterlerin hazır animasyon oynatmak yerine fizikle yere yığıldığı ilk oyunlardandı.
  • Yazıya göre cesetler katı cisimlerden değil, çubuk kısıtlarıyla bağlı parçacıklardan oluşan “çöp adamlar”dı. Kol ve bacakların yönü, çizim için parçacık konumlarından sonradan hesaplanıyordu.
  • Kod, az önce yazdıklarımızın aynısı: tek satırlık Verlet adımı ve “hatanın yarısı sana, yarısı bana” kısıtı.
  • Kumaş ve bitkilerde gevşetme döngüsünden kare başına tek geçiş yetmişti: kalan hata sonraki karelerde eriyor; az tekrar bitkilere “tam kıvamında” bir esneklik veriyordu.
  • Karekök pahalıydı: yazı, kökü bağın boyu çevresinde yaklaşık hesaplayan bir numara da verir.
  • Bugün: aynı fikir “position based dynamics” adıyla Unreal’ın kumaşında, NVIDIA’nın fizik kütüphanelerinde ve çoğu oyunun ip/saç/pelerin kodunda.

🐍 Laboratuvar: bir Verlet adımı

Tek formül: yeni = 2 * x - x_onceki + a * dt * dt; sonra return [yeni, x]. Örnek: x = 1, önceki = 0, a = −10, dt = 0,1 → [1,9, 1]. İvmenin dt ile değil dt * dt ile çarpıldığına dikkat.

🐍 Laboratuvar: uzaklık kısıtı

Fark: dx, dy = p2[0] - p1[0], p2[1] - p1[1]; uzaklık d = math.hypot(dx, dy); her ucun payı k = (d - boy) / d / 2. Sonuç: [[p1[0] + dx * k, p1[1] + dy * k], [p2[0] - dx * k, p2[1] - dy * k]]. Örnek: (0, 0), (3, 4), boy 3 → [[0,6, 0,8], [2,4, 3,2]]. Bağ kısaysa (boy 7) k eksi çıkar ve noktalar kendiliğinden uzaklaşır.
Quiz 4 · kısıtlar
Oyununuzdaki kanca ipi 30 parçalı bir Verlet zinciri. Karakter ipin ucunda sallanırken ip lastik gibi uzuyor. Kısıt tekrarı 2. Ne yaparsınız?
Uzama, gevşetme döngüsünün hatayı bir karede zincirin sonuna kadar taşıyamamasından geliyor. Tekrarı artırmak ipi sertleştirir; bedeli doğrusal ek maliyettir (30 bağ × 15 tekrar = 450 işlem: önemsiz). dt’yi büyütmek uzamayı artırır; sert yaylar ise patlama riskini geri getirir.
LEVEL 4
İtme
çarpışmadan sonra kim, ne hızla, nereye?

Çizimle: hafif top ağır topa çarpıyor

Öncemomentum 1·4 + 3·0 = 41 kg3 kghız +4duruyorSonra (e = 1)momentum 1·(−2) + 3·2 = 4 ✓1 kg3 kghız −2hız +2

1 kg’lık top +4 hızla, duran 3 kg’lık topa çarpıyor. Çarpışmadan sonra hafif top geri seker (−2), ağır top yavaşça ilerler (+2). Toplam momentum (kütle × hız) önce de sonra da 4. Sonraki slayt bu sayıların nasıl çıktığını gösteriyor.

Problem ve fikir: hıza bir anda dokun

  • G3’te çarpışmayı bulduk (normal n, iç içe geçme derinliği) ve duvara çarpan topun hızını yansıttık.
  • İki hareketli cisim çarpışınca? Bilardo topu ile bowling topu aynı tepkiyi vermez: kütle işin içine girer.
  • Çarpışma çok kısa sürer; kuvveti zamana yaymak yerine hızlara bir anda bir değişiklik uygularız: itme (impuls), j.
  • Kural (Newton’un 3. yasası): iki cisme aynı büyüklükte, zıt yönlü itme. Hız değişimi = itme / kütle: hafif olan daha çok savrulur.
  • Sekme katsayısı e: ayrılma hızı = e × yaklaşma hızı. e = 1 bilardo (enerji korunur), e = 0 çamur (yapışır), 0,8 basketbol topu.

$$j = \frac{-(1+e)\, v_n}{\frac{1}{m_a} + \frac{1}{m_b}}$$

İtmenin büyüklüğü. vn: b’nin a’ya göre hızının normal üzerindeki bileşeni (nokta çarpım, G1); eksiyse yaklaşıyorlar.

Elle hesap: 1 kg’lık top 3 kg’lık duran topa çarpıyor
  • a: kütle 1, hız +4. b: kütle 3, hız 0. Normal: +x. e = 1.
  • vn = 0 − 4 = −4 (yaklaşıyorlar).
  • j = −(1 + 1)·(−4) / (1/1 + 1/3) = 8 / 1,333 = 6
  • a’nın yeni hızı: 4 − 6/1 = −2 (geri sekti)
  • b’nin yeni hızı: 0 + 6/3 = +2
öncesonra
momentum (m·v toplamı)1·4 + 3·0 = 41·(−2) + 3·2 = 4 ✓
enerji (½·m·v² toplamı)8 + 0 = 82 + 6 = 8 ✓
ayrılma hızıyaklaşma: 42 − (−2) = 4 = e × 4 ✓

e = 0 olsaydı: j = 3; iki top da +1 hızla birlikte gider. Momentum yine 4; enerji 8’den 2’ye düşer (gerisi ses ve ısı).

Canlı: kütle ve sekme 🎱

  1. Ekranı okuyun: altı top; üstlerinde kütleleri (büyük top = ağır top). Yeşil çizgi hız vektörü. Çarpışan toplar bir an sarı yanar. Üst satırda çarpışma sayısı, toplam enerjinin başlangıca oranı ve toplam momentum.
  2. e = 1: enerji %100’de kalır; toplar sonsuza kadar sekmeye devam eder. Küçük bir topun büyüğe çarpıp geri fırladığını, büyüğün ise yolundan pek sapmadığını izleyin.
  3. e = 0,5: her çarpışmada enerji azalır; bir dakika içinde hepsi durulur.
  4. e = 0: toplar çarpışma doğrultusunda birbirine yapışır, yalnızca teğet yönde kayarlar.
  5. 💥 karıştır ile yeni hızlar verin. Momentum satırı yalnızca bir top duvara çarptığında değişir; top–top çarpışmalarında sabit kalır: itmeler eşit ve zıt.
🔎 DEMODA DENEYİN
  1. Ekranı okuyun: altı top; üstlerinde kütleleri (büyük top = ağır top). Yeşil çizgi hız vektörü. Çarpışan toplar bir an sarı yanar. Üst satırda çarpışma sayısı, toplam enerjinin başlangıca oranı ve toplam momentum.
  2. e = 1: enerji %100’de kalır; toplar sonsuza kadar sekmeye devam eder. Küçük bir topun büyüğe çarpıp geri fırladığını, büyüğün ise yolundan pek sapmadığını izleyin.
  3. e = 0,5: her çarpışmada enerji azalır; bir dakika içinde hepsi durulur.
  4. e = 0: toplar çarpışma doğrultusunda birbirine yapışır, yalnızca teğet yönde kayarlar.
  5. 💥 karıştır ile yeni hızlar verin. Momentum satırı yalnızca bir top duvara çarptığında değişir; top–top çarpışmalarında sabit kalır: itmeler eşit ve zıt.

Kod: iki cisim arasında itme

Python ve C# sürümleri satırı satırına aynı aritmetik. Unity’de bunu motor yapar; e = “Physics Material 2D → Bounciness”, kütle = “Rigidbody2D → Mass”.

🎒 Kodu okuyalım
  • vn >= 0 → return: iç içe ama uzaklaşan iki cisme itme uygulamak onları yeniden birbirine yapıştırır. En sık unutulan satır.
  • tersA, tersB: motorlar kütlenin tersini saklar. Duvar, zemin, hareketli platform: ters kütle = 0. Formül hiç değişmeden “duvar kıpırdamaz, top tam yansır” sonucunu verir.
  • j / kütle: aynı itme, hafif cismin hızını daha çok değiştirir.
  • Sıra: önce iç içe geçmeyi konumdan ayır (G3: en kısa çıkış vektörü; pay ters kütleyle orantılı), sonra hızlara itme uygula.
  • Eksik olan: dönme ve sürtünme. İtme merkezden geçmiyorsa cisim döner; teğet yöndeki itme sürtünmedir. Tam katı cisim fiziği burada başlar (Kaynaklar).

Önerme: bu itmeden sonra normal boyunca ayrılma hızı tam e × yaklaşma hızıdır ve toplam momentum değişmez; e = 1 ise toplam enerji de korunur.

LEVEL 5
Hepsi bir arada
tam kod, araçlar, kapanış

📦 Tam kod: p5.js ile zıplama laboratuvarı

Döngünün iskeleti. Eksiksiz dosyada harita, çarpışma, ses ve çizim de var.

🎮 Çalıştırın, kopyalayın, bozun

▶ Eksiksiz örneği aç — yaklaşık 90 satır: A / D yürü, W zıpla; 1 / 2 / 3 tuşlarıyla çakal süresi, tampon ve değişken yükseklik açılıp kapanır.

Deneyin:

  • DT’yi 1/10 yapın: zıplama alçalır, karakter platformlara “gömülür”.
  • TEPE_SURESI’ni 0,2 ve 0,7 yapın: aynı yükseklik, bambaşka oyun.
  • Düşme hızına tavan koyun (Celeste: MaxFall).
  • Tepe noktasında yarım yerçekimi ekleyin: abs(o.vy) < 40 iken G’yi yarıya indirin.
  • Çizime ara değer (alfa) ekleyin ve DT = 1/30 ile farkı görün.

🧰 Motorlarda ve kütüphanelerde

Unity (2B)Rigidbody2D · FixedUpdate · Time.fixedDeltaTimeİçeride Box2D. Gövde türü: Dynamic (motor yürütür), Kinematic (siz yürütürsünüz). Interpolate ayarı = ara değerli çizim.
Unity (eklemler)DistanceJoint2D · HingeJoint2D · SpringJoint2DUzaklık kısıtı, menteşe, yay. Tekrar sayıları: Project Settings → Physics 2D → Velocity / Position Iterations.
Godot_physics_process(delta) · CharacterBody2D · RigidBody2DSabit 60 Hz. move_and_slide(): G3’teki “kaydır” çözümü + zeminde olma bilgisi (is_on_floor()).
UnrealChaos Physics · Chaos ClothKatı cisim ve kumaş; kumaş tarafı konum tabanlı kısıtlarla (Verlet’nin torunu) çalışır.
JavaScriptMatter.js · Planck.js (Box2D) · RapierTarayıcıda hazır 2B fizik. p5.js ile birlikte kullanılabilir.
Pythonpymunk (Chipmunk) · pybox2dpygame ile 2B fizik.

Pratik kural: karakter hareketini (yürüme, zıplama) çoğu zaman kendiniz yazarsınız, çünkü “his” fizik yasalarına uymaz. Çevredeki nesneler (kutular, zincirler, enkaz) için motorun fiziğini kullanın.

🤖 Bu konuyu bir YZ kod asistanıyla yazarken

Sık yapılan hatalar — kontrol edin
  • Hareketi dt ile çarpmamak (x += 5): oyun 144 Hz ekranda 2,4 kat hızlı
  • Konumu hızdan önce güncellemek (açık Euler)
  • Fiziği değişken kare süresiyle adımlamak; biriktirici yok
  • Biriktiricide while yerine if; dev kareleri kırpmamak
  • Unity’de tuş basışını FixedUpdate’te okumak (basış kaçar)
  • Zıplamada çakal süresi var ama zıpladıktan sonra sayaç sıfırlanmıyor: havada ikinci zıplama
  • Çarpışmada vn >= 0 kontrolünün unutulması: cisimler birbirine yapışır
İyi bir istek (prompt) örneği

“p5.js, fizik motoru yok. 2B platform karakteri: (1) sabit zaman adımı: DT = 1/120, biriktirici, kare süresi 0,25 sn’de kırpılsın. (2) Yarı-örtük Euler; yatay ve dikey eksen ayrı ayrı çözülsün. (3) Zıplama: YUKSEKLIK ve TEPE_SURESI sabitlerinden yerçekimi ve ilk hızı hesapla (g = 2h/t², v0 = 2h/t). (4) Değişken yükseklik, düşüş çarpanı 1,8, çakal süresi 0,1 sn, zıplama tamponu 0,12 sn; her biri bir tuşla açılıp kapansın. (5) Hata ayıklama: zıplama yayını noktalarla çiz; ekrana g, v0 ve ölçülen tepe yüksekliğini yaz. (6) Test: DT’yi 1/30 ve 1/240 yapınca tepe yüksekliği %5’ten az değişmeli.”

  • Yöntemin adını verin (yarı-örtük Euler, sabit adım, biriktirici).
  • Ölçülebilir bir test isteyin: “tepe yüksekliği kare hızından bağımsız”.
  • Sihirli sayıları değil, tasarım sayılarını (h, t) parametre yaptırın.

💬 Tartışma soruları

Yanınızdakiyle 2 dakika konuşun, sonra sınıfça tartışalım
  1. Sevdiğiniz bir platform oyununda zıplama nasıl “hissettiriyor”? Ağır mı, uçucu mu? Bu sunumdaki hangi ayarlar (tepe süresi, düşüş çarpanı, değişken yükseklik) o hissi veriyor olabilir?
  2. Çakal süresi ve zıplama tamponu oyuncuya söylenmeden yapılan “yardımlar”. Bu bir hile mi? Oyuncu bilse oyundan aldığı zevk değişir mi? (Y14’teki dinamik zorlukla karşılaştırın.)
  3. Quake III’teki kare hızına bağlı zıplama bir hataydı ama oyuncular onu bir beceriye çevirdi. Geliştirici böyle bir hatayı düzeltmeli mi? Hız koşucuları (speedrun) açısından düşünün.
  4. Bez bebek fiziği (ragdoll) hazır ölüm animasyonlarının yerini aldı. Ne kazanıldı, ne kaybedildi? Hangi tür oyunlarda hâlâ el yapımı animasyon daha iyi?
  5. Gerçekçi fizik ne zaman oyunu daha kötü yapar? Bir yarış oyunu, bir futbol oyunu ve bir platform oyunu için ayrı ayrı düşünün.

🚀 Proje ve araştırma önerileri

  1. [kolay] Zıplama ayar masası — Örnek kodu genişletin: sürgülerle h, t, düşüş çarpanı; üç hazır ayar (“Mario”, “Celeste”, “ay”). Arkadaşlarınıza oynatıp hangisini sevdiklerini sorun.
  2. [kolay] İp köprüsü — p5.js’te iki ucu çivili Verlet köprüsü; üstünden yuvarlanan bir top (top–nokta itmesi). Tekrar sayısı sürgüsü.
  3. [orta] Kanca (grappling hook) — Platform karakterine fareyle atılan bir ip: tek uzaklık kısıtı. Sallanma, ipi kısaltıp uzatma, bırakınca savrulma. Hız neden kendiliğinden korunuyor?
  4. [orta] Bilardo — Dairesel toplar, itme ile çarpışma, sürtünme, delikler. G2’deki ızgarayla geniş aşama; sabit adım. Aynı vuruş her seferinde aynı sonucu veriyor mu?
  5. [zor] Çöp adam bez bebek — 11 nokta, 10 çubuk + açıları sınırlayan ek kısıtlar (Jakobsen’in yazısı). Fareyle tutup fırlatın; merdivenden yuvarlayın.
  6. [araştırma] Integratör karşılaştırması — Açık Euler, yarı-örtük Euler, Verlet ve RK4’ü aynı yay ve yörünge probleminde çalıştırın: enerji sapması ile kare başına işlem sayısını grafiğe dökün. Hangi oyun için hangisi?

Neler öğrendik? 🎒

  • Hareket: önce hız, sonra konum (yarı-örtük Euler). Aynı iki satırın ters sırası enerji üretir ve patlar. (Box2D’nin iki satırı.)
  • Sabit zaman adımı: gerçek zamanı biriktir, eşit DT’lerle harca; kalanı devret, dev kareleri kırp, çizimde ara değer al. Aksi hâlde oyun makineden makineye değişir. (Quake III.)
  • Zıplama: yüksekliği ve süreyi seç: g = 2h/t², v₀ = 2h/t. Değişken yükseklik, hızlı düşüş, çakal süresi ve tampon oyuncunun niyetini affeder. (Celeste.)
  • Verlet: hız = şimdiki − önceki konum. Konumu elle düzeltince hız kendiliğinden uyar; bu da kısıtları mümkün kılar.
  • Kısıt + gevşetme: kuralı çiğneyen noktaları yerine koy, birkaç kez tekrarla. İp, kumaş, bez bebek. (Hitman.)
  • İtme: çarpışmada iki cisme eşit ve zıt itme; hız değişimi kütleyle ters orantılı; sekme katsayısı enerjinin ne kadarının kaldığını söyler.
  • Sonraki: eğriler ve ara değerleme: kamera nasıl yumuşak takip eder, bir nesne A’dan B’ye nasıl “tatlı” gider, yarış pisti hangi eğriyle çizilir?

🎮 Bu sunumdaki oyunlar

Adı geçen oyunu tanımıyorsanız: oyuncuların kullandığı wiki sayfası ve oyunu kısaca gösteren bir video. ▶ video slaydın üstünde açılır (Esc ile kapanır); 🎯 işaretli bağlantılar doğrudan bu sunumdaki konuya gider.

Quake III Arena 1999 · arena nişancı
Hızlı tempolu çok oyunculu nişancı; kaynak kodu açık.
Celeste 2018 · platform
Bir dağa tırmanış; zor ama cömert kontrol noktalı hassas platform oyunu.
Hitman: Codename 47 2000 · gizlilik
Kiralık katil 47; vurulan karakterlerin bez bebek fiziği oyunlarda ilklerdendi.
Angry Birds 2009 · fizik bulmaca
Sapanla kuş fırlatıp domuzların kulelerini yıktığınız mobil fizik oyunu.
Hollow Knight 2017 · metroidvania
El çizimi, böceklerin dünyasında geçen zorlu 2B keşif oyunu; çok küçük bir ekip yaptı.
Super Mario Bros. 1985 · platform
Nintendo’nun yan kaydırmalı platform klasiği; zıplama hissi bugün hâlâ ölçüt.

Kaynaklar: videolar 🎬

Kaynaklar: kaynak kodu, yazılar ve dersler 📚

NEXT LEVEL ▶
Eğriler ve Ara Değerleme
lerp, yumuşatma (easing), Bézier, Catmull–Rom, yumuşak kamera takibi
SONRAKİ SUNUM ▶