Ana Başlık 3. Menü–Oyun–Bitiş Akışı ve Kaç-Kaç

Amaç: Durum makinesini menü–oyun–bitiş akışında uygulayarak kaç-kaç benzeri mini oyunu tamamlamak.

Öğrenme Çıktıları:

Anahtar Sözcükler: menü, bitiş, geçiş, kaç-kaç, oyun döngüsü, durum

Giriş

Durum, girdi ve geçiş kuralları bilindikten sonra sıra bunları tek sketch’te birleştirmeye gelir. Menü ekranı yönerge ve Start bekler; oyun durumu hareket, çarpışma veya kaçış kuralını çalıştırır; bitiş ekranı skor veya sonucu gösterip yeniden başlatma sunar.

Kaç-kaç benzeri örnek, önceki ana başlıkların (hareket, girdi, çarpışma fikri) durum makinesiyle örülmesidir. Amaç AAA üretim değil; okunabilir oyunDurumu dallarıyla bütünleşik bir akış bitirmektir. Bu alışkanlık, sonraki XOX projesinde menü ve tur yönetimini de kolaylaştırır. Bundan sonra gelen alt başlıklarda önce veri modeli, sonra hamle kuralı, en sonda çizim ve etkileşim kurulur. Her yeni fonksiyon mümkün olduğunca küçük bir örnekte çalıştırılır; böylece hatanın kaynağı daha kolay bulunur. Anlatımda önce ilgili yapının ne işe yaradığı açıklanır, ardından kod satır satır okunur. Terimler ilk geçtikleri yerde kısa bir tanımla verilir; gerekirse tablo veya görsel ile desteklenir. Örnek sketch’ler tek bir mekaniğe odaklanır; böylece yan konular okumayı dağıtmaz. Parametreleri değiştirip yeniden çalıştırmak, kuralın sınırını görmenin en hızlı yoludur. Bu ana başlığın çıktısı, sonraki oyunda yeniden kullanılabilecek bir iskelet oluşturur.

3.1. Kaç-Kaç Top: Kurallar ve Durum Tasarımı

Aşağıdaki Kaç-Kaç Top mini oyunu üç durumu birleştirir: menüde SPACE ile başlama, oyun içinde WASD ile hedef toplama, topa değince bitiş, R ile menüye dönüş. Skor yalnızca hedefe dokununca artar; bu yüzden köşede beklemek işe yaramaz, oyuncu topun yakınına gitmeyi göze almak zorundadır.

Oyun kuralları:

3.2. Tam Sketch ve Akışın Okunması

Kod 1.

javascript
const DURUM_MENU = "menu";const DURUM_OYUN = "oyun";const DURUM_BITIS = "bitis"; const BOYUT = 36;const TOP_YARICAP = 20;const HEDEF_YARICAP = 12;const HIZ_OYUNCU = 200;const UZAK_MESAFE = 90; // hedef oyuncuya bu kadar uzakta doğar let oyunDurumu;let oyuncuX, oyuncuY;let topX, topY, hizX, hizY;let hedefX, hedefY;let skor; function setup() {  createCanvas(520, 360);  noStroke();  oyunDurumu = DURUM_MENU;} function draw() {  if (oyunDurumu === DURUM_MENU) {    cizMenu();  } else if (oyunDurumu === DURUM_OYUN) {    guncelleOyun();    cizOyun();  } else if (oyunDurumu === DURUM_BITIS) {    cizBitis();  }} function cizMenu() {  background(28, 32, 48);  fill(255);  textSize(28);  textAlign(CENTER, CENTER);  text("Kac-Kac Top", width / 2, height / 2 - 40);  textSize(16);  text("Topa degmeden hedefleri topla", width / 2, height / 2);  text("SPACE — başla", width / 2, height / 2 + 50);} function guncelleOyun() {  let dt = deltaTime / 1000;   if (keyIsDown(LEFT_ARROW) || keyIsDown(65)) oyuncuX -= HIZ_OYUNCU * dt;  if (keyIsDown(RIGHT_ARROW) || keyIsDown(68)) oyuncuX += HIZ_OYUNCU * dt;  if (keyIsDown(UP_ARROW) || keyIsDown(87)) oyuncuY -= HIZ_OYUNCU * dt;  if (keyIsDown(DOWN_ARROW) || keyIsDown(83)) oyuncuY += HIZ_OYUNCU * dt;   oyuncuX = constrain(oyuncuX, 0, width - BOYUT);  oyuncuY = constrain(oyuncuY, 0, height - BOYUT);   topX += hizX * dt;  topY += hizY * dt;  if (topX - TOP_YARICAP < 0) { topX = TOP_YARICAP; hizX = -hizX; }  if (topX + TOP_YARICAP > width) { topX = width - TOP_YARICAP; hizX = -hizX; }  if (topY - TOP_YARICAP < 0) { topY = TOP_YARICAP; hizY = -hizY; }  if (topY + TOP_YARICAP > height) { topY = height - TOP_YARICAP; hizY = -hizY; }   let px = oyuncuX + BOYUT / 2;  let py = oyuncuY + BOYUT / 2;   if (dist(px, py, hedefX, hedefY) < HEDEF_YARICAP + BOYUT / 2) {    skor += 1;    hedefYerlestir();  }   if (dist(px, py, topX, topY) < TOP_YARICAP + BOYUT / 2) {    oyunDurumu = DURUM_BITIS;  }} function cizOyun() {  background(22, 28, 42);  fill(120, 230, 140);  ellipse(hedefX, hedefY, HEDEF_YARICAP * 2, HEDEF_YARICAP * 2);  fill(100, 200, 255);  rect(oyuncuX, oyuncuY, BOYUT, BOYUT);  fill(255, 190, 70);  ellipse(topX, topY, TOP_YARICAP * 2, TOP_YARICAP * 2);  fill(255);  textSize(16);  textAlign(LEFT, TOP);  text("Skor: " + skor, 12, 12);} function cizBitis() {  background(35, 25, 30);  fill(255);  textSize(26);  textAlign(CENTER, CENTER);  text("Oyun bitti", width / 2, height / 2 - 50);  textSize(18);  text("Skor: " + skor, width / 2, height / 2);  textSize(14);  text("R — menüye dön", width / 2, height / 2 + 50);} function hedefYerlestir() {  let px = oyuncuX + BOYUT / 2;  let py = oyuncuY + BOYUT / 2;  let kenar = HEDEF_YARICAP + 10;  hedefX = random(kenar, width - kenar);  hedefY = random(kenar, height - kenar);  let deneme = 0;  // Güvenlik sınırı: UZAK_MESAFE tuvale göre çok büyük seçilirse uygun konum  // bulunamaz ve draw() içindeki bu döngü sketch'i dondurur.  while (dist(px, py, hedefX, hedefY) < UZAK_MESAFE && deneme < 50) {    hedefX = random(kenar, width - kenar);    hedefY = random(kenar, height - kenar);    deneme = deneme + 1;  }} function oyunuSifirla() {  oyuncuX = width / 2 - BOYUT / 2;  oyuncuY = height / 2 - BOYUT / 2;  topX = random(60, width - 60);  topY = 50;  hizX = random(-100, 100);  hizY = random(80, 140);  skor = 0;  hedefYerlestir();} function keyPressed() {  if (oyunDurumu === DURUM_MENU && key === " ") {    oyunuSifirla();    oyunDurumu = DURUM_OYUN;  }  if (oyunDurumu === DURUM_BITIS && (key === "r" || key === "R")) {    oyunDurumu = DURUM_MENU;  }}

Bu sketch, önceki ana başlıklardaki deltaTime, klavye, constrain, çarpışma (dist) ve önceki bölümlerdeki fonksiyon modülerliğini tek akışta toplar. Sonraki ana başlıkta DURUM_OYUN içinde 3×3 tahta çizimi ve tıklama ile hamle eklenecektir; menü ve bitiş aynı oyunDurumu değişkeniyle yönetilmeye devam edecektir.

draw() her zaman başlangıç noktasıdır: üç if / else if dalından yalnızca biri, oyunDurumu'nun o anki değerine göre çalışır. Oyun aşamasına girildiğinde guncelleOyun() önce klavye durumunu okuyup oyuncu konumunu günceller, ardından topu duvarlardan sekmeli biçimde hareket ettirir, sonra dist() ile önce hedefe değilip değilmediğine (skor + yeni hedef), en son da topa çarpılıp çarpılmadığına bakar. Çarpışma tespit edildiği an fonksiyon içinde oyunDurumu = DURUM_BITIS atanır; bu atamadan sonra aynı frame'de cizOyun() yine çağrılır (son sahneyi göstermek için), fakat bir sonraki draw() çağrısında artık else if (oyunDurumu === DURUM_BITIS) dalı çalışacak ve cizBitis() devreye girecektir. oyunuSifirla() fonksiyonu, yalnızca menüden oyuna geçişte çağrılarak her yeni turun rastgele ama tutarlı bir başlangıç konumuyla başlamasını sağlar; bu, önceki bölümlerde tanıtılan random() fonksiyonunun durum makinesiyle birleştirilmiş hâlidir.

3.3. Geçişlerin Doğrulanması ve Uygulama Etkinlikleri

Aşağıdaki Yapay Zekâ Etkileşimi etkinlikleri kavramdan diyagrama ve koda doğru sıralanır. İlk ikisi kısadır (kavram çıkarımı ve Mermaid diyagramı); ardından gelen Hafıza kartları mini oyun projesi bu zamana kadar öğrenilenleri bir araya getirir. Son etkinlik ise unutulan geçişleri yakalamaya odaklanan kısa bir hata ayıklama alıştırmasıdır. İstenen etkinlik seçilebilir; hepsini yapmak pekiştirir.

Yapay Zekâ Etkileşimi: Oyunlardan durum makinesi çıkarmak Tahmini süre: 20–25 dakika.

Amaç: Tanıdık bir oyunun aşamalarını durum / olay / geçiş diline çevirmek. Bu etkinlikte tam oyun kodu yazmanız beklenmez.

Adım 1 — Önce kendiniz yazın: Yukarıdaki tablodan veya bildiğiniz bir oyundan birini seçin (satranç, hafıza kartları, Tetris, XOX…). En az üç durum bulun. Her durum için iki kısa not: ekranda ne görünür? Hangi tuş / tıklama işe yarar?

Adım 2 — Örnek istem: "Yeni başlayan bir oyun programcısıyım. Şu oyunu durum makinesi gözüyle anlat: [oyun adı]. (1) En az 3–5 anlamlı durum (menü / hazır / oynuyor / duraklatma / bitti vb. — oyuna uygun olanlar). (2) Her durum için: ekranda tipik olarak ne görünür, hangi girdiler anlamlıdır? (3) Geçişleri tablo yap: mevcut durum | olay | yeni durum (en az 5 satır). (4) Bu oyunda durum makinesi olmasa (her şey dağınık boolean / if olsa) hangi tür hatalar kolay çıkar? İki somut örnek ver. Türkçe yaz; kod yazma; formül ezberletme."

Adım 3 — Kontrol ve kendi kararınız: YZ’nin verdiği durum listesini okuyun. Yanlış veya gereksiz bir şey var mı? (Örnek: basit satrançta “duraklatma menüsü” şart değildir; YZ bazen böyle ekstra durum ekler.) Sonra kendi mini oyununuz için (Kaç-Kaç Top veya aklınızdaki küçük oyun) şu üç soruyu yanıtlayın:

  1. Hangi üç durum şart?
  2. Her biri ne işe yarar? (birer kısa cümle)
  3. Neden bu üçü yeterli / gerekli?

Örnek yanıt: “Menü, oyun, bitiş şart. Menüde başlatma, oyunda hareket, bitişte skor gösterilir; üçünü tek ekranda karıştırmak istemem.”

Not: YZ bazen yanlış kural veya uydurma örnek verir. Yanıtı ezberlemeyin; önce kendi bilginizle (veya kısa bir aramayla) kontrol edin, doğru gelenleri kullanın.


Yapay Zekâ Etkileşimi: Mermaid ile durum diyagramı çizdirmek Tahmini süre: 25–35 dakika.

Amaç: Geçiş tablosunu Mermaid metnine çevirmek; Live Editor’da diyagramı görmek; isteğe bağlı olarak ok/kutu renkleri gibi görünümü Mermaid koduyla süslemek. Diyagram p5.js kodu yazmaz; yalnızca “hangi olay hangi duruma götürür?” sorusunu resmeder.

Adım 1 — Önce tabloyu yazın: Kaç-Kaç Top veya kendi mini oyununuz için geçiş tablosu yazın: mevcut durum | olay | yeni durum. En az dört satır. Ayrıca bir olmayan geçiş not edin (ör. menüde R’ye basınca hiçbir şey olmamalı).

Adım 2 — Örnek istem A (diyagram): "Aşağıdaki durum geçiş tablosunu Mermaid stateDiagram-v2 sözdizimine çevir. Başlangıç için [*] --> ... kullan. Ok etiketlerinde olay adlarını yaz (SPACE, carpisma, R gibi). Yalnızca Mermaid kodu ver; açıklama kısa tut.

[Tabloyu buraya yapıştır]"

Adım 3 — Ekranda kontrol: Çıkan metni Mermaid Live Editor adresine yapıştırın. Diyagram çizilmezse veya ok eksikse YZ’ye “şu hata / şu ok yok, düzelt” diye yazın. Sonucu kitaptaki menü / oyun / bitiş durum diyagramıyla karşılaştırın: menü, oyun, bitiş durumları ve SPACE / çarpışma / R okları var mı?

Adım 4 — Makyaj (görünüm): Diyagram doğruysa görünümü iyileştirmek için YZ’ye sorun. Amaç: okları veya durum kutularını renklendirmek, önemli geçişi (ör. çarpışma → bitiş) vurgulamak. Gelen stil kodunu yine Live Editor’da deneyin; çalışmazsa hata mesajını YZ’ye yapıştırıp düzeltmesini isteyin.

Örnek istem C (makyaj): "Aşağıdaki Mermaid stateDiagram-v2 kodunu koru; geçişler aynı kalsın. Şunları ekle / ayarla: (1) menu kutusu yeşilimsi, oyun mavimsi, bitis kırmızımsı olsun (veya benzer net renkler). (2) oyun --> bitis okunu diğerlerinden daha belirgin / kırmızı yap. (3) Mümkünse ok kalınlığı veya etiket stilini de biraz ayır. Yalnızca çalışan Mermaid kodunu ver. Hangi satırların stil için olduğunu kısa madde madde açıkla. Live Editor’da denenecek.

[Adım 2’deki Mermaid kodunu buraya yapıştır]"

İsterseniz kendi listenizi sorun: “yalnızca okları renklendir”, “başlangıç noktasını büyüt”, “Türkçe durum adları kullan (menu → menü)” gibi. Her seferinde çıktıyı Live Editor’da kontrol edin; Mermaid sürümüne göre bazı stil komutları çalışmayabilir — YZ’den alternatif sözdizimi isteyin.

Adım 5 — Örnek istem B (isteğe bağlı, kod bağlantısı): "Bu Mermaid diyagramındaki her ok, p5.js’te kabaca nereye yazılır? oyunDurumu = ... atamasını draw içinde mi keyPressed içinde mi beklersin? Kod yazma; madde madde eşleştir."

Adım 6 — Dikkat: YZ bazen olmayan bir duruma ok çizer, durum diyagramı yerine akış şeması (flowchart) üretir veya Live Editor’da çalışmayan stil satırı ekler. Sizden istenen önce doğru geçişler, sonra makyajdır. Kutular = durumlar, oklar = olaylı geçişler.


Yapay Zekâ Etkileşimi: Hafıza kartları — durum diyagramından çalışan oyuna Tahmini süre: 60–90 dakika (mekanik öğrenme + kod + YZ ile onarım).

Amaç: Yukarıdaki tabloda ve Hafıza kartları durum diyagramında özetlenen Hafıza kartları oyununu p5.js’te yazmak. Önce mekaniği netleştirin, sonra durum diyagramıyla planlayın, sonra kodu siz yazın. Takıldığınız veya çalışmayan yerlerde YZ’den yardım alın; YZ’yi hem “onarım” hem “öğretmen” gibi kullanın. Bu görev; durum makinesi, diziler, fare tıklaması, fonksiyonlar ve draw / olay ayrımını bir araya getirir. Kaç-Kaç Top ve önceki bölüm sketch’lerindeki desenlerden yararlanın — tüm oyunu YZ’ye baştan yazdırmayın.

Adım 1 — Mekaniği anlayın (YZ’siz + örnek siteler): Bu tür oyunlara İngilizcede Memory, Concentration veya Matching pairs denir. Kapalı kartlardan sırayla iki tanesini açarsınız; eşleşirse açık kalır, değilse kısa süre sonra kapanır; tüm çiftler bulununca biter. Önce en az bir çevrimiçi örneği 2–3 dakika oynayın (kayıt gerekmez):

Kuralların kısa özeti için: Concentration (card game) — Wikipedia.

İsteğe bağlı istem (mekanik, kod yok): "Hafıza / Memory / Concentration kart oyununu yeni başlayan bir oyun programcısına anlat. (1) Nasıl oynanır? Adım adım. (2) Bu türden bilinen 3–4 oyun veya varyasyon adı ver (ör. Memory, Pairs…). (3) Online ücretsiz oynayabileceğim 2–3 site öner (mümkünse kayıt istemeyen). (4) Programcı gözüyle: hangi anlar ayrı durum olmalı? (ilk kart / ikinci kart / eşleşme kontrolü / bitti gibi) Kod yazma; Türkçe yaz."

Adım 2 — Durum diyagramı (önce plan): Kitaptaki Hafıza kartları satırını ve buna karşılık gelen durum diyagramını temel alın. Kendi geçiş tablonuzu yazın (mevcut durum | olay | yeni durum); ardından Mermaid stateDiagram-v2 ile çizin (Mermaid Live). İsterseniz dışarıda basit bir Menü ve sonda Bitti ekleyin (Kaç-Kaç Top’taki gibi SPACE / R). Diyagram netleşmeden koda geçmeyin.

Adım 3 — Sizin kodunuz (mini oyun): Web Editor’de hedef: küçük bir ızgara (ör. 4×3 → 6 çift). Öneri (zorunlu şablon değil):

Kaç-Kaç Top’taki oyunDurumu + draw dallanması + oyunuSifirla fikrini yeniden kullanın. Kart karıştırma, tıklanan hücreyi bulma, eşleşme kontrolü sizin yazacağınız kısımlardır.

Adım 4 — Takılınca / çalışmayınca YZ (onarım + öğretmen): Sketch’in tamamını (veya bozulan fonksiyonu) yapıştırın. YZ’den hem düzeltme hem açıklama isteyin; körü körüne yapıştırmayın — gelen her değişikliği okuyun.

Örnek istem — hata / eksik kısım: "p5.js Hafıza kartları sketch’im var (class yok, ek kütüphane yok). Durum makinesi kullanıyorum. Şu sorun var: [ör. ikinci kart açılmıyor / eşleşmeyen kartlar kapanmıyor / tüm çiftler bulununca bitişe geçmiyor / tıklama yanlış kartı seçiyor].

(1) Önce öğretmen gibi anlat: bu hata hangi durum geçişinde veya hangi dizide kaynaklanıyor olabilir? (2) Sonra yalnızca gerekli satırları düzelt; tüm oyunu baştan yazma. (3) En sonda: neyi neden değiştirdiğini madde madde, bu ders düzeyinde açıkla (oyunDurumu, dizi, mousePressed, millis…).

Kod:"

(Kendi sketch’inizi yapıştırın. Konsolda kırmızı hata varsa onu da ekleyin.)

Örnek istem — yazamadığınız bir parça: "p5.js’te class kullanmadan, paralel dizilerle 4×3 hafıza tahtası için yalnızca şu fonksiyonu yaz: kartlariKaristir(). 6 çift (değerler 0–5, her biri iki kez) üretip karıştırsın. Kısa yorum ekle. Başka dosya / kütüphane yok."

Adım 5 — Kontrol listesi:

Sıra Sizde: Yukarıdaki etkinliğin Adım 2–3’ünü YZ’siz deneyin: Hafıza kartları için geçiş tablosu + Mermaid diyagramı yazın; ardından mümkün olduğunca kendi başınıza küçük bir p5 sketch’i kurun (önce 2×2 = 2 çift bile yeter). Takıldığınız yerde Adım 4’teki YZ istemlerine geçin.

Yapay Zekâ Etkileşimi: Eksik Geçiş Tahmini süre: 15–20 dakika.

Amaç: Unutulan geçiş bug’larını yakalamak.

Adım 1: Menüden oyuna geçiş varken bitişten menüye dönüşü “unutan” bir iskelet yazın veya yazdırın. Adım 2: Yapay zekâya bug’ı buldurun; sizin teşhisinizle karşılaştırın. Adım 3: Düzeltmeyi kendi sketch’inizde uygulayıp üç ekranı da gezin.

Özet

Menü–oyun–bitiş akışı, durum makinesinin tek bir sketch içinde bütünleşik olarak uygulandığı en yaygın biçimdir. Kaç-Kaç Top örneğinde menü ekranı yönergeyi gösterip SPACE bekler; oyun aşamasında kare biçimindeki oyuncu WASD veya ok tuşlarıyla deltaTime’a bağlı hızda hareket eder, top duvarlardan sekerek dolaşır, hedefe temas skoru bir artırıp hedefi yeniden yerleştirir, topa temas ise bitiş ekranına götürür ve orada R tuşu menüye döndürür. Skor yalnızca hedefe dokununca arttığı için köşede beklemek işe yaramaz; kural, topun yakınına gitmeyi göze almayı gerektirir.

Kodun okunma sırası her zaman draw ile başlar: üç dallı if / else if zincirinden yalnızca oyunDurumu’nun o anki değerine karşılık gelen dal çalışır. Oyun dalında önce klavye okunup oyuncu konumu güncellenir ve constrain ile tuval içinde tutulur, ardından top hareket ettirilip kenarlarda hızı ters çevrilir, en sonda dist ile önce hedef, sonra top teması denetlenir. Çarpışma anında atanan yeni durum, aynı frame’de değil bir sonraki draw çağrısında bitiş ekranını devreye sokar. oyunuSifirla yalnızca menüden oyuna geçişte çağrıldığı için her tur rastgele ama tutarlı bir başlangıçla kurulur. Aynı oyunDurumu omurgası, sonraki ana başlıktaki XOX projesinde tur tabanlı tahta verisiyle birleştirilir.

Neler Öğrendik?

Soru 1 Start tuşu tipik olarak hangi geçişi yapar?

A) menü → oyun

B) bitiş → DNS

C) oyun → CSS

D) menü → yazıcı

E) oyun → setup silme

Soru 2 Bitiş ekranında hareket kodu neden genelde çalışmaz?

A) Çünkü güncelleme yalnızca oyun durumunda yapılmalıdır

B) Çünkü p5.js menüyü desteklemez

C) Çünkü dizi yasaktır

D) Çünkü tuval yoktur

E) Çünkü dist yoktur

Soru 3 Kaç-kaç örneğinin pedagojik amacı nedir?

A) Durum + hareket + kuralı bir arada göstermek

B) AAA fizik motoru yazmak

C) Sunucu cluster kurmak

D) SQL öğretmek

E) GPU sürücüsü yazmak

Soru 4 Yeniden dene akışı çoğu zaman nereye döner?

A) menü veya oyun başlangıç durumuna

B) Rastgele CSS dosyasına

C) Git history’ye

D) Yazıcı kuyruğuna

E) DNS cache’e

Neler Öğrendik Yanıt Anahtarı

Soru 1: Doğru Cevap: A

Soru 2: Doğru Cevap: A

Soru 3: Doğru Cevap: A

Soru 4: Doğru Cevap: A

Yararlanılan Kaynaklar

Bu içerik aşağıdaki kaynaklardan derlenerek hazırlanmıştır: