HTML5 Canvas · Sprite ve varlık sistemi
Kare animasyonu: zamanla ilerleyen kare indeksi
Sprite levhasında hangi dilimin çizileceği (
ızgara veya meta ) görsel
düzen işidir; hangi karenin şu anda seçili olduğu ise zamana bağlı oyuncu
durumu veya nesne durumudur. Bu sayfa kare süresini (
frame duration ), birikimli
dt ile güvenli ilerlemeyi ve döngü (
loop ), tek sefer, ileri–geri (
ping-pong ) gibi oynatma modlarını Canvas oyun döngüsü açısından
sabitler.
Rasterın belleğe alınması
Resim yükleme sayfasında; dilim
koordinatlarının drawImage’e aktarımı
sprite levhası başlığında
kalır.
Gerçek zaman ölçeği için Delta time ve kare yaşamı için Kare yönetimi ile birlikte okuyun; oyun fazı ve duraklatma oyun durumu ile bağlanabilir.
Özet: animasyon durumu ve çizim
| Veri | Rol | Bağlantı |
|---|---|---|
dt |
Kare süresi ( saniye ) | Birikimci |
secondsPerFrame |
Bir sprite karesinin ekranda kalma süresi | TPS = 1 / SPF |
index |
Aktif levha hücresi ( 0 tabanlı ) | Izgara |
| Oynatma modu | Döngü / bir kez / ping-pong | Son kare politikası |
| Çizim | drawSpriteSlice ile sabit index |
Levha sayfası |
Ayık kare ve sürekli zaman: indeks üretimi
Film şeridi gibi düşünün: görüntü sürekli değişmez, belirli aralıklarla bir sonraki kareye geçilir. Oyunda «kare» süresi ekran yenileme hızından ( 60 Hz ) bağımsız tasarlanır: yürüyüş animasyonu saniyede 12 gerçek sprite karesi gösterebilirken tuval her karede çizilir — çizilen sprite karesi iki üç ekran karesinde aynı kalabilir.
Bu ayrım, animasyonu «zaman tabanlı» yapar: düşük FPS’te oynanış yavaşlar ama animasyon
saniye başına aynı sayıda sprite karesi göstermeye çalışır (
büyük dt sıçramalarında birden çok kare atlama —
birikimci bölüm ). Film karesi gibi
sabit ekran başına bir sprite karesi isterseniz
secondsPerFrame değerini ekran süresine eşlersiniz — tasarım tercihidir.
Her varlık için ayrı zamanlayıcı tutmak doğaldır; aynı levha üzerinde birden fazla bağımsız
klip (
farklı satır veya indeks aralığı ) oynatmak için hem index hem «hangi klip»
kimliği (
satır ofseti ) ayrı alanlarda tutulur — levha geometrisi
sprite levhası sayfasında
çözülür.
Birikimci zaman ve güvenli atlama
Her güncellemede index += dt * k gibi doğrusal sürekli ilerleme basit
kaydırmalarda işe yarar; sprite kareleri için tipik yol birikimci (
accumulator ): acc += dt, acc kare süresini
aştıkça bir veya daha fazla kez kare sayacını artır ve fazlalığı
acc’de bırak. Böylece iki kare sonra dönen oyuncu «iki kare birden atlayarak»
senkron kalır.
Uzun sekme aralarında dt büyür; döngü içinde çok kez adım atmak doğru davranış
olabilir (
hızlı ileri sarı ). Tavan koymak (
bir güncellemede en fazla N adım ) bazen oyun tasarımı gereği eklenir — yoksa tek tick’te
tüm animasyonu tüketmiş olursunuz. Bu politika
delta
time tartışmasıyla paraleldir.
Sabit setInterval ile animasyon güncellemesi yapmak, ana döngüden kopuk zaman
kaynağı üretir; requestAnimationFrame tabanlı oyunda animasyon ilerlemesini
yine aynı oyunda ölçülen dt ile beslemek tutarlılık sağlar.
/**
* loop: true → index 0..frameCount-1 döner; false → son karede finished.
* dt: saniye (delta time).
*/
function createFrameClip(frameCount, secondsPerFrame, loop = true) {
return {
frameCount,
secondsPerFrame,
loop,
acc: 0,
index: 0,
finished: false,
};
}
function stepFrameClip(c, dt) {
if (c.finished || c.frameCount <= 0 || c.secondsPerFrame <= 0) return;
c.acc += dt;
while (c.acc >= c.secondsPerFrame) {
c.acc -= c.secondsPerFrame;
const next = c.index + 1;
if (next >= c.frameCount) {
if (c.loop) c.index = 0;
else {
c.index = c.frameCount - 1;
c.finished = true;
break;
}
} else {
c.index = next;
}
}
}
Kare hızı ve saniye başına kare: tasarım parametreleri
secondsPerFrame doğrudan hissettirir: 1/12 saniye tipik el çizimi hissi, 1/24
sinematik, 1/60 ise çoğu zaman neredeyse her ekran karesinde bir atlas karesi demektir.
Tersi TPS (
ticks per second ) olarak da düşünülür — kodda tek temsil seçin, iki formu aynı yerde
karıştırmayın.
Farklı animasyonlar farklı süreler taşır: «ateş et» tek seferlik üç kısa kare, «nefes al»
uzun aralıklı iki kare. Bu yüzden sabit global «animasyon hızı» yerine klip başına
secondsPerFrame yaygındır; paylaşılan sabitler için adlandırılmış sabitler (
WALK_SPF ) kullanın.
Zaman ölçeği yavaşlatma / hızlandırma (
güç toplama efekti ) çarpanı tüm klibe uygulanabilir: efektif süre
secondsPerFrame / speedMul — çarpanın sıfıra yaklaşması sonsuz yavaşlama
üretir, üst sınır koyun.
İleri–geri oynatma ve uç kareler
Ping-pong, son kareye gelince yönü çevirerek ileri ve geri oynatır; döngüsel klip ile aynı değildir ( son kareden birinci kareye sıçramaz ). Ateşleme veya tek yönlü yürüyüş genelde düz döngü veya bir kez oynatmadır; meşale, sarkaç gibi nesneler ping-pong ile ucuz canlanır.
frameCount === 1 durumunda yön değiştirmenin anlamı yoktur; erken dönüş korur.
Uç karelerde taşmayı önlemek için indeks artırıldıktan sonra sınırla ve yönü ters çevirmek
okunaklıdır — aşağıdaki yardımcı bunu yapar.
İleri–geri klip bittiğinde «duraklat» mı yoksa başa mı sarılır ürün kararıdır; aşağıdaki örnek sonsuz ping-pong varsayar.
function createPingPongClip(frameCount, secondsPerFrame) {
return {
frameCount,
secondsPerFrame,
acc: 0,
index: 0,
direction: 1,
};
}
function stepPingPongClip(c, dt) {
if (c.frameCount <= 1 || c.secondsPerFrame <= 0) return;
c.acc += dt;
while (c.acc >= c.secondsPerFrame) {
c.acc -= c.secondsPerFrame;
c.index += c.direction;
if (c.index >= c.frameCount) {
c.index = c.frameCount - 1;
c.direction = -1;
} else if (c.index < 0) {
c.index = 0;
c.direction = 1;
}
}
}
Levha ile birleştirme: indeksten dilime
Animasyon katmanı yalnız «şu an kaçıncı kare» bilgisini üretir; piksel kutusu
sx, sy, sw, sh için
gridCellRect
veya meta tablo kullanılır. Ayrım net olunca aynı klibi farklı levhalara (
düşük / yüksek çözünürlük ) bağlamak kolaylaşır — indeks sözleşmesi aynı kalır.
Başlangıç ofseti (
levhanın üçüncü satırı yürüyüş ) sabit bir baseIndex ile modele eklenir:
gridIndex = baseIndex + clip.index — böylece veri odaklı tasarlanmış atlaslar
tek satırda kalabilir.
Çizim çağrısı her karede çalışır; değişen tek şey çoğu zaman
clip.index’tir. Çift step çağrısı (
update ve render’da iki kez ) kareleri iki kat hızlı oynatır — güncellemeyi tek fazda
yapın.
Durum makinesi ve klip seçimi
«Yürü», «zıpla», «vur» gibi üst durumlar her biri kendi FrameClip örneğini
taşır; geçişte ya mevcut klibi sıfırlarsınız ya da kaldığı yerden devam (
kesintisiz geçiş nadir, bilinçli seçim gerektirir ). Animasyon değişince indeks ve
biriktirici
sıfırlanmazsa ilk kare atlanmış veya yarım süreyle başlamış olur.
Bu mantıksal geçişler oyun durumu ile uyumludur; durum makinesi hangi klibin seçileceğine karar verir, bu sayfa klibin içinde nasıl akacağını verir — çakışmayı önlemek için sınır burada çizilir.
Aynı varlıkta üst gövde ve alt bacak farklı klipler oynatabilir (
sentetik karakter ); senkron için ortak zaman çizgisi veya paylaşılan
acc kullanılır. Konu ileri seviye rig; bu sayfa tek klibin zamanlamasına
odaklanır.
Duraklatma ve sekme görünürlüğü
Duraklatıldığında stepFrameClip çağrılmazsa animasyon donar; yalnızca çizim
devam ederse son kare yakılı kalır —
oyun
durumu politikasıyla uyumlayın. Arka planda büyük
dt tek seferde birikmesini istemiyorsanız duraklatma bayrağı ile biriktiriciyi
sıfırlayın veya tavanlı adım kullanın.
Sekme görünürlüğü API’si ile arka planda simülasyonu durdurmak, kullanıcı deneyimi için yaygındır; animasyon güncellemesi de durmalıdır — aksi halde sekmeye geri dönüşte klibin ortasına sıçranılmış gibi görünür.
«Boss intro» gibi kesme sahnesi animasyonlarında tek seferlik klip bitince olay üretmek (
geçiş tetikleyici ) yaygındır; finished bayrağını bir karede okuyup kuyruğa
atın.
Anti-kalıplar: kare hızı ve ekran FPS’i karışması
Bu dört örnek en sık «animasyon giderek kayıyor», «60’ta iyi 120’de uçuyor» veya
«duraklatınca
iki kare birden sıçradı» şikâyetine yol açar; ortak payda ekran kare sayısını doğrudan
sprite
kare oranına bağlamaktır. Çözüm bu sayfada zaten tarif edilen saniye tabanlı birikim, tek
step çağrısı ve klip başına secondsPerFrame sabitidir — ek fizik
motoru gerekmez.
İndeksi her çizimde ++ ile artırmak: Çizim ne kadar sık
yapılırsa animasyon o kadar hızlı görünür; düşük güç modunda veya
VSync farkında oynanış hızı «titriyor» gibi algılanır. Zaman ölçeğini
saniye ile tanımlayıp
birikimci dt ile
ilerletmek, hem
delta
time
disiplinine uyar hem cihazdan cihaza daha tutarlı sonuç verir.
step’i hem güncellemede hem çizimde çağırmak: Aynı karede iki
kez ilerleme, klip süresini yarıya indirir. Çizim (
render ) safı salt okunur kabul edin; ilerleme yalnızca simülasyon /
güncelleme aşamasında çalışsın —
Update
vs
render ayrımıyla uyumludur.
Durum değişiminde biriktirici sıfırlanmıyor: Önceki klibin
acc birikimi, yeni klibin ilk karesini «hemen atla» veya yarım süreyle oynatır;
indeks de ara değerde kalabilir. Klip veya üst durum değişirken
geçiş anında
acc = 0 ve çoğu zaman index = 0 (
veya giriş karesine sabit değer ) atamak, hareketin her seferinde aynı noktadan başlamasını
sağlar; istisna (
kesintisiz süreklilik ) bilinçli dokümante edilmelidir.
secondsPerFrame olmadan ham indeks döngüsü: «Her
requestAnimationFrame bir sprite karesi» modeli, üst sınırı olan bir
animasyonu kilitler ve tasarımcının istediği
TPS ile örtüşmeyebilir. Süreyi kodda tek bir
sayı (
veya adlandırılmış sabit ) olarak tutun; böylece levha tarafında indeks taşması ve süre
ayarı birbirinden ayrışır —
taşan kaynak indeks ile
karışan hataları ayıklamak kolaylaşır.
Bu sayfanın sınırı
İskelet animasyonu ( skeletal ), kemik ağırlıkları ve doku deformasyonu burada işlenmez. Odak: Canvas 2D sprite kare dizilerinin zamanla seçilmesi ve oynatma modlarıdır.
- Animasyon ilerlemesi saniye ve
dtile ölçekli mi? stepyalnızca bir fazda mı çağrılıyor?- Klip / durum değişiminde indeks ve biriktirici sıfırlanıyor mu?
- Levha indeksi üst sınırı taşmıyor mu?
- Duraklatmada veya sekmede birikmiş
dtsürprizi var mı?