Bölüm 3 · Sunum 3

Beş yuvalı envanter, bir avuç balon

Boyutu baştan belli diziler ve birçok nesneyi birlikte yöneten paralel diziler.

Sabit kapasitetepe ve BOSMini envanterBalon sahnesisplice

Oyunlarda envanter neden hep sınırlı?

Çantada 5 yuva var, altıncı eşyayı alamıyorsunuz. Geçen ders gördüğümüz push ile sınırsız ekleyebiliyorduk. Sınırı kim, nasıl koyuyor?

İki yol var: uzayan liste, sabit yuvalar

Dinamikpush / popEleman gelince dizi uzar, gidince kısalır. Eleman yoksa yuva da yoktur. Uzunluk baştan bilinmiyorsa: tahmin geçmişi, kule listesi.
SabitSabit kapasiteYuva sayısı baştan bellidir ve değişmez. Boş yuvada özel bir “boş” işareti durur. Kaç yuvanın dolu olduğu ayrı bir sayaçta tutulur.

Sabit model, belleği ve döngü sınırını öngörülebilir kılar. Tasarım açısından da bir kuraldır: “en fazla 5 eşya”.

Can göstergesi: beş yuva, kaçı dolu?

Zelda’daki kalpler, Minecraft’ın 9 yuvalı çubuğu, bir silahın şarjörü: hepsinde yuva sayısı sabit, değişen yalnızca doluluk.

Fikir dört adımda

  1. Üst sınırı sabitle: MAKS.
  2. Diziyi MAKS uzunluğunda oluştur ve BOS ile doldur.
  3. Ekle: yigin[tepe] = deger; tepe++. push() yok.
  4. Çıkar: tepe--; değeri oku; yuvaya yeniden BOS yaz. pop() yok.

Yığın mantığı metotlara bağlı değil: “sona ekle, sondan çıkar” davranışı indeks ve atamayla da kurulur. Birçok dilde hazır push / pop zaten yoktur.

tepe: sıradaki boş yuvayı gösteren parmak

kilic0
iksir1
anahtar2
BOS3 ▲ tepe
BOS4

MAKS = 5 · tepe = 3 · envanter.length her zaman 5

tepe iki şeyi birden söylerKaç yuva dolu (3) ve yeni eşyanın nereye yazılacağı (indeks 3).
BOS“Bu yuvada bir şey yok” demek için seçtiğimiz özel değer: -1, "" gibi.

Ekleme: yiginEkle

Önce sınıra bakıyor: yer yoksa false döner ve hiçbir şey eklemez. Yer varsa tepe’nin gösterdiği yuvaya yazar, sonra tepe’yi bir ilerletir.

Çıkarma: yiginCikar

  1. Yığın boşsa BOS döner.
  2. tepe’yi bir geri al: artık son dolu yuvayı gösteriyor.
  3. Oradaki değeri bir kenara al.
  4. Yuvaya yeniden BOS yaz.
  5. Aldığın değeri döndür.

Adım adım izleyelim

Adımİşlemyigintepe
Başlangıç—[-1, -1, -1, -1, -1]0
1yiginEkle(7)[7, -1, -1, -1, -1]1
2yiginEkle(3)[7, 3, -1, -1, -1]2
3yiginCikar() → 3[7, -1, -1, -1, -1]1

yigin.length hep MAKS. Dolu eleman sayısı tepe. Döngü bu yüzden i < tepe ile sınırlanır.

Skor listesinde boş yuvayı 0 ile işaretlesek olur mu?

Oyuncu gerçekten 0 puan aldıysa ne olacak?

Boş işareti, geçerli bir değerle çakışmamalı

Tahmin 1–10 arasıBOS = -1 uygun. −1 hiçbir zaman geçerli bir tahmin olamaz.
Skor 0 olabilirO zaman 0 “boş” anlamına gelemez; yine -1 gibi imkânsız bir değer seçilir.
Metin listeleriÇoğunlukla "" ya da "BOS" kullanılır.

tepe tutmak istemezseniz “ilk BOS yuvaya yaz” diye tarayabilirsiniz. Fikir aynıdır; doluluk hesabı döngüyle yapılır.

Hangisi ne zaman?

YaklaşımBoyutBoş yerNe zaman uygun?
push / poplength değişirEleman yoksa yuva da yokUzunluk baştan bilinmiyor; geçmiş listesi
Sabit kapasite + BOS + tepelength === MAKS, sabitYuva var, değeri BOSEnvanter, can, sabit düşman havuzu, sınırlı geri al

Kule kurma ve skor geçmişi gibi örnekleri push / pop ile yazmak kısa ve okunaklıdır. Sabit model “en fazla N yer” kuralını koda gömmek için bilinmelidir.

Başka dillerde bu bir tercih değil, zorunluluk

JavaScript’te diziler dinamikpush ile büyür, pop ile küçülür. Doğru boyutu baştan seçmek çoğu zaman bir tasarım tercihidir.
C, C++, Pascal ve gömülü sistemlerdeDizinin boyutu tanımlanırken belirlenir, sonradan büyütülemez. Küçük seçilirse taşma (overflow) ve hatalı bellek yazımı, büyük seçilirse bellek israfı olur.
Oyun motorlarında“En fazla 20 adım geri al”, “en fazla 3 açık menü”, “ekranda en fazla 100 mermi” gibi üst sınırlar çok yaygındır. Kapasite baştan kesilir ki oyun ortasında bellek sürprizi olmasın. Bu fikre nesne havuzu (object pool) denir.

Mini envanter: tıkla ekle, sol alttan çıkar

Tuvale birkaç kez tıklayın, sonra ÇIKAR kutusuna basın. length hep 5 kalıyor; değişen yalnızca tepe. Altıncı eşyayı eklemeyi deneyin.

Listeyi yuva kutularına çevirin

Sabit envanter: yuvalar ve tepe
  1. draw içindeki metin listesini kaldırın; yan yana 5 dikdörtgen yuva çizin.
  2. Dolu yuva: dolu kutu ve eşyanın adı. Boş yuva: soluk çerçeve ve “BOS”.
  3. tepe indeksinin altına küçük bir üçgen koyun. tepe === MAKS iken “dolu” yazın.
  4. yiginEkle, yiginCikar ve mousePressed’e dokunmayın; yalnızca çizimi değiştirin.

Ekranda beş balon olacak. Her biri oval ve farklı boyda.

Bir balonu tarif etmek için kaç sayıya ihtiyacınız var? Bunları kaç dizide tutarsınız?

Dört paralel dizi, aynı indeks

balonX

1200
3001
4802

balonY

1200
1801
1002

balonRX · balonRY

32 · 420
50 · 321
24 · 362

Konum için balonX ve balonY; boyut için yatay ve dikey yarıçap: balonRX, balonRY.

ellipse çap ister: genişlik = 2 × rx, yükseklik = 2 × ry. Daire için rx ile ry eşittir.

Hedef: balon sahnesi

Bu, birazdan sizin yazacağınız sahnenin bir örneği. Her diziye birer sayı ekleyin: altıncı balon belirir.

Paralel dizilerde ekleme ve çıkarma hep birlikte

Diziler farklı uzunlukta kalırDöngü, yeni balon için balonY[i]’yi okumaya çalışır ve undefined bulur.
KuralBir diziye ne yapıyorsanız hepsine yapın: dört push, dört pop.

Sık karşılaşılan dizi hataları

BelirtiOlası nedenÇözüm
Ekranda undefinedGeçersiz indeks: üç elemanlı dizide d[3]i < dizi.length koşulunu kontrol et
Son satırda fazladan boş değeri <= length ile sınır aşımıi < length kullan
push sonrası liste görünmüyordraw() içinde dolaşma yokfor ile ekrana yazdır
Balonlar üst üste ya da garip boydaParalel dizide indeks kaymasıX, Y, RX, RY’yi aynı i ile oku
Bir balon “kaybolmuş” gibiYalnızca bir diziye push / popEkleme-çıkarma bütün dizilerde birlikte

Balon sahnenizi çizin

Hedef: paralel dizilerle oval balonlar
  1. balonX, balonY, balonRX, balonRY dizilerini kurun. İsterseniz bir de renk dizisi.
  2. for içinde ellipse ile çizin. Bonus: kısa bir line ile ip.
  3. Kâğıtta: yalnızca balonX.push(...) yapıp diğer üç diziyi unutan arkadaşınızın ekranında ne bozulur? Bir cümleyle yazın.

Örnek olay: kule yığını

Mini kule sketch’inde üç kule kuruldu, sonra bir kez GERİ AL’a basıldı. Dizilerde ne kaldı, ekranda kaç kule görünüyor?

  1. kuleX[0] hangi kuleyi temsil eder: ilk kurulanı mı, son kurulanı mı?
  2. GERİ AL neden her iki dizide pop ister?
  3. length === 0 iken pop çağırmak neden risklidir?

Ortadaki balonu patlatmak istiyoruz. pop() işe yarar mı?

pop yalnızca sondaki elemanı çıkarıyor. Oyuncu ise istediği balona tıklıyor.

splice(i, 1): aradan bir eleman çıkar

1200
3001
4802
2203

↓ balonX.splice(1, 1)

1200
4801
2202

sonrakiler bir yuva sola kayar · length 3 olur

dizi.splice(i, 1)İndeksi i olan elemanı çıkarır; length bir azalır. Boşluk kalmaz.
Paralel dizilerdeAynı i için her dizide splice(i, 1) çağrılır. Biri unutulursa indeksler kayar.

Balon Patlat: hedeflediğimiz oyun

Balona tıklayınca o balon bütün dizilerden splice ile çıkar; boşluğa tıklayınca bütün dizilere push edilir. Birazdan bunu siz yazacaksınız.

Balon Patlat (1/3): mekaniği siz yazın

  1. Paralel dizileri ve birkaç başlangıç balonunu kurun. Class ya da nesne kullanmayın.
  2. setup / draw: arka plan, skor, for + ellipse.
  3. mousePressed akışı:
    • Tıklanan nokta hangi balonun içinde? İndeksini bulun.
    • Bulunduysa: bütün dizilerde splice(i, 1); skoru artırın.
    • Bulunamadıysa: yeni bir balonu bütün dizilere push edin.

Balon Patlat (2/3): yardımcıları yapay zekâdan isteyin

A · Nokta elipsin içinde mi?noktaElipsIcindeMi(px, py, cx, cy, rx, ry) → boolean. Elips denklemini kullansın.
B · Çakışmayan yeni balonyeniBalonOlustur(…): tuvalin içinde, diğer balonlara değmeyen rastgele bir balon. Yer bulamazsa null. Dönüş: [x, y, rx, ry].
C · Uyumlu renkuyumluBalonRengi() → [r, g, b]. Pastel, birbirine yakışan renkler.
Sonra denetleyinnoktaElipsIcindeMi gerçekten elips mi kullanıyor, yoksa dist ile daire mi? yeniBalonOlustur kenarları ve diğer balonları hesaba katıyor mu? Siz splice / push’u bütün dizilerde birlikte yaptınız mı?

Balon Patlat (3/3): makyaj

  1. Mekanik çalışıyorsa sketch’in tamamını yapıştırıp görsel ve ses cilasını isteyin: patlayınca küçülerek kaybolma, yeni balonda büyüyerek belirme, kısa ses efektleri. “Mekaniği bozma, class kullanma.”
  2. “Neleri değiştirdin, bu ders düzeyinde madde madde açıkla” deyin. Anlamadığınız satır için devam sorusu sorun.
  3. Önce yalnızca fikir isteyin: “Bu oyunu daha eğlenceli yapacak 6–8 makyaj fikri öner; kod yazma.” Beğendiğinizi sonra uygulatın.
HatırlatmaAnimasyon için çoğu zaman ek diziler gelir: balonOlcek, balonAlfa. Onların da birlikte push / splice edildiğini kontrol edin. Düzeyinizin üstünde bir parça gelirse “daha basit anlat ya da sadeleştir” demek doğaldır.

Yapay zekâyla: paralel diziden nesneye

  1. x[i], y[i], r[i] için bir nesne biçimi tasarlayın: {x, y, r}.
  2. Yapay zekâdan paralel dizi ↔ nesne dizisi dönüşümünün iskeletini isteyin. Henüz sınıf kullanmasın.
  3. Bu aşamada hangi modelin daha okunur olduğunu bir cümleyle not edin.

MAKS = 3. Bu çağrılardan sonra tepe kaç, yigin nasıl?

  1. tepe 3 · [4, 9, 7]
  2. tepe 2 · [4, 9, -1]
  3. tepe 3 · [9, 2, 7]
  4. tepe 4 · [4, 9, 2, 7]

Dördüncü ekleme reddedilir: yer yok. Sonra en üstteki 2 çıkar. Dizi asla 3 yuvayı aşmaz.

d = [5, 8, 1, 6] iken d.splice(1, 1) sonrası d[2] kaç?

  1. 1
  2. 6
  3. 8
  4. undefined

8 çıkar; dizi [5, 1, 6] olur. Sonraki elemanlar bir yuva sola kaydığı için indeksleri değişir.

Can sayısı 0 olabilen bir oyunda, “bu yuvada oyuncu yok” işareti olarak hangisi güvenli?

  1. 0
  2. -1
  3. 3
  4. Hepsi olur

0 geçerli bir can değeri; “boş” ile karışır. İşaret, oyunda asla oluşmayacak bir değer olmalı.

Merak edenler için kaynaklar

Bugün ne öğrendik?

  • Sabit kapasite: dizi baştan MAKS uzunlukta kurulur, boş yuvalar BOS ile işaretlenir.
  • tepe hem dolu yuva sayısını hem sıradaki boş yuvayı gösterir.
  • Ekleme: yigin[tepe] = deger; tepe++. Çıkarma: tepe-- ve yuvaya BOS.
  • BOS geçerli bir oyun değeriyle çakışmamalı.
  • Paralel dizilerde push, pop, splice her zaman bütün dizilerde birlikte yapılır.
  • splice(i, 1) aradan eleman çıkarır; sonrakiler sola kayar.

Sıradaki ders

Kontrol oyuncuya geçiyor

Klavye: basılı tutmak, bir kez basmak ve tuş kodları.