holodepth

Three.js · Animasyon

Animasyon döngüsü ve zaman yönetimi

3B sahnede bir nesnenin hareket etmesi, aslında saniyede onlarca kez sahnenin yeniden çizilmesidir. Profesyonel bir projede bu çizim hem akıcı olmalı hem de mümkün olduğunca her cihazda aynı zaman ölçeğinde ilerlemelidir. Temel araçlar requestAnimationFrame,
delta time ve THREE.Clock'tur; render döngüsü ile birlikte düşünülür.

Döngü, zaman ve kare hızı

requestAnimationFrame: modern döngü

Eskiden animasyonlar için setInterval kullanılırdı; modern web standartlarında tek bir "kral" vardır: requestAnimationFrame (rAF).

  • Akıllı senkronizasyon: rAF, tarayıcının yenileme hızıyla (çoğu ekranda 60 Hz civarı) otomatik hizalanır.
  • Performans dostu: Kullanıcı başka sekmeye geçtiğinde veya pencereyi küçülttüğünde animasyon genelde duraklar; GPU ve pil ömrü için kritiktir.
  • Daha düzgün çizim: Kod, ekranın bir sonraki boyama döngüsünden hemen önce çalıştırılarak "tekleme" (stuttering) riski azaltılır.

requestAnimationFrame ekranın yenileme hızına bağlı çalışır; bu yüzden farklı cihaz ve monitörlerde farklı FPS değerleri oluşabilir. Bu da delta time ile kare hızından bağımsız hareket gerektirmesinin nedenidir.

Tick: güncelle, sonra çiz

Oyun ve etkileşimli 3B geliştirmede her animate çağrısı genelde bir tick sayılır: önce sahne durumu (konum, animasyon, fizik) bu tick içinde güncellenir, ardından renderer.render(scene, camera) ile kare çizilir.

Bu döngü, Renderer sayfasında anlatılan render çağrısını sürekli tekrarlar; böylece render pipeline her karede yeniden işletilmiş olur — yani sistemin "kalbi" burada atar.

Delta time: kare hızından bağımsızlık (FPS independence)

En sık yapılan hata, nesneleri her karede sabit bir adımla hareket ettirmektir — örneğin cube.rotation.y += 0.01.

Sorun: 60 Hz bir ekranda bu "normal" görünürken, 144 Hz bir oyuncu monitöründe aynı kod nesneyi belirgin şekilde daha hızlı döndürür.

Çözüm (delta time): Bir önceki kareden bu yana geçen süreyi (çoğunlukla saniye) hesaplayıp hareketi bu süreyle çarpmak gerekir. Böylece donanım ne kadar hızlı olursa olsun, nesne gerçek zamanda (saniye başına) tutarlı hızla hareket eder.

THREE.Clock ile tipik kullanım:

const delta = clock.getDelta();
mesh.position.x += 2 * delta; // saniyede 2 birim (birim/s × saniye)

2 * delta ifadesi, hızın birim/saniye cinsinden verildiği düşünüldüğünde her karede ne kadar yer değiştireceğini verir; FPS artsın azalsın, saniyede toplam mesafe aynı kalır.

THREE.Clock: zamanı ölçmek

Three.js, zamanı yönetmek için THREE.Clock sunar. İki temel okuma birbirinin yerine geçmez; kısaca ayrım şöyledir:

  • .getElapsedTime() sürekli artan toplam süreyi (saniye) döndürür; genelde matematiksel / periyodik animasyonlar için kullanılır (sinüs, dalga, dönen değerler).
  • .getDelta() yalnızca iki kare arasındaki süreyi verir; fiziksel hız, ivme ve sabit birim/saniye ile hareket gerektiren güncellemeler için tercih edilir.

Uygulamada aynı karede hem toplam süreye hem delta'ya ihtiyaç varsa: Three.js kaynaklarında getElapsedTime() içeride getDelta() çağırdığı için önce const delta = clock.getDelta() deyip, toplam süre için clock.elapsedTime özelliğini okumak güvenli bir kalıptır (aşağıdaki birleşik örnek).

Teknik tablo: zaman yönetimi yöntemleri

Yöntem Kullanım alanı Avantajı
getElapsedTime() Dalgalanma, yüzen nesneler, sürekli dönüşler. Sin / cos ile periyodik hareketlerde doğrudan kullanım.
getDelta() Karakter hareketi, mermi hızı, fiziksel düşüş. 60 FPS / 144 FPS farkında aynı gerçek zaman hızı.
Date.now() Düz JavaScript zaman damgası. Three.js'e bağlı değildir; rAF ile doğal senkron garantisi yoktur.

Uygulama: profesyonel render döngüsü

Aşağıdaki örnek, aynı animate içinde hem süre tabanlı dalga hareketini (elapsedTime ile sinüs) hem delta ile FPS bağımsız dönüşü birleştirir. mesh, scene, camera ve renderer önceden tanımlıdır.

const clock = new THREE.Clock();

function animate() {
  requestAnimationFrame(animate);

  const delta = clock.getDelta();
  const elapsedTime = clock.elapsedTime;

  // Dalga hareketi (sürekli artan süre → sin/cos için uygun)
  mesh.position.y = Math.sin(elapsedTime) * 0.5;

  // FPS bağımsız dönüş (radyan/s × saniye)
  mesh.rotation.y += 2 * delta;

  renderer.render(scene, camera);
}

animate();

Holodepth notu: getDelta() tuzağı

getDelta() metodunu aynı döngü içinde birden fazla kez çağırmayın. getElapsedTime() da içeride getDelta() tetiklediği için peş peşe getElapsedTime(); getDelta(); yazmak ikinci bir delta ölçümü yaratır ve süreleri bozar. Hem delta hem toplam süre gerekiyorsa: önce const delta = clock.getDelta(), toplam süre için clock.elapsedTime (veya yalnızca biri yeterliyse tek bir API kullanın).