Flappy Dird: macOS Finder içinde uygulanmış Flappy Bird
(eieio.games)- Flappy Dird, macOS Finder dosya listesini ekran, dosya seçme ve açma işlemlerini ise girdi olarak kullanarak Flappy Bird çalıştıran deneysel bir uygulamadır
- Temel fikir, yalnızca Finder içinde güncellenen bir dizinin Date Last Opened değerini düğme gibi okuyup, sembolik bağlantı dosya adlarını değiştirerek ekranı çizmekten yola çıkıyor
- Basit dosya adı değiştirme tabanlı render yavaştı ve ciddi ekran yırtılmasına yol açıyordu; bu yüzden AppleScript ile Finder penceresinin hedef dizinini değiştiren bir çift tamponlama yöntemiyle geliştirildi
- Çift tıklama girdisi, saniye düzeyindeki zaman damgası sınırı nedeniyle 2 FPS üzerinde kararsızdı; daha sonra Finder'ın seçili öğelerini okuma yöntemine geçildi
- Nihai yapıda döngüyü AppleScript çalıştırıyor, oyun durumu ve render işini Python üstleniyor; sistem 4 FPS'de çalışıyor ama girdi kaçırma ve Finder'a özgü kısıtlar sürüyor
Finder'ı oyun ekranı olarak kullanmak
- Flappy Dird, koduna GitHub deposu üzerinden bakılabilen ve doğrudan çalıştırılabilen Finder tabanlı bir oyundur
- Oyunda kullanım talimatları, en yüksek skor takibi ve kayan banner reklam bulunuyor
- Kullanıcı oyunu çift tıklamayla başlatıyor ve Finder penceresi içindeki herhangi bir dosyayı seçerek kuşu zıplatıyor
- Çalışma hızı saniyede 4 kare düzeyinde; bundan çok daha hızlı çalıştırmak zor ve girdiler de zaman zaman kaçıyor
Date Last Opened değerini düğme gibi kullanmak
- Çıkış noktası, Finder'da dizinler için Date Last Opened alanının bulunmasıydı
- Gözlemlenen davranış üç maddeden oluşuyordu
cdile dizine girildiğinde zaman damgası güncellenmiyor- Finder içinde dizinin sembolik bağlantısı çift tıklandığında zaman damgası güncelleniyor
- Bu değer
mdlsile saniye hassasiyetinde okunabiliyor
- Bu özellikten yararlanılarak Finder içinde bir düğme oluşturulabildi
dirdizini içinde,dir'i işaret edenbuttonadlı bir sembolik bağlantı oluşturuluyor- Başlangıçta
diriçin son açılma zamanı okunuyor - Zaman damgası düzenli olarak sorgulanıyor ve değer değiştiğinde girdi olarak kabul ediliyor
- Finder konumu değiştirilmeden
buttonaçılarakdiriçin son açılma zamanı değiştiriliyor
Emoji dosya adlarıyla 15x15 ekran çizmek
- Finder'ın varsayılan yazı tipiyle ASCII art hizalamak zordu, ancak dosya adlarına konan emojilerin sabit genişlikte gösterildiği fark edildi
- Prototipte,
diriçinde yinedir'i işaret eden 15 alt sembolik bağlantı oluşturuluyor ve her bağlantının adı bir emoji satırına dönüştürülerek ekran kuruluyordu - Render fonksiyonu
bird_y_pos,pipe_locations,framedeğerlerini alıp bir 15x15 emoji ızgarası üretiyor - Her karede dizin içindeki tüm sembolik bağlantı adları, emoji ızgarasının ilgili satırlarıyla değiştiriliyor
- Finder'ın Date Modified ölçütüne göre sıralaması için ad değiştirme sırasını ayarlayan bir hack de gerekiyordu
- Bu yöntem çalıştı ama çok yavaştı ve ekran yırtılması belirgindi
AppleScript ve çift tamponlama
- Ekran yırtılmasını azaltmak için Finder dosya listesini yenilemenin yolu aranırken AppleScript komutu
tell application "Finder" to tell front window to update every itemkullanıldı - Bu komut biraz yardımcı oldu ama yırtılma devam etti; sonrasında yön, AppleScript ile Finder'ı doğrudan kontrol etmeye kaydı
- Çift tamponlama için birkaç aday vardı
- Sembolik bağlantılarla dizin içeriğini atomik biçimde değiştirme yöntemi, Finder sembolik bağlantı dizinlerinin içeriğini genişletemediği için uygun değildi
- Birbirini işaret eden iki dizin kullanma yöntemi yırtılmayı azaltabiliyordu, ancak kare ancak kullanıcı tıkladığında ilerleyebildiği için oyun için uygun değildi
- Finder'ın gösterdiği dizini değiştirirken “son açılma zamanı”nı değiştirmeyen bir yol gerekiyordu
- Çözüm, AppleScript içindeki
tell application "Finder" to set target of front Finder window to ("PATH" POSIX file)komutuyla Finder penceresinin hedef dizinini değiştirmek oldu - Bu yöntemle çift tamponlama uygulanınca oyun 1 FPS'te akıcı çalıştı, ancak hız hâlâ düşüktü
Girdi yöntemini değiştirmek: çift tıklamadan dosya seçimine
- Çift tıklama girdisi, zaman damgası hassasiyeti saniye düzeyinde olduğu için 2 FPS üzerinde sınırına ulaşıyordu
- AppleScript ile Finder penceresindeki seçili öğeler okunabildiği için girdi yöntemi “herhangi bir dosya seçimi” olarak değiştirildi
- Yeni oyun döngüsü şu yapıdaydı
- Kullanıcı çift tıklayana kadar bekleniyor
- Her karede AppleScript ile mevcut pencerede bir dosya seçilip seçilmediği kontrol ediliyor
- Seçim varsa veya son açılma zamanı değişmişse kuş zıplıyor
- AppleScript ile Finder'ın gösterdiği dizin değiştiriliyor
- Hedef kare hızını tutturmak için kalan süre kadar bekleniyor
- Bu yöntem 2 FPS seviyesine bir ölçüde ulaştı, ancak AppleScript çağrısı yaklaşık 0.2 saniye sürdüğü için sabit maliyet yüksekti
- 2 FPS'de bile girdi kontrolünden sonra kullanıcı bir dosya seçerse bu girdi kaçırılabiliyordu
Ana döngüyü AppleScript'e taşımak
- Profilleme sırasında,
1sayısını log'a yazıp çıkan bir AppleScript'in yaklaşık 0.14 saniye sürdüğü görüldü - Daha sonra bu ölçümün adil bir AppleScript başlangıç süresi ölçümü olmadığı ve boş bir AppleScript için sürenin yaklaşık 0.06 saniyeye daha yakın olabileceği düzeltmesi eklendi
- Her karede AppleScript başlatma maliyetini ödememek için ana döngü AppleScript'e taşındı, Python ise oyun mantığı ve render işini üstlendi
- Temel yapı şöyleydi
game.py awaitile kullanıcının çift tıklayarak başlatması bekleniyorgame.py start-frameile karenin başlangıç zamanı kaydediliyor- Finder seçim öğesi sayısı okunup
game.py tickkomutuna aktarılıyor ve kare render ediliyor ticktarafından hazırlanan dizin adı alınıp Finder penceresinin hedefi bu dizine çevriliyorgame.py sleepile hedef kare hızı tutuluyor ve oyuncu kaybetmediyse döngü sürüyor
- Sonrasında
start-framevesleepbirleştirilebildi, ayrıca öldükten sonra yeniden başlatma için bir döngü eklendi
Oyunu tamamlamak için gereken ayrıntılar
- Oyun durumu, her karede okunup yazılan
state.jsondosyası oluşturularak tutuluyor - Kullanılan metin karakterlerinin genişliği emoji'lerden dar olduğu için hizalamayı korumak adına çok sayıda hardcode edilmiş boşluk kullanıldı
- Üstte kayan banner metni özellikle zorluydu; bir seferde kaç karakter gösterileceğini tahmin eden
read_n_ad_charsfonksiyonu yazıldı - Çalışma dizinini AppleScript'e aktarmak yerine,
first-time-setupPython çağrısında şablon değişkenleri yerleştirildi - AppleScript Python çıktısını yuttuğu için log'lar bir dosyaya append edilip daha sonra
catile kontrol edildi - Arka arkaya iki karede tıklanırsa bir satır daha yukarı zıplamayı sağlayarak oyun hissi iyileştirildi
Oyun motoru olmadan yapılan Finder oyununun kısıtları
- Prototip yaklaşık 90 satırdı, nihai kod ise yaklaşık 550 satır, ancak bunun yaklaşık üçte biri boilerplate ya da sabitlerden oluşuyor
- Oyun motoru kullanmadan küçük bir 2D dizi içinde kare durumunu tutmak, tüm oyunu zihinde taşımayı kolaylaştırdı
- 4 FPS ve sınırlı girdi büyük kısıtlar olsa da, aynı yöntemle başka oyunlar yapmak da mümkün görünüyor
- Finder içinde Tetris yapma fikri de ortaya atıldı ve zor ama mümkün olabilecek bir örnek olarak kaldı
1 yorum
Hacker News yorumları
Sözün bittiği derecede şaşırtıcıydı; kendimi kalıpların dışında düşünen biri olarak görmeme rağmen, bu yazıyı okumadan önce bunun imkânsız olduğunu söylerdim sanırım.
Birinin hafta sonu kendine meydan okumak için yaptığı bir projenin gerçekten mümkün olup olmadığını sonuna kadar zorladığını görmek harikaydı; yazıyı da büyük keyifle okudum.
Bob, cuma öğleden sonra bira içmeye giden ekibe yetişmek için aceleyle ceketini ve sırt çantasını kapar, MacOS’ta çalışan Flappy Bird’ü açık bıraktığını fark etmez.
Resmî tatilin de eklendiği hafta sonu boyunca dizüstünün SSD yazma ömrü saniyede 4 kare hızında tükenir; salı günü dönen Bob, SSD’nin artık çalışmadığını görür.
“Bu bir görsel. Artık tweet’i nasıl embed edeceğimi bilmiyorum” yaklaşımı aslında çok daha hoşuma gidiyor.
OP’nin AppleScript başlatma hızı sorununu JavaScript ile yeniden yazarak çözüp çözemeyeceğini merak ediyorum.
Birkaç yıldır temel framework ve komut satırı aracı
osascript, AppleScript ile JavaScript’i neredeyse eşdeğer biçimde destekliyor; JavaScript tarafında da$nesnesinin içinde epey şey var.Python’dan AppleScript event’lerini native olarak çağırmak için py-appscript kullanmayı da deneyebilirsiniz.
https://github.com/hhas/appscript/tree/master/py-appscript
Sadece Ruby sürümü Appscript’i kullandım ama Python sürümü de aynı şekilde çalışıyorsa, AppleScript’in kendisine gerek bırakmayacak kadar ideal bir seçenek gibi görünüyor.
Böyle olmaması gereken yerdeki oyunları seviyorsanız, daha da saçma projeler de var.
Fontemon: fontun içindeki oyun https://www.coderelay.io/fontemon.html
Dungeons & Directories: dosya tarayıcısının içindeki metin macerası https://wheybags.com/dungeons_and_directories/ — bunu ben yaptım.
Font oyunuyla da biraz oynadım; font yazmak nasıl bir şey görmek için Hexagone[1]’u yaptım. Ne tür bir oyun yapacağımdan emin olamadım ama böyle ilhamların gelmesi eğlenceli.
[1] http://eieio.games/nonsense/hexagone-converting-hex-to-rgb-w...
https://github.com/facundoolano/rpg-cli
Yazının tamamına yayılan “daha iyisi yapılabilir” tavrını gerçekten sevdim. Tüm geliştiriciler böyle bir zihniyete sahip olsa nasıl olurdu diye düşündürüyor.
Elbette herkes böyle olursa, her zaman daha iyisi yapılabileceği için hiçbir şey bitmez; bazen “bu kadarı yeterli” diyecek birine ihtiyaç var.
Bu tür hobi projelerinde “daha iyisi yapılabilir” tarafını korumak daha kolay. İşte ise genelde bir gün daha harcayıp cilalamanın değerinin, yapmak istediğimiz diğer her şeyden büyük olup olmadığını düşünmek gerekiyor.
Ama Flappy Dird’de projeden memnun kalana ya da oyunu artık daha fazla zorlayamayacağıma inanana kadar devam etmek istedim.
Başlığı görünce önce Finder penceresinin ikon görünümünde AppleScript ile ikonları sprite gibi hareket ettirme yöntemi sandım.
Gerçekte liste görünümünde uygulanmış olması ve başlangıçta dizinin “son açılma tarihi”ni poll etme yöntemini kullanması şaşırttı; kesinlikle yaratıcı bir yaklaşım.
Küçük oyunları mümkün olan her yere yerleştirme ruhu bu yazıda gerçekten keyifli.
Eski GNOME’da kullanılabilen Fortune Teller fish görev çubuğu widget’ını hatırlattı.
Tamamen ayrı olarak, “MacOS Finder” şeklindeki büyük harf kullanımını görünce önce MacOS 8 ya da 9 için bir eklenti sandım; gerçek sonuç ise çok daha eğlenceliydi.
Sıradaki meydan okuma: Finder içinde Yaşam Oyunu çalıştırmak ve onun içinde Finder çalıştıran bir Mac’i emüle etmek.
Başta klasörleri desene göre oluşturup sağ tıklayarak sıralarsanız ızgaraya yerleşirler. Sonra script, DS_Store dosyasından klasör konumlarını okur; bir sonraki karenin yeni konumlarına günceller ya da gerekirse klasör oluşturup siler.