holodepth

HTML5 Canvas · Sprite ve varlık sistemi

Sprite levhası: tek görüntüden çok kare çizmek

Sprite sheet ( veya texture atlas ), birden çok küçük kareyi tek raster dosyada toplar; tarayıcı bir kez yükler, siz her kare için farklı kaynak dikdörtgen ( sx, sy, sw, sh ) seçersiniz. Canvas 2D’de bu iş drawImage’in dokuz argümanlı biçimiyle yapılır — bu sayfa parametre sırasını, düzenli ızgara matematiğini ve ölçekte «komşu piksel sızıntısı» riskini sabitler. Levha dosyası belleğe alma Resim yükleme sayfasında; karelerin zamanda nasıl döneceği Kare animasyonu başlığında kalır.

Çizim alışkanlıkları ve dönüşüm yığını için 2D bağlam ve Temizle ve yeniden çiz ile uyum düşünün; varlık önbelleği Varlık önbellekleme ile ilişkilidir.

Özet: levhadan tuvala veri yolu

Veri Anlam Not
image Yüklü levha Hazır raster
sx, sy, sw, sh Levhadaki kaynak pencere Piksel; levha uzayında
dx, dy, dw, dh Tuvalde hedef pencere Dünya veya piksel birimi
Kare indeksi Izgara satır / sütun Marj ve köken ile birlikte
Zaman Hangi kare seçilecek Animasyon sayfası

Atlas mantığı: neden tek dosya

Çoklu küçük dosya yerine tek levha, HTTP istek sayısını düşürür ve ağ gecikmesinde özellikle mobilde hissedilir kazanç sağlar. Bellekte tek büyük doku tutulduğu için toplam piksel aynı kalsa bile yönetim basitleşir — tek HTMLImageElement referansı, çok sayıda çizim çağrısı. Tasarım aracından dışa aktarırken kareler arasına ince boşluk ( padding ) bırakmak, ölçeklendirmede komşu hatları yutmamayı kolaylaştırır.

Levha «kaç sütun / kaç satır» bilgisi ya görsel olarak sabitlenir ya da dışarıdan ( JSON, doku paketleyici çıktısı ) okunur. Bu sayfa düzenli ızgara ve indeks üzerinden gider; tam otomatik dikdörtgen paketleme ( bin packing ) araç konusudur, çizim API’si konusu değildir.

Aynı levha hem karakter yürüyüşü hem UI rozetleri içerebilir; mantıksal olarak «alt atlaslar» gibi düşünmek ( farklı indeks aralıkları veya köken ofsetleri ) kod okunurluğunu artırır. Çizim sırasında hangi kütük önce hangi sonra — bu sayfanın bağlam ve sıra bölümünde özetlenir.

Dokuz argümanlı drawImage: kaynak ve hedef

İmza: ctx.drawImage(image, sx, sy, sw, sh, dx, dy, dw, dh) — kaynak piksel bölgesi kopyalanır, hedef dikdörtgene ölçeklenerek yapıştırılır. Koordinatlar kaynakta levha sol–üstüne göre, hedefte tuval ( veya aktif dönüşüm matrisine göre ) ölçülür. sw veya sh sıfırsa tarayıcı çizmez veya davranış belirsizdir; üretimde savunmacı sınır koyun.

Üç argümanlı biçim tüm görüntüyü hedefe taşır; beş argümanlı biçim kaynak boyutunu değiştirmeden hedef boyutu verir; dokuzlu biçim sprite diliminin özüdür. Karıştırmak sık hata kaynağıdır — proje içinde tek sarmalayıcı fonksiyon kullanmak imzayı sabitler.

Görüntü henüz yüklenmediyse veya decode beklenmediyse çağrı sessizce başarısız veya eksik çizim üretebilir; çağırmadan önce hazırlık katmanından geçirin.

/**
 * Levha dilimini hedef dikdörtgene çizer. image: yüklü HTMLImageElement.
 */
function drawSpriteSlice(ctx, image, sx, sy, sw, sh, dx, dy, dw, dh) {
  if (!ctx || !image || !image.complete) return;
  if (sw <= 0 || sh <= 0 || dw <= 0 || dh <= 0) return;
  ctx.drawImage(image, sx, sy, sw, sh, dx, dy, dw, dh);
}

Düzenli ızgara: indeks, sütun ve marj

Kareler eş boyutlu ve soldan sağa, yukarıdan aşağıya diziliyse indeks i için sütun i % cols, satır (i / cols) | 0 ( tamsayı bölme ) ile bulunur. Köşede originX, originY ofseti ( başlık şeridi, boşluk ) varsa her hücrenin sol–üstü bu ofsete göre kaydırılır. Kareler arası boşluk padX, padY ile modellenir: adım genişliği cellW + padX olur.

İndeks sınırları dışına taşmak ( animasyon fazla kare oynatıyorsa ) levhanın yan piksellerini çeker ve görsel «glitch» üretir; üst katmanda indeksi clamp’lemek veya döngü uzunluğunu animasyon mantığında sabitlemek gerekir. Bu sayfa matematiksel dikdörtgeni üretir, oyun mantığı hangi indeksi istediğine karışmaz.

Bazı araçlar «sıcak nokta» ( pivot ) meta verisi yazar; dilim çizimi yine aynı drawImage ile yapılır, yalnız dx, dy seçiminde pivot çıkarılır — konuşlandırma bu sayfanın sınırında, çizimle temas eden kısımda tutulur.

/**
 * index: 0 uzunlu sol–üst; cols: satır başına hücre; pad*: hücreler arası boşluk.
 * Dönüş: drawSpriteSlice ile uyumlu { sx, sy, sw, sh }.
 */
function gridCellRect(index, cols, cellW, cellH, originX = 0, originY = 0, padX = 0, padY = 0) {
  const col = index % cols;
  const row = (index / cols) | 0;
  const stepW = cellW + padX;
  const stepH = cellH + padY;
  return {
    sx: originX + col * stepW,
    sy: originY + row * stepH,
    sw: cellW,
    sh: cellH,
  };
}

Ölçek ve komşu piksel: sızıntı ve yumuşatma

Kaynak dikdörtgen komşu karelere yapışıksa, hedefte kesirli ölçek ( dw / sw oranı irrasyonel ) veya dönüşüm sonrası alt piksel kayması, doğrusal filtreleme nedeniyle yan taraftaki renkleri «sızdırtır». Tasarımda ara tampon ( bir piksel şeffaf veya kopya kenar ), araçta dışa aktarım extrude veya içeride marj çoğu zaman daha ucuz çözümdür; shader yazmadan Canvas 2D’de tam kontrol sınırlıdır.

Piksel sanatı için ctx.imageSmoothingEnabled = false ve mümkünse tam sayılı hedef boyutları tercih edilir; vektörel ölçekte ise yumuşatma açık kalabilir — ürün stiline göre bağlam bazında ( kare başı ) değiştirmek yaygındır, global ve kalıcı ayarı unutmayın.

Cihaz piksel oranı ( devicePixelRatio ) tuvalin iç çözünürlüğünü büyütür; mantık koordinatları ile çizim koordinatları ayrılmışsa dilim boyutları tutarlı kalmalıdır — ölçek sadece tuval kurulumunda değil, dw, dh planında da gözden geçirilmelidir.

Düzensiz kareler ve harici meta veri köprüsü

Gerçek atlaslar sıkça farklı genişlik ve yüksekliklere sahip kareler içerir; her kare için { name, x, y, w, h } tablosu ( JSON ) okunur ve doğrudan drawSpriteSlice’e iletilir. Bu sayfa JSON şemasını üretmez — güvenli tüketim için şemayı doğrulayın ( pozitif w, h, levha sınırları içinde ).

Araç çıktıları bazen döndürme ve kırpma bilgisi de ekler; Canvas 2D’de ek dönüşüm için save / translate / rotate / restore kullanılır — detay bağlam dokümantasyonunda. Levha sayfası yalnız dikdörtgen dilimi üretildiğini varsayar.

İsim tabanlı çizim ( "walk_03" → dikdörtgen ) bir sözlük önbelleği ile çözülür; dosya değişince sözlüğü yenilemek gerekir — önbellek stratejisiyle uyumlu düşünün.

Hedef dikdörtgen: oyun birimi ve en-boy oranı

Kaynak en–boy oranı ile hedef en–boy oranı farklıysa sprite gerilir; istenmeyen sıkışma için dw, dh seçiminde ya «içine sığdır» ( contain ) ya da «kırp» ( cover ) politikasını kodlayın. Fizik gövdesi ile görsel sınır çoğu zaman aynı değildir; çarpışma kutusu ayrı sayılarda tutulur — levha sayfası yalnız görsel boyutu ilgilendirir.

Negatif dw ile yatay yansıtma tekniği ( aynaya çevirme ) kullanılabilir; bu da bağlam dönüşümü alternatifidir. İki yöntemi aynı varlıkta karıştırmamak için ekip içi tek tercih belirleyin.

Uzamsal sıralama ( önce zemin sonra oyuncu ) için globalCompositeOperation veya sadece çizim sırası yeterlidir; bu levha kullanımından bağımsız ama aynı kare içinde planlanmalıdır.

Bağlam durumu: save, clip ve çizim sırası

Her sprite öncesi save / restore kullanmak maliyetlidir; yalnız dönüşüm veya opaklık değişecekse sarın. Kalıcı olarak bırakılan translate bir sonraki varlığı yanlış yere çizer — kare sonunda bağlamın bilinen bir tabana dönmesi ( restore veya explicit reset ) hata ayıklamayı kolaylaştırır.

clip() ile maskelenmiş çizim, levha dilimini değil sonraki çizim komutlarını kısıtlar; yanlışlıkla tüm sahneyi kesmek sık görülür. Dilim zaten drawImage ile sınırlı alan kopyalar — ek clip genellikle gereksizdir.

Vurgu, seçim parıltısı gibi efektler çoğu zaman ayrı geçici tuval veya ikinci geçiş ister; bu sayfa tek geçişli basit dilim çizimini hedefler. Performans için aynı levha ve aynı filtre ayarıyla toplu çizim gruplamak küçük de olsa kazanç sağlayabilir.

Anti-kalıplar: yanlış argüman sırası ve taşan indeks

Bu dört örnek çoğu zaman birlikte görülür: yanlış parametre sırası bir karede bozulmuş kopya, marj ihmalinde sistematik «yanlış kostüm», taşan indekslerde rastgele komşu pikseller ve çizim döngüsünde ağ çağrısı ise kare süresi dalgalanması üretir. Hepsi, bu sayfada zaten önerilen sarmalayıcı ve ızgara modeli ile sınırlı kalır; yeni API öğrenilmez.

Beş ve dokuz parametreyi karıştırmak: Üç, beş ve dokuz argümanlı aşırı yüklemeler farklı anlamlarda konum ve boy sunar; doğru imzayı ezberlemek yerine yalnız dokuzlu yolu kullanan bir drawSpriteSlice ( veya tek isimli yardımcı ) ile çağrıları kilitleyin. Hata ayıklamada «değerler doğru görünüyor ama görüntü kayıyor» genelde bu karışımın işaretidir.

Marj unutulmuş ızgara: Dışa aktarımda sütunlar arası bir iki piksel boşluk bile, saf col * cellW hesabını kaydırır ve her karede komşu kostümün şeridini gösterir. Tasarım ölçüleriyle birebir örtüşen padX / padY ve gerekiyorsa originX / originY zorunludur; araç önizlemesi ile levha piksel koordinatlarını yan yana doğrulamak hızlı teşhis sağlar.

Levhadan taşan sx / sy veya sw / sh: Kaynak dikdörtgen levha sınırını kesiyorsa sonuç tarayıcıya göre kırpılabilir veya tutarsız görünebilir; özellikle negatif kalanlar oluşturan son kareler animasyonda taşar. İndeksi clamp’lemek, kare sayısını animasyon tarafında sabitlemek veya levha dışına çıkmayı geliştirme ortamında sınır kontrolleriyle tespit etmek üretimde sürprizi azaltır. Komşu kare «sızıntısı» ile karıştırmak için ölçek ve filtre bölümüne bakın — biri geometri taşması, diğeri örnekleme kaynaklıdır.

Yükleme çağrısı çizim döngüsünde: Her requestAnimationFrame turunda new Image() veya ağ tabanlı yükleme başlatmak hem gecikme üretir hem çizimi belirsiz bırakır; levha önceden yüklenip hazır tutulmalı, çizim yalnız hazır referans üzerinden drawSpriteSlice çağırmalıdır. Geç yükleme senaryosu ayrı bir «hazır değil» fazı veya yer tutucu ile yönetilir, kare içinde ağ bloklamaz.

Bu sayfanın sınırı

GPU dokuları, atlas bin-packing araçları ve çoklu çözünürlük ( mipmap ) burada işlenmez. Odak: Canvas 2D drawImage ile dikdörtgen dilim seçmek ve düzenli ızgara matematiğidir.

  • Dokuzlu drawImage parametreleri doğru sırada mı?
  • Izgara marjı ve köken ofseti tasarımla eşleşiyor mu?
  • Ölçekte komşu kare sızıntısı için tasarım tamponu var mı?
  • Animasyon indeksi levha boyutları içinde mi?
  • Bağlam dönüşümü kare sonunda tutarlı bir tabana dönüyor mu?