T: tema · Z: kodu büyüt · S: konuşmacı notları · F: tam ekran · Esc: genel bakış · ⌂ ders haritası
OYUNLARDA YAPAY ZEKÂ · HAFTA 2
Sonlu Durum Makineleri
Durumlar, geçişler, hiyerarşik FSM — NPC beyninin en klasik hâli
PRESS ▶ TO START

Bu derste oyunun haritası 🗺️

  1. Durum ve geçiş — trafik ışığından Pac-Man’e
  2. Bir nöbetçi tasarlayalım — canlı FSM
  3. Kod: üç yol — switch, durum sınıfları, geçiş tablosu
  4. Sorunlar — titreme, durum patlaması, spagetti
  5. Hiyerarşik ve yığınlı FSM — durumların içinde durumlar
  6. Gerçek oyunlarda — Doom, Metal Gear, Hollow Knight, Unity Animator
  7. Kütüphaneler — UnityHFSM, PlayMaker, StateTree, XState, transitions
🎮 Bu dersin sonunda…

…devriye gezen, ses duyunca araştıran, sizi görünce kovalayan ve canı azalınca kaçan bir düşman tasarlayabilecek, onu üç farklı şekilde kodlayabilecek ve bir FSM kütüphanesini projenize ekleyebileceksiniz.

🤔 ISINMA: BİR GİZLİLİK OYUNUNDAKİ NÖBETÇİYİ ANLATIN

“Normalde devriye gezer. Bir ses duyarsa gidip bakar. Sizi görürse kovalar, yaklaşınca ateş eder. Canı azalırsa kaçar.”

  • Kalın kelimeler: nöbetçinin durumları — aynı anda sadece birindedir.
  • “-ırsa / -erse” ile başlayan kısımlar: geçişler ve koşulları.
  • Tebrikler: az önce bir sonlu durum makinesi tasarladınız. Kodun geri kalanı bu cümlenin tercümesi.

🎯 Bu konu için bilmeniz gerekenler

Yeşil = gerekli · kesikli = faydalı ama şart değil. Tıklayınca ilgili sunuma gider.

Neden?FSM’nin diyagramı bir yönlü graftır: düğümler durumlar, oklar geçişler. Yığınlı FSM ise bildiğimiz yığını kullanır. Ama ikisi de şart değil; burada sıfırdan anlatıyoruz.
🧰 Kodlamadan korkuyorsanızFSM’yi anlamak için kod gerekmez: kâğıda yuvarlaklar ve oklar çizmek yeter. Unity Animator, PlayMaker gibi görsel araçlarla kod yazmadan da FSM kurabilirsiniz (son bölüm).

Mini sözlük

finite state machine (FSM)sonlu durum makinesiSonlu sayıda durum + aralarındaki geçişler
statedurumKarakterin o anki “modu”: devriye, saldırı…
transition · conditiongeçiş · koşulBir durumdan diğerine ok · okun ne zaman izleneceği
eventolayGeçişi tetikleyen anlık bilgi: “ses duyuldu”
entry / exit actiongiriş / çıkış eylemiDuruma girerken / çıkarken bir kez yapılan iş
hierarchical FSM (HFSM)hiyerarşik FSMDurumların içinde alt durum makineleri
pushdown automatonyığınlı otomatDurumları yığında tutan, “önceki duruma dön” bilen FSM
hysteresishisterezis (gecikme)Girmek ve çıkmak için farklı eşikler: titremeyi önler
LEVEL 0
Durum ve geçiş
trafik ışığından Pac-Man’e
  • durum ve geçiş
  • bir nöbetçi tasarlayalım
  • kod: üç yol
  • sorunlar ve çözümler
  • hiyerarşik ve yığınlı FSM
  • gerçek oyunlarda FSM
  • kütüphaneler ve entegrasyon

Her yerde durum makineleri

YEŞİL SARI KIRMIZI 30 sn 4 sn 30 sn
  • Sonlu sayıda durum: 3
  • Aynı anda tek aktif durum
  • Oklar: geçişler; üstündeki yazı: koşul
  • Diğer örnekler: kapı (açık/kapalı/kilitli), içecek makinesi, asansör, oyun menüleri, karakter animasyonları (dur/yürü/koş/zıpla)
  • 🎒 Bir FSM diyagramı = yönlü graf (Yönlü Graflar sunumu): düğüm = durum, kenar = geçiş

Önce bir kedi: “şu an ne yapıyor?” ve “ne olursa değişir?” 🐱

Bir durum makinesi iki sorudan ibarettir. Durum = şu an ne yapıyor (kutular). Geçiş = ne olursa başka bir şey yapmaya başlar (oklar; üstündeki yazı olay). Senaryoları oynatın, sonra olay düğmelerine kendiniz basın.

📸 Oyundan bir kare: Pac-Man (1980)

Pac-Man (1980) ekran görüntüsü
Dört hayalet (Blinky, Pinky, Inky, Clyde) aynı labirentte ama her biri başka bir “kovala” kuralıyla. Hepsi aynı üç durum arasında geçiş yapar: dağıl, kovala, kork (mavi). Sonraki slayt bu durumları diyagram olarak çiziyor.
Görsel: Pac-Man gameplay, Bandai Namco Entertainment America, Wikimedia Commons, CC BY 3.0

Pac-Man hayaletleri bir FSM’dir

DAĞIL KOVALA KORKU 7 sn geçti 20 sn geçti güç hapı güç hapı süre bitti
  • Y1’deki demo: zamanlayıcıyla DAĞIL ↔ KOVALA geçişi
  • Güç hapı → KORKU (rastgele yürü, mavi ol)
  • Her durumda farklı hedef kuralı çalışır
  • Aslında bir de “yenildi → evine dön” durumu var. FSM’ler büyüdükçe diyagram karışır — birazdan göreceğiz.
Quiz 1 · durum ve geçiş
Bir FSM’de aynı anda kaç durum aktif olabilir?
Düz bir FSM’de her an tek bir durum aktiftir: nöbetçi ya devriyededir ya kovalamaktadır. Aynı anda “yürü + nişan al” gibi şeyler gerekirse birden fazla FSM (katmanlar) ya da hiyerarşik FSM kullanılır.
LEVEL 1
Bir nöbetçi tasarlayalım
beş durum, sekiz geçiş
  • durum ve geçiş
  • bir nöbetçi tasarlayalım
  • kod: üç yol
  • sorunlar ve çözümler
  • hiyerarşik ve yığınlı FSM
  • gerçek oyunlarda FSM
  • kütüphaneler ve entegrasyon

Nöbetçinin beş durumu: her biri ne demek?

🚶 DEVRİYE

Ne yapar? Dört yol noktası arasında sakin sakin yürür.

Aklından geçen: “Her şey yolunda.”

Çıkış: oyuncuyu görürse ya da ses duyarsa.

❓ ŞÜPHE

Ne yapar? Sesin geldiği (ya da oyuncuyu son gördüğü) yere gider, durup etrafına bakınır.

Aklından geçen: “Orada bir şey mi vardı?”

Çıkış: görürse kovalar; 4 sn bir şey bulamazsa devriyeye döner.

🏃 KOVALA

Ne yapar? Oyuncuya doğru koşar.

Aklından geçen: “Gördüm seni!”

Çıkış: 60 birimden yakına gelirse saldırır; gözden kaybederse şüpheye düşer.

🔫 SALDIR

Ne yapar? Durur, oyuncuya döner, ateş eder.

Aklından geçen: “Menzildesin.”

Çıkış: oyuncu 90 birimden uzaklaşırsa yeniden kovalar; kaybederse şüphe.

💨 KAÇ

Ne yapar? Oyuncudan uzaklaşır, bu sırada canı yavaşça dolar.

Aklından geçen: “Ölmek üzereyim!”

Giriş: hangi durumda olursa olsun can 30’un altına düşünce. Çıkış: can 70’i geçince devriye.

Durum = “şu an ne yapıyorum”. Geçiş = “ne olursa başka bir şey yapmaya başlarım”. Renkler sonraki slaytlardaki tablo, diyagram ve canlı demoda hep aynı.

Nöbetçinin geçiş tablosu

şu anki durumkoşulyeni durum
her durumcan < 30KAÇ
DEVRİYEoyuncuyu görüyorKOVALA
DEVRİYEses duyduŞÜPHE
ŞÜPHEoyuncuyu görüyorKOVALA
ŞÜPHE4 sn geçtiDEVRİYE
KOVALAgöremiyorŞÜPHE (son görülen yere git)
KOVALAmesafe < 60SALDIR
SALDIRgöremiyor / mesafe > 90ŞÜPHE / KOVALA
KAÇcan > 70DEVRİYE
Her durumda ne yapar?
  • DEVRİYE: yol noktaları arasında yürü
  • ŞÜPHE: sesin geldiği yere git, etrafa bak
  • KOVALA: oyuncuya koş
  • SALDIR: dur, nişan al, ateş et
  • KAÇ: oyuncudan uzaklaş, iyileş

Tablonun sırası önemli: “can < 30” kuralı önce kontrol edilir. Saldırıya 60’ta girip ancak 90’da çıkmasının sebebi birazdan (“titreme”).

Tablodan diyagrama: olayları siz verin 🎛️

Az önceki tablonun her satırı burada bir ok. Canlı oyundan önce nöbetçiyi elle yönetin: bir senaryo seçin ya da alttaki olay düğmelerine basın ve her seferinde önce tahmin edin: hangi kutuya geçecek, yoksa hiç kıpırdamayacak mı?

Demoyu okuma kılavuzu: ekranda ne var?

Sol: oyun alanı
  • Ok biçimli üçgen = nöbetçi. Rengi ve üstündeki yazı şu anki durumu. Altındaki çubuk canı.
  • Soluk dilim = görüş konisi (110°, 260 birim). Oyuncuyu görünce kırmızıya döner.
  • Yeşil daire = oyuncu. Fareyi alana sokunca siz olursunuz; fare alandan çıkınca oyuncu olduğu yerde kalır.
  • Koyu dikdörtgenler = duvar: arkasındaki görülmez.
  • Kesikli dikdörtgen = devriye yolu.
  • Turuncu halkalar = duyulan ses (tıkladığınız yer).
Sağ: nöbetçinin “beyni”
  • Diyagram: dolu kutu = şu anki durum. Kalın kırmızı ok = az önce kullanılan geçiş.
  • Sayaçlar: nöbetçinin o anki algısı — can, oyuncuya mesafe, görüyor mu?
  • “Son 5 sn’de geçiş”: çok yüksekse nöbetçi kararsız (titreme) demektir.
  • Günlük: son geçişler ve zamanları.

Kontroller: fare = oyuncu · tıkla = ses çıkar · X = nöbetçiye ateş et (−25 can) · “1 kare” ile adım adım izleyin.

Nöbetçi FSM — canlı

🔎 DEMODA DENEYİN
  1. Nöbetçinin arkasında bir yere tıklayın (ses). ŞÜPHE durumuna geçip oraya gidiyor mu?
  2. Görüş konisine girin, sonra bir duvarın arkasına saklanın. KOVALA → ŞÜPHE geçişini izleyin: son gördüğü yere gidiyor.
  3. Yaklaşıp SALDIR’a geçirin, sonra klavyeden üç kez X’e basın (her biri −25 can). Can 30’un altına düşünce hangi durumda olursa olsun KAÇ’a geçiyor mu? Can 70’i geçince ne oluyor?
  4. ▶| 1 kare düğmesiyle tek tek ilerleyin: sağdaki diyagramda kırmızı ok = az önce kullanılan geçiş.

🔗 Dışarıda da deneyin: Stately görselleştirici (durum makinesini yazın, diyagramı canlı çizsin, olay gönderin) · Game Programming Patterns: State (bir platform kahramanının zıplama/eğilme hatalarından FSM’ye) · Pac-Man’s Ghosts Were Never a Team (hayalet modları).

🔗 Başka gözle: etkileşimli anlatımlar ve yazılar

Aynı konuyu başka bir anlatımla, kendi elinizle oynayarak görmek çok işe yarar. Kartlar yeni sekmede açılır; çoğu İngilizce ama görsel ve etkileşimli.

🎬 Ara: Half-Life’ın askerleri bir FSM’dir

The AI of Half-Life: Finite State Machines
AI and Games (Tommy Thompson) · YouTube’da aç

Az önce elle yönettiğiniz nöbetçinin 1998’deki atası: Half-Life’taki askerlerin ve yaratıkların durumları, bu durumlar arasındaki geçişler ve her durumda çalışan “görev listeleri”.

🤔 İzlerkenVideodaki canavarın durumlarından hangileri bizim nöbetçinin beş durumuna karşılık geliyor? Bizde olmayan hangi durum var? “Koşul” (condition) dedikleri şey, bizim olay düğmelerimizden hangisine benziyor?
LEVEL 2
Kod: üç yol
switch · durum sınıfları · geçiş tablosu
  • durum ve geçiş
  • bir nöbetçi tasarlayalım
  • kod: üç yol
  • sorunlar ve çözümler
  • hiyerarşik ve yığınlı FSM
  • gerçek oyunlarda FSM
  • kütüphaneler ve entegrasyon

Yol 1: enum + switch (1/2)

Yol 1: enum + switch (2/2)

🎒 enum: sabit isimlerden oluşan tür — yazım hatası yapınca derleyici yakalar ("CAHSE" gibi string hataları sessizce bug olur!). Python’da enum.Enum, JS’te sabit nesne kullanılabilir.

🎒 Ön bilgi: arayüz ve çok biçimlilik

  • Arayüz interface: “bu metotlara sahip olacağım” sözü. IState: Enter, Update, Exit
  • Her durum bu sözü veren ayrı bir sınıf: PatrolState, ChaseState…
  • Çok biçimlilik polymorphism: nöbetçi sadece current.Update() der; hangi sınıfın kodu çalışacağına nesnenin türü karar verir
  • Python ve JS’te “arayüz” zorunlu değil: aynı isimli metotları yazmak yeter (ördek tipleme duck typing: “ördek gibi vaklıyorsa ördektir”)

Yol 2: durum sınıfları State pattern

Artı: her durum kendi dosyasında, giriş/çıkış eylemleri temiz. Eksi: geçişler sınıfların içine dağılmış — bütün resmi görmek zor.

Yol 3: geçiş tablosu (veri güdümlü) (1/2)

Yol 3: geçiş tablosu (veri güdümlü) (2/2)

Artı: bütün davranış tek bir tabloda; tasarımcı (hatta bir JSON/CSV dosyası) değiştirebilir. Bu, PlayMaker ve Unity Animator gibi görsel editörlerin arkasındaki fikir.

🎒 FSM kodundaki yeni ifadeler

current?.Exit()C# null-koşullu çağrı“current null değilse Exit’i çağır”. Geleneksel: if (current != null) current.Exit();
Func<Guard, bool>C# fonksiyon türü“Guard alıp bool döndüren fonksiyon” tutan değişken
g => g.hp < 30 · lambda g: g.hp < 30lambda / ok fonksiyonuAdsız minik fonksiyon: “g’yi al, g.hp < 30 sonucunu döndür”. Geleneksel: bool LowHp(Guard g) { return g.hp < 30; } yazıp adını vermek
for frm, to, cond in tabledemet açmaTablodaki her satırı üç değişkene ayır
readonly Guard gsalt okunur alanYapıcıda bir kez atanır, sonra değişmez
"*"jokerBizim kuralımız: “her durumdan”

📦 Tam kod: p5.js’te çalışan nöbetçi

Bütün program bu üç satırın etrafında döner: sense() algıyı toplar, nextState() geçiş tablosudur (laboratuvarda yazacağınız fonksiyon), act() her durumda ne yapılacağını söyler.

🎮 Çalıştırın, kopyalayın, bozun

▶ Eksiksiz örneği aç — yaklaşık 150 satır, tek dosya, yorumlu. Sayfada hem oyun hem kodun tamamı var; “kodu kopyala” ile editor.p5js.org’a yapıştırın.

Deneyin:

  • Çıkış eşiğini 90’dan 60’a çekin: titremeyi kendi gözünüzle görün.
  • Görüş açısını 110°’den 40°’ye indirin: arkasından dolaşmak ne kadar kolaylaştı?
  • Yeni durum: YARDIM_CAGIR — oyuncuyu ilk gördüğünde 1 sn dursun, sonra kovalasın.

🌱 Isınma kodu: trafik ışığı

Dünyanın en küçük durum makinesi: 3 durum, 3 geçiş. İki satır ekleyin. p5.js’te bunu draw() içinde bir zamanlayıcıyla çağırıp ışığın rengini state’e göre seçersiniz.

🌱 Isınma kodu: kapı (olaylı FSM)

Bu sefer geçişi zaman değil olay tetikliyor. Dikkat: kilitli kapı “ac” olayıyla açılmamalı, açık kapı kilitlenememeli — return state satırı bunu bedavaya sağlıyor. Bir FSM’nin gücü: izin verilmeyen geçiş, yazılmamış geçiştir.

🧪 Canlı laboratuvar: nöbetçinin beynini yaz

Tablodaki 9 kuralı tamamlayın (saldırıdan çıkış eşiği 90!). Çalıştırınca önce testler, sonra sağdaki canlı simülasyon sizin fonksiyonunuzla çalışır. Bilerek bir hata yapın (ör. KOVALA’dan çıkışı silin): nöbetçi nasıl davranıyor?

🐍 Kod laboratuvarı — Python

Geçiş tablosunun tamamını Python’la yazın. İpucu: önce “can < 30 ve durum KAC değilse → KAC” kuralı; sonra her durum için bir if/elif bloğu. Eşikler: saldırıya giriş < 60, saldırıdan çıkış > 90, şüphe süresi 4 sn, iyileşme > 70.
LEVEL 3
Sorunlar ve çözümler
titreme, durum patlaması, spagetti
  • durum ve geçiş
  • bir nöbetçi tasarlayalım
  • kod: üç yol
  • sorunlar ve çözümler
  • hiyerarşik ve yığınlı FSM
  • gerçek oyunlarda FSM
  • kütüphaneler ve entegrasyon

Titreme oscillation / flicker

Bu kopyada saldırıya giriş ve çıkış eşiği aynı (60) ve oyuncu (bot) nöbetçi yaklaştıkça yavaşça geri çekiliyor. Nöbetçi tam eşikte KOVALA ↔ SALDIR arasında saniyede defalarca gidip geliyor: “son 5 sn’de geçiş” sayacına ve diyagramdaki oka bakın. Histerezis kutusunu açın: çıkış eşiği 90 olur, titreme anında biter. İkinci kaynak: “algı hafızası” kapalıyken fareyle bir duvar köşesinin tam kenarında durun (KOVALA ↔ ŞÜPHE).

Titremeye üç çare

1) HisterezisGirmek ve çıkmak için farklı eşikler: “10 m’den yakınsa kovalamaya başla, 15 m’den uzaksa vazgeç.” Arada kalan 5 m: tampon bölge. (Termostatlar da böyle çalışır.)
2) Hafıza“Gördüm” bilgisini bir süre sakla: son görülme zamanı + konum. Algılama sunumunda ayrıntılı (Y11).
3) Minimum süreBir duruma girince en az 0,5 sn kal (timeInState). İnsan da saniyede 10 kez fikir değiştirmez.

Y1’deki kural: fikir değiştiren NPC, oyuncunun gözünde “bozuk YZ”dir. Titreme, yapay aptallığın en yaygın kaynağı.

Durum patlaması ve spagetti

Tasarımcı yeni istekler getiriyor:

  • “Mermisi bitince şarjör değiştirsin” → hangi durumlardan? Kovalarken, saldırırken, kaçarken… her birine ok
  • “Yaralı hâlde yürüyüş animasyonu farklı olsun” → Devriye-yaralı, Kovala-yaralı, … durum sayısı ikiye katlanır
  • “Telefon çalınca konuşsun, sonra kaldığı yerden devam etsin” → nereden geldiğini nasıl bilecek?

N durumlu bir FSM’de olası geçiş sayısı ~N². 20 durumda diyagram bir spagetti tabağına döner.

Çareler (sonraki bölüm ve sunumlar)
  • Hiyerarşi: durumları gruplara topla (HFSM)
  • Yığın: “öncekine dön” (pushdown)
  • Paralel FSM’ler: hareket ve silahı ayrı makinelerde yönet
  • Büyük karakterlerde: davranış ağaçları (Y3) ve utility (Y4)

Paralel FSM’ler: çarpma yerine toplama

Tek makine: 3 × 3 = 9 durumyürü+ hazıryürü+ ateşyürü+ şarjörkoş+ hazırkoş+ ateşkoş+ şarjörçömel+ hazırçömel+ ateşçömel+ şarjörsarı: “yürürken şarjör değiştir” ayrı bir durum
  • Karakter aynı anda iki ayrı konuda karar veriyor: nasıl hareket ettiği ve silahıyla ne yaptığı.
  • Hepsini tek makineye koyarsanız her birleşim ayrı bir durum olur: “yürü + hazır”, “yürü + ateş”, “yürü + şarjör”, “koş + hazır”…
  • Yeni bir hareket (“sürün”) eklemek: üç yeni durum. Bir de “yaralı / sağlam” eklenirse: 3 × 3 × 2 = 18. Buna durum patlaması denir.
HAREKET makinesiyürükoşçömelSİLAH makinesihazırateşşarjörmesajiki makine: 3 + 3 = 6 durum; aynı anda “yürü” ve “şarjör”
  • Çözüm: her konu için ayrı bir makine; hepsi aynı anda çalışır. Karakterin durumu = makinelerin o anki durumlarının birleşimi.
  • “Yürürken şarjör değiştir” için özel durum gerekmez: hareket makinesi “yürü”de, silah makinesi “şarjör”de.
  • Gerektiğinde mesajlaşırlar: silah “ateş”e geçince harekete “koşmayı kes” der.
konulartek makineparalel makineler
3 hareket × 3 silah93 + 3 = 6
4 hareket × 4 silah × 3 sağlık484 + 4 + 3 = 11
+ 5 duygu durumu24016

Unity’nin Animator penceresindeki katmanlar (layer) tam olarak budur: “bacaklar” ve “üst gövde” ayrı durum makineleridir; karakter koşarken şarjör değiştirebilir.

Quiz 2 · sorunlar
Nöbetçi “oyuncu 8 m’den yakınsa KOVALA, değilse DEVRİYE” kuralıyla çalışıyor ve oyuncu tam 8 m civarında dururken saniyede onlarca kez durum değiştiriyor. En uygun çözüm hangisi?
Tek eşik nerede olursa olsun sorun o çizgide tekrarlanır (A). Seyrek güncelleme titremeyi yavaşlatır ama çözmez (C). İki farklı eşik, arada bir tampon bölge bırakır: girmek ve çıkmak için farklı koşullar.
LEVEL 4
Hiyerarşik ve yığınlı FSM
durumların içinde durumlar, “öncekine dön”
  • durum ve geçiş
  • bir nöbetçi tasarlayalım
  • kod: üç yol
  • sorunlar ve çözümler
  • hiyerarşik ve yığınlı FSM
  • gerçek oyunlarda FSM
  • kütüphaneler ve entegrasyon

Hiyerarşik FSM HFSM

BARIŞ SAVAŞ Devriye Şüphe Kovala Saldır KAÇ görüyor kaybetti can < 30 can < 30 can > 70 → Devriye üst durumdan çıkan ok,içindeki HER alt durum için geçerli
  • Her durum kendi içinde bir FSM olabilir
  • Üst seviyedeki geçiş (BARIŞ → SAVAŞ) içerideki bütün alt durumlar için geçerli: oklar azalır
  • “Can < 30 → KAÇ” artık dört ayrı ok değil: iki yeşil ok (BARIŞ’tan ve SAVAŞ’tan). Devriye, Şüphe, Kovala, Saldır — hangisinde olursa olsun çalışır.
  • Game AI Pro’daki örnek: gece bekçisi kasa ile kapı arasında devriye gezer. “Telefon çaldı → konuş → devriyeye dön” durumunu her devriye durumuna ayrı ayrı eklemek yerine, devriyeyi tek bir üst durum yaparsınız.

Problem: “işim bitince kaldığım yere döneyim”

🎮 Kesintiler
  • Nöbetçi devriye geziyor. Telefon çaldı: konuş. Konuşurken silah sesi geldi: alarm.
  • Tehlike geçince ne yapmalı? Telefona dönmeli. Telefon bitince? Devriyeye.
  • Oyun menüleri aynı kalıptır: ana menü → ayarlar → ses ayarları; “geri” tuşu bir önceki ekrana döner.
  • Düz FSM’nin derdi: tek bir değişkeni var: “şu anki durum”. ALARM’a geçince TELEFON’da olduğunu unutur. Alarm bitince nereye döneceğini bilemez; herkes “varsayılan” duruma (devriye) atılır.
  • Her kesinti için “önceki durum” değişkeni eklemek? Kesinti içinde kesinti olunca yetmez.
  • Fikir: durumu tek değişkende değil, bir yığında tut (Yığınlar sunumu: son giren ilk çıkar). Yeni iş çıkınca üstüne koy (push); iş bitince üstten al (pop): altındaki, yani yarım kalan iş kendiliğinden geri gelir.

Adı nereden geliyor? “Pushdown automaton” = “aşağı iten otomat”: yeni durum eskileri yığında aşağı iter. “Otomat”, durum makinesinin eski adı. Türkçesi: yığınlı durum makinesi.

Elle izleme (yığının tepesi sağda)
olayyığınlı FSM: yığınşu andüz FSM: tek değişken
başlangıçDEVRİYEdevriyeDEVRİYE
📞 telefon çaldıDEVRİYE · TELEFONtelefonTELEFON
🔫 silah sesiDEVRİYE · TELEFON · ALARMalarmALARM
✔ tehlike geçtiDEVRİYE · TELEFONtelefona döndü??? → DEVRİYE
✔ telefon bittiDEVRİYEdevriyeye döndüDEVRİYE

Dördüncü satır farkı gösteriyor: yığın, alarmın altında telefonu saklamıştı; düz FSM telefonu çoktan unutmuştu, yarım kalan konuşma havada kaldı.

Kurallar: (1) şu anki durum = yığının tepesi; (2) yalnızca tepedeki durum çalışır, alttakiler bekler; (3) bir durum “işim bitti” deyince yığından çıkar. Menülerde: her açılan ekran push, “geri” tuşu pop.

Yığınlı FSM pushdown automaton

🔎 Deneyin Nöbetçi devriyede. Sırayla basın: 📞 Telefon çaldı → 🔫 Silah sesi → iki kez ✔ Bitti. Soldaki yığın her “bitti”de bir önceki işe döner (alarm → telefon → devriye). Sağdaki düz FSM tek bir değişken tuttuğu için telefonu çoktan unutmuştur: ???.

Durumları bir yığında tut (Yığınlar sunumu!): yeni bir kesinti üste eklenir (push), bitince çıkarılır (pop) ve karakter otomatik olarak kaldığı yerden devam eder. Düz FSM tek bir değişken tuttuğu için nereden geldiğini unutur.

🎮 Nerede?Oyun menüleri (ana menü → ayarlar → ses → geri, geri), duraklatma ekranı, diyaloglar, “kesinti” davranışları (yaralanma animasyonu, el bombasından kaçma).

Yığınlı FSM — kod

🎒 Peek() / stack[-1]: yığının tepesine çıkarmadan bakmak. Python’da [-1] = son eleman.

📜 Statecharts (Harel, 1987)David Harel, hiyerarşi, paralel bölgeler ve geçmiş (history: “en son hangi alt durumdaydım?”) özelliklerini bir araya getirip statechart notasyonunu tanımladı — ilk uygulaması bir savaş uçağının arayüzüydü. Bugün UML durum diyagramları ve JavaScript’teki XState kütüphanesi bu fikirlerden gelir.
⚙️ Paralel FSM’lerBir karakterin aynı anda birkaç makinesi olabilir: hareket FSM’si (yürü/koş/çömel), silah FSM’si (hazır/ateş/şarjör), animasyon FSM’si. Birbirlerine mesaj gönderirler. Böylece “yürürken şarjör değiştir” için ayrı bir durum gerekmez — durum patlamasının çaresi.
LEVEL 5
Gerçek oyunlarda FSM
Doom, Metal Gear, Hollow Knight, Unity Animator
  • durum ve geçiş
  • bir nöbetçi tasarlayalım
  • kod: üç yol
  • sorunlar ve çözümler
  • hiyerarşik ve yığınlı FSM
  • gerçek oyunlarda FSM
  • kütüphaneler ve entegrasyon

📸 Oyundan bir kare: DOOM (1993)

DOOM (1993) ekran görüntüsü
Ekrandaki her canavar, birkaç slayt sonra satır satır okuyacağımız info.c tablosundaki durumlardan birinde: ayakta bekliyor, yürüyor, saldırıyor ya da acı çekiyor.
Görsel: DOOM oyun içi görüntüsü, id Software, Wikipedia (eğitim amaçlı adil kullanım)

📜 Doom (1993): kaynak kodda bir FSM

Doom’un Imp’i: animasyon kareleri ile YZ durumları aynı tabloda. A_Look oyuncuyu görünce “görme durumu”na, A_Chase yakın mesafede saldırı durumuna geçirir (p_enemy.c). 30 yıllık kod, bugün okuduğunuz FSM’nin aynısı.

🎒 Doom tablosunu okuyalım: bir satır = bir durum

Tablonun her satırı beş bilgi taşır. İlk satır üzerinden:

{SPR_TROO, 0, 10, {A_Look}, S_TROO_STND2, 0, 0}

alandeğeranlamı
spriteSPR_TROOHangi yaratığın resimleri? TROO = “trooper”: oyundaki Imp (ateş topu atan kahverengi iblis).
kare0O yaratığın kaçıncı resmi çizilsin? (0 = A karesi, 1 = B…)
süre10Bu durumda kaç tic kalınsın? Doom saniyede 35 tic işler: 10 tic ≈ 0,29 sn.
eylemA_LookBu duruma girildiğinde çağrılacak fonksiyon: “etrafa bak, oyuncu görünüyor mu?”
sonraki durumS_TROO_STND2Süre dolunca geçilecek satır.

Son iki sıfır kullanılmayan yedek alanlar. Satırların adı sağdaki yorumda: S_TROO_STND = “Imp, ayakta bekliyor”.

  • STND → STND2 → STND → …: iki satır birbirini gösteriyor: sonsuz bir bekleme döngüsü. Her 10 tic’te resim değişiyor (nefes alıyor gibi) ve A_Look çağrılıyor.
  • Döngüden nasıl çıkılıyor? Tablodan değil, eylemden: A_Look oyuncuyu görürse durumu elle S_TROO_RUN1’e çevirir. “Sonraki durum” sütunu yalnızca “hiçbir şey olmazsa” yoludur.
  • RUN1 … RUN8: sekiz satırlık yürüme animasyonu; her 3 tic’te A_Chase: oyuncuya doğru bir adım at, menzildeysen saldırı durumuna geç.
  • ATK1 → ATK2 → ATK3 → RUN1: iki karede hedefe dön (8 + 8 tic: oyuncunun kaçması için uyarı süresi), üçüncüde ateş topunu at, kovalamaya geri dön.
  • PAIN → PAIN2 → RUN1: vurulunca 4 tic’lik sersemleme; sonra kovalamaya dön.
🎮 Neden böyle yazmışlar?Animasyon ve yapay zekâ tek tabloda: çizer yeni kare eklerken, tasarımcı süreleri değiştirirken programcıya gerek kalmaz. “Imp’in ateş etmeden önceki bekleme süresi” bir sayıdır: 8’i 4 yaparsanız oyun zorlaşır. Oyundaki bütün yaratıklar, silahlar ve eşyalar aynı tablonun 967 satırıdır.

Aynı tablo, durum diyagramı olarak: Imp’in beyni

BEKLES_TROO_STND ⇄ STND2her 10 tic: A_Look(etrafa bak)KOVALAS_TROO_RUN1 … RUN8her 3 tic: A_Chase(oyuncuya doğru bir adım)SALDIRATK1 → ATK2 → ATK38 + 8 + 6 ticA_FaceTarget, A_FaceTarget,A_TroopAttack (ateş topu)ACI ÇEKS_TROO_PAIN → PAIN22 + 2 tic: A_Pain (inleme)oyuncuyu gördümenzilde: ateş etATK3 bittihasar aldıPAIN2 bittihasar aldıgörmedi: beklemeye devamKutular tablodaki satır gruplarıdır; oklar ya “sonraki durum” sütunundan ya da eylem fonksiyonunun kararından gelir.

Bu sunumun başındaki nöbetçi FSM’siyle karşılaştırın: Devriye ↔ BEKLE, Kovala ↔ KOVALA, Saldır ↔ SALDIR. Fark yalnızca yazım biçiminde: orada switch, burada veri tablosu. Tabloyla yazılan FSM’ye “veri güdümlü” denir: davranışı değiştirmek için kodu değil veriyi değiştirirsiniz.

Oyunlardan FSM örnekleri

Metal Gear Solid 2 (2001)Nöbetçilerin bütün üssü kapsayan alarm evreleri: Sızma (normal) → Alarm (görüldünüz, ekranda geri sayım) → Kaçış (sizi arıyorlar) → Dikkat (tetikte devriye) → normal. Ekrandaki sayaç oyuncuya FSM’nin hangi durumda olduğunu açıkça gösterir.
Hollow Knight · HearthstoneTeam Cherry, Hollow Knight’ın düşman ve boss davranışlarını Unity için görsel bir FSM aracı olan PlayMaker ile kurdu (mod yapımcıları bu FSM’leri düzenler). Blizzard da Hearthstone’daki birçok olayı PlayMaker ile yaptı: sanatçılar kod yazmadan etkileşim kurabildi.
Half-Life (1998)Askerler “görev planları” (schedule) ve durumlardan oluşan bir FSM ile çalışır; bir koşul bozulunca plan kesilir ve yenisi seçilir. AI and Games’in “The AI of Half-Life” videosu ayrıntılı anlatıyor.
Final Fantasy XV (2016)Karakter YZ’si davranış ağaçları ile durum makinelerini birleştirir: büyük resim FSM, her durumun içi davranış ağacı (Game AI Pro 3, 11. bölüm). Gerçek projelerde teknikler karışır.

📸 Oyundan bir kare: Metal Gear Solid 2 (2001)

Metal Gear Solid 2 (2001) ekran görüntüsü
Muhafızlar oyuncuyu fark edince bütün üs “alarm” evresine geçer; ekranın köşesindeki sayaç evrenin ne kadar süreceğini gösterir. Sonraki slayttaki diyagram bu evreleri çiziyor.
Görsel: Metal Gear Solid 2: Substance ekran görüntüsü, Konami, Wikipedia (eğitim amaçlı adil kullanım)

Metal Gear Solid 2: bütün üssün tek bir durum makinesi

SIZMAInfiltrationnormal devriyesayaç yokALARMAlertbütün üs sizi biliyortakviye geliyorKAÇIŞEvasionsizi kaybettilerson görülen yeri arıyorlarDİKKATCautiontetikte devriyesayaç geri sayıyorbir nöbetçi sizi gördügörüş koptusayaç bittisayaç bittiyeniden görüldünüzgörüldünüzALERTekranda: durumun adı + kalan süre
  • Burada FSM tek bir nöbetçinin değil, bütün üssün ortak durumudur: bir nöbetçi telsizle haber verince herkes aynı anda ALARM’a geçer.
  • Geçişlerin çoğu süreye bağlı: KAÇIŞ ve DİKKAT’te ekranda geri sayan bir çubuk var. Sayaç bitene kadar saklanabilirseniz bir sonraki, daha sakin duruma geçilir.
🎮 Tasarım dersi: FSM’yi oyuncuya gösterinÇoğu oyunda durum makinesi gizlidir; oyuncu düşmanın ne düşündüğünü tahmin eder. Metal Gear durumun adını ve kalan süresini ekrana yazar: oyuncu “şu an neredeyim, ne kadar dayanmalıyım?” sorusunun cevabını bilir ve plan yapar. Gizlilik oyunlarındaki “!” ve “?” işaretleri de aynı işi görür (Y11, Y19).

Unity Animator da bir FSM

  • Animator Controller: kutular = durumlar (Idle, Walk, Attack), oklar = geçişler, parametreler (Speed, IsAttacking) = koşullar
  • Bir duruma StateMachineBehaviour betiği eklenebilir: OnStateEnter, OnStateUpdate, OnStateExit
  • ⚠️ Tartışmalı: YZ mantığını Animator’a koymak hızlıdır ama animasyon ve karar birbirine karışır. Çoğu ekip kararı ayrı bir FSM’de verip Animator’a sadece parametre gönderir (animator.SetBool("IsChasing", true)).
Quiz 3 · oyunlarda
Metal Gear Solid 2’nin “Alarm → Kaçış → Dikkat → Sızma” evrelerinin ekranda bir sayaçla gösterilmesinin YZ tasarımı açısından en önemli faydası nedir?
Y1’deki ders: oyuncunun anlamadığı YZ “rastgele” görünür. Durumu görünür kılmak (sayaç, müzik, nöbetçi replikleri) oyuncuya bilgiye dayalı karar verdirir ve YZ’yi adil hissettirir.
LEVEL 6
Kütüphaneler ve entegrasyon
hazır araçlar: ne yapar, nasıl eklenir
  • durum ve geçiş
  • bir nöbetçi tasarlayalım
  • kod: üç yol
  • sorunlar ve çözümler
  • hiyerarşik ve yığınlı FSM
  • gerçek oyunlarda FSM
  • kütüphaneler ve entegrasyon

🧰 Kütüphaneler ve araçlar

adplatformne yapar / sınırlarınasıl eklenir
Animator + StateMachineBehaviourUnityGörsel FSM; animasyon için tasarlandı, YZ için sınırlıHazır (Window → Animation → Animator)
UnityHFSMUnity (C#)Kodla hiyerarşik FSM; hafif, ücretsiz, açık kaynak. Görsel editörü yokPackage Manager → Add from Git URL: https://github.com/Inspiaaa/UnityHFSM.git#upm
StatelessC# (.NET, Unity)Akıcı (fluent) API ile durum makinesi; hiyerarşi, korumalı geçişler. Oyun dışı projelerde de yaygınNuGet; Unity’de DLL kopyalama
PlayMakerUnityGörsel FSM ile oyun mantığı (Hollow Knight, Hearthstone). Kod bilmeden üretim; büyük projede okunabilirlik zorlaşırAsset Store (ücretli)
NodeCanvasUnityFSM + davranış ağacı + diyalog ağacı tek paketteAsset Store (ücretli)
StateTreeUnreal Engine 5FSM ile davranış ağacının karışımı; Epic’in yeni önerilen aracıEklenti olarak etkinleştir
LimboAIGodot 4Hiyerarşik FSM + davranış ağacı, görsel editörAssetLib ya da GitHub
XStateJavaScriptStatechart’lar: hiyerarşi, paralel durumlar, görselleştirici (Stately)npm install xstate
transitions · python-statemachinePythonHafif FSM; tetikleyiciler, koşullar, giriş/çıkış geri çağırmalarıpip install transitions

Aynı nöbetçi, üç kütüphaneyle

Fark: UnityHFSM koşul güdümlü (her karede kontrol eder), XState ve transitions olay güdümlü (bir şey olunca siz haber verirsiniz). Oyunlarda ikisi de yaygın.

🧩 Projenize FSM eklerken: adım adım

  1. Kâğıtta çizin: durumlar, oklar, koşullar. Tabloya dökün (bu sunumdaki gibi).
  2. Algıyı ayırın: SeesPlayer(), HeardNoise gibi fonksiyonlar FSM’nin dışında hesaplansın.
  3. Önce en basit hâli: enum + switch ile 2–3 durum. Çalıştığını görün.
  4. Hata ayıklama görselleri: NPC’nin başına durum adını yazın (Unity: OnDrawGizmos / Handles.Label). Bu sunumdaki demolar gibi.
  5. Büyüdükçe: durum sınıflarına ya da bir kütüphaneye (UnityHFSM) geçin.
  6. Animasyonla bağ: FSM karar verir, Animator’a parametre gönderir.
⚙️ PerformansFSM çok ucuzdur: her karede sadece aktif durumun kodu ve onun birkaç koşulu çalışır. Pahalı olan koşulların kendisi (görüş ışını, yol bulma). Onları her karede değil, saniyede birkaç kez hesaplayın (Y18).

🤖 Bu konuyu bir YZ kod asistanıyla yazarken

Sık yapılan hatalar — kontrol edin
  • Her karede new ChaseState() üretmek (GC çöpü — durumları bir kez oluşturup saklayın)
  • Enter/Exit’i unutmak: animasyon, ses, zamanlayıcı sıfırlanmıyor
  • “Her durumdan” geçişleri her duruma kopyalamak
  • String durum adlarında yazım hatası (enum kullanın)
  • Karar mantığını Animator geçişlerine gömmek
İyi bir istek (prompt) örneği

“Unity 6, C#. Bir Guard için enum + switch ile FSM yaz: durumlar Patrol, Suspicious, Chase, Attack, Flee. Geçiş tablosu şu: [tabloyu yapıştır]. Kovalamaya başlama mesafesi 8, bırakma mesafesi 12 olsun (histerezis). Her durum değişiminde Debug.Log yaz ve OnDrawGizmos ile durum adını karakterin üstüne çiz. Durum değişince Animator’a "State" int parametresi gönder.”

  • Durum ve geçiş tablosunu isteğe siz yazın; asistana tasarımı değil kodu yaptırın
  • Hangi yolu istediğinizi söyleyin (enum+switch / State pattern / UnityHFSM)
  • Her geçişte Debug.Log ile durum yazdırmasını isteyin
  • Titreme önlemini (histerezis, minimum süre) açıkça isteyin

💬 Tartışma soruları

Yanınızdakiyle 2 dakika konuşun, sonra sınıfça tartışalım
  1. Bugün kullandığınız, oyun olmayan bir şeyin (asansör, çamaşır makinesi, otomat, telefon kilidi) durumlarını ve geçişlerini sayın. Hangi geçiş yasak?
  2. Metal Gear Solid, nöbetçinin durumunu ekranda sayaçla gösteriyor. Aynı şeyi bir korku oyununda yapar mıydınız? Durumu göstermek ne zaman oyunu bozar?
  3. Nöbetçinize 6. bir durum ekleyeceksiniz: “arkadaşını yardıma çağır”. Kaç yeni ok çizmeniz gerekir? Bu sayı sizi neden rahatsız etmeli?
  4. Titreme (KOVALA ↔ ŞÜPHE) bir hata. Peki bir karakterin bilerek kararsız görünmesini isteyeceğiniz bir sahne olabilir mi? Nasıl tasarlardınız?

🚀 Proje ve araştırma önerileri

  1. [kolay] Trafik ışığı + yaya — p5.js’te trafik ışığı FSM’i ve ışığa uyan yayalar. Bonus: yaya butonu (olay güdümlü geçiş).
  2. [kolay] Pac-Man modları — Y1’deki Pac-Man demosuna korku modunu ve “yenildi → evine dön” durumunu ekleyin; diyagramını çizin.
  3. [orta] Gizlilik oyunu — Unity’de 3 nöbetçili bir oda: bu sunumdaki FSM + görüş konisi + MGS tarzı alarm evreleri (bütün nöbetçileri etkileyen global FSM).
  4. [orta] Yığınlı menü — Oyununuzun menülerini yığınlı FSM ile yazın (ana menü → ayarlar → ses → geri).
  5. [zor] HFSM boss — Üç fazlı bir boss: her faz bir üst durum, içinde 3–4 saldırı durumu. UnityHFSM ya da LimboAI ile.
  6. [araştırma] Doom’u okuyun — Doom kaynak kodunda (info.c, p_enemy.c) bir canavarın bütün durumlarını çıkarıp diyagramını çizin. A_Chase hangi koşullarda saldırıya geçiriyor?

Neler öğrendik? 🎒

  • FSM: sonlu durum + geçişler; her an tek durum.
  • Tasarım: önce tablo/diyagram, sonra kod.
  • Kod: enum+switch (basit), durum sınıfları (düzenli), geçiş tablosu (veri güdümlü).
  • Titreme: histerezis, hafıza, minimum süre.
  • Durum patlaması → HFSM, yığınlı FSM, paralel FSM’ler.
  • Gerçek oyunlar: Doom, MGS2, Half-Life, Hollow Knight, FFXV.
  • Araçlar: Animator, UnityHFSM, PlayMaker, StateTree, LimboAI, XState, transitions.
  • Sonraki: davranış ağaçları — büyük karakterler için daha modüler bir yol.

🎮 Bu sunumdaki oyunlar

Adı geçen oyunu tanımıyorsanız: oyuncuların kullandığı wiki sayfası ve oyunu kısaca gösteren bir video. ▶ video slaydın üstünde açılır (Esc ile kapanır); 🎯 işaretli bağlantılar doğrudan bu sunumdaki konuya gider.

Pac-Man 1980 · arcade
Labirentte yem toplayıp dört hayaletten kaçarsınız; her hayaletin ayrı bir kovalama kuralı var.
Doom 1993 · nişancı
Türü popülerleştiren nişancı; kaynak kodu 1997’de açıldı.
Metal Gear Solid 2 2001 · gizlilik
Hideo Kojima’nın sızma oyunu; fark edilince nöbetçinin başında “!” belirir.
Hollow Knight 2017 · metroidvania
El çizimi, böceklerin dünyasında geçen zorlu 2B keşif oyunu; çok küçük bir ekip yaptı.
Half-Life 1998 · nişancı
Hikâyeyi ara sahnesiz anlatan, türü değiştiren nişancı; asker timleri birlikte hareket eder.
Hearthstone 2014 · dijital kart oyunu
Warcraft evreninde sıra tabanlı kart oyunu; rakibin elini göremezsiniz.
Final Fantasy XV 2016 · aksiyon RPG
Dört arkadaşın yol hikâyesi; yoldaşlar FSM + davranış ağacı karışımı bir YZ ile savaşır.
Halo 2 2004 · nişancı
Serinin ikinci oyunu; davranış ağaçlarını oyun endüstrisine tanıtan YZ’siyle bilinir.

Kaynaklar: videolar 🎬

📄 Akademik okumalar

Harel 1987 · Statecharts: A Visual Formalism for Complex Systems (Science of Computer Programming 8(3))
Hiyerarşi, paralellik ve geçmiş: HFSM’nin akademik temeli.
Anguelov, Humphreys, Mark 2013 · Behavior Selection Algorithms (Game AI Pro, Bölüm 4)
FSM, HFSM, BT, utility, GOAP, HTN karşılaştırması; gece bekçisi örneği.
Lim 2017 · A Reusable, Light-Weight Finite-State Machine (Game AI Pro 3, Bölüm 12)
Üretimde kullanılan, veri güdümlü hafif bir FSM tasarımı.
Shirakami vd. 2017 · A Character Decision-Making System for FINAL FANTASY XV by Combining Behavior Trees and State Machines (Game AI Pro 3, Bölüm 11)
AAA bir oyunda FSM ile davranış ağaçlarının birlikte kullanımı.

Kaynaklar: okuma 📚

NEXT LEVEL ▶
Davranış Ağaçları
Halo 2’den Unreal’a: selector, sequence, decorator ve kara tahta
SONRAKİ SUNUM ▶