- Flappy Dird adalah implementasi eksperimental yang menjalankan Flappy Bird dengan memanfaatkan daftar file di Finder macOS sebagai layar, serta aksi memilih dan membuka file sebagai input
- Ide intinya berawal dari membaca nilai Date Last Opened pada direktori, yang hanya diperbarui di Finder, seperti sebuah tombol, lalu menggambar layar dengan mengubah nama file symbolic link
- Rendering sederhana dengan hanya mengganti nama file lambat dan menimbulkan tearing yang parah, sehingga diperbaiki dengan metode double buffering yang mengubah direktori target jendela Finder melalui AppleScript
- Input double-click tidak stabil di atas 2 FPS karena keterbatasan timestamp hingga satuan detik, lalu diganti ke metode membaca selected items di Finder
- Struktur akhirnya menempatkan AppleScript untuk menjalankan loop dan Python untuk menangani status game serta rendering; game berjalan pada 4 FPS, tetapi masih ada input yang kadang terlewat dan keterbatasan khas Finder
Menggunakan Finder sebagai layar game
- Flappy Dird adalah game berbasis Finder yang kodenya bisa dilihat dan dijalankan langsung dari repositori GitHub
- Game ini mencakup petunjuk penggunaan, pelacakan skor tertinggi, dan iklan banner bergulir
- Pengguna memulai game dengan double-click, lalu memilih file apa pun di dalam jendela Finder untuk membuat burung melompat
- Kecepatan jalannya berada di kisaran 4 frame per detik, dan sulit dibuat berjalan jauh lebih cepat, sementara input juga kadang terlewat
Menggunakan Date Last Opened seperti tombol
- Titik awalnya adalah adanya field Date Last Opened untuk direktori di Finder
- Ada tiga perilaku yang diamati
- Timestamp tidak diperbarui meskipun masuk ke direktori dengan
cd - Timestamp diperbarui jika symbolic link ke direktori di-double-click di Finder
- Nilai tersebut bisa dibaca dengan presisi detik melalui
mdls
- Timestamp tidak diperbarui meskipun masuk ke direktori dengan
- Sifat ini memungkinkan pembuatan tombol di dalam Finder
- Membuat symbolic link
buttonyang menunjuk kedirdi dalam direktoridir - Membaca waktu terakhir dibuka dari
dirsaat memulai - Melakukan polling timestamp secara berulang, lalu memprosesnya sebagai input saat nilainya berubah
- Membuka
buttontanpa mengubah lokasi Finder untuk mengubah waktu terakhir dibuka daridir
- Membuat symbolic link
Menggambar layar 15x15 dengan nama file emoji
- Menyusun ASCII art dengan font bawaan Finder sulit dilakukan, tetapi emoji yang dimasukkan ke nama file ternyata ditampilkan dengan lebar yang konsisten
- Prototipe membuat 15 symbolic link turunan yang lagi-lagi menunjuk ke
dirdi dalamdir, lalu membentuk layar dengan mengubah nama tiap link menjadi satu baris emoji - Fungsi rendering menerima nilai
bird_y_pos,pipe_locations, danframe, lalu membentuk grid emoji 15x15 - Pada setiap frame, nama semua symbolic link di dalam direktori diubah menjadi masing-masing baris dari grid emoji
- Bahkan diperlukan hack untuk mengatur urutan penggantian nama agar Finder tetap mengurutkan berdasarkan
Date Modified - Pendekatan ini berhasil berjalan, tetapi sangat lambat dan tearing-nya parah
AppleScript dan double buffering
- Saat mencari cara menyegarkan daftar file Finder untuk mengurangi tearing, digunakan perintah AppleScript
tell application "Finder" to tell front window to update every item - Perintah ini sedikit membantu, tetapi tearing tetap ada, dan dari situ arahnya berkembang ke kontrol Finder secara langsung lewat AppleScript
- Ada beberapa kandidat untuk double buffering
- Metode mengganti isi direktori secara atomik dengan symbolic link tidak cocok karena Finder tidak bisa menampilkan isi direktori symbolic link dengan cara itu
- Metode memakai dua direktori yang saling menunjuk bisa mengurangi tearing, tetapi frame hanya bisa maju saat pengguna mengklik, sehingga tidak cocok untuk game
- Dibutuhkan cara mengubah direktori yang ditampilkan Finder tanpa mengubah “waktu terakhir dibuka”
- Solusinya adalah mengubah direktori target jendela Finder dengan AppleScript
tell application "Finder" to set target of front Finder window to ("PATH" POSIX file) - Setelah double buffering diterapkan dengan cara ini, game bisa berjalan mulus di 1 FPS, tetapi tetap lambat
Mengubah metode input: dari double-click ke pemilihan file
- Input double-click memiliki batas karena presisi timestamp hanya sampai detik, sehingga bermasalah di atas 2 FPS
- Karena AppleScript bisa membaca selected items pada jendela Finder, input diubah menjadi “pilih file apa pun”
- Loop game yang baru memiliki struktur berikut
- Menunggu sampai pengguna melakukan double-click
- Pada setiap frame, memeriksa lewat AppleScript apakah ada file yang dipilih di jendela saat ini
- Jika ada pilihan atau waktu terakhir dibuka berubah, burung melompat
- Mengubah direktori yang ditampilkan Finder lewat AppleScript
- Menunggu selama sisa waktu untuk menyesuaikan target frame rate
- Metode ini cukup mendekati 2 FPS, tetapi pemanggilan AppleScript memakan waktu tetap sekitar 0,2 detik
- Bahkan pada 2 FPS, input masih bisa terlewat jika pengguna memilih file setelah pengecekan input dilakukan
Memindahkan loop utama ke AppleScript
- Dalam profiling, AppleScript yang hanya mencatat angka
1ke log lalu berhenti terlihat memakan waktu sekitar 0,14 detik - Belakangan ditambahkan koreksi bahwa pengukuran ini bukan ukuran waktu startup AppleScript yang adil, dan dengan AppleScript kosong angkanya bisa lebih dekat ke sekitar 0,06 detik
- Untuk menghindari biaya startup AppleScript di setiap frame, loop utama dipindahkan ke AppleScript, sementara Python menangani logika game dan rendering
- Struktur dasarnya seperti berikut
game.py awaitmenunggu double-click pengguna untuk memulaigame.py start-framemencatat waktu mulai frame- Jumlah item yang dipilih di Finder dibaca lalu diteruskan ke
game.py tickuntuk merender frame - Nama direktori yang disiapkan oleh
tickditerima lalu target jendela Finder diarahkan ke direktori tersebut game.py sleepmenyesuaikan target frame rate, lalu proses diulang jika pemain belum kalah
- Setelah itu,
start-framedansleepbisa digabung, dan ditambahkan loop untuk restart setelah mati
Detail yang diperlukan untuk menyelesaikan game
- Status game disimpan dengan membuat file
state.jsondan membacanya serta menulisnya pada setiap frame - Teks menggunakan karakter yang lebarnya lebih sempit daripada emoji, sehingga banyak spasi hardcoded diperlukan agar perataan tetap pas
- Teks banner bergulir di bagian atas sangat sulit ditangani, sampai dibuat fungsi
read_n_ad_charsuntuk memperkirakan berapa banyak karakter yang bisa ditampilkan sekaligus - Alih-alih meneruskan working directory ke AppleScript, variabel template diganti saat pemanggilan Python
first-time-setup - Karena AppleScript menelan output Python, log ditambahkan ke file lalu diperiksa belakangan dengan
cat - Sensasi permainan diperbaiki dengan membuat ketukan pada dua frame berturut-turut menyebabkan lompatan satu baris lebih tinggi
Keterbatasan game Finder yang dibuat tanpa engine
- Prototipe awal hanya sekitar 90 baris, sedangkan kode final sekitar 550 baris, tetapi sekitar sepertiganya adalah boilerplate atau konstanta
- Tanpa game engine, menyimpan status frame dalam array 2D kecil membuat keseluruhan game tetap mudah dipahami di dalam kepala
- 4 FPS dan input yang terbatas adalah kendala besar, tetapi pendekatan yang sama tampaknya masih bisa dipakai untuk membuat game lain
- Bahkan muncul ide membuat Tetris di Finder, dan itu tetap menjadi contoh sesuatu yang sulit tetapi mungkin dilakukan
1 komentar
Opini Hacker News
Luar biasa sampai membuat saya kehabisan kata-kata; bahkan meski saya menganggap diri saya orang yang berpikir di luar kotak, sebelum membaca tulisan ini mungkin saya akan bilang ini mustahil
Saya sangat senang melihat seseorang mendorong proyek yang dibuat sebagai tantangan pribadi di akhir pekan untuk membuktikan apakah ini benar-benar mungkin, dan tulisannya juga sangat menyenangkan untuk dibaca
Bob buru-buru mengambil jaket dan ranselnya untuk ikut tim minum bir pada Jumat sore, dan tidak sadar ia membiarkan Flappy Bird yang berjalan di MacOS tetap menyala
Selama akhir pekan panjang dengan libur bank, masa pakai tulis SSD laptopnya terkuras pada laju 4 frame per detik, dan saat Bob kembali pada Selasa, ia mendapati SSD-nya tidak lagi berfungsi
Saya malah jauh lebih suka pendekatan “Ini gambar. Sekarang saya tidak tahu cara menyematkan tweet”
Saya penasaran apakah OP bisa menyelesaikan masalah kecepatan startup AppleScript dengan menulis ulangnya dalam JavaScript
Sejak beberapa tahun lalu, framework bawaan dan tool command-line
osascriptmendukung AppleScript dan JavaScript hampir setara, dan di JavaScript ada cukup banyak hal di dalam objek$Untuk memanggil event AppleScript secara native dari Python, bisa juga mencoba py-appscript
https://github.com/hhas/appscript/tree/master/py-appscript
Saya hanya pernah memakai Appscript versi Ruby, tetapi kalau versi Python bekerja dengan cara yang sama, ini tampaknya pilihan ideal sampai-sampai AppleScript itu sendiri tidak lagi diperlukan
Kalau suka game di tempat yang seharusnya tidak ada game, ada juga proyek-proyek yang lebih tidak masuk akal
Fontemon: game di dalam font https://www.coderelay.io/fontemon.html
Dungeons & Directories: petualangan teks di dalam file browser https://wheybags.com/dungeons_and_directories/ — yang ini saya buat
Saya juga sempat mengutak-atik game font, dan membuat Hexagone[1] untuk melihat seperti apa rasanya menulis font. Saya belum jadi yakin game apa yang akan dibuat, tapi menyenangkan mendapat inspirasi seperti ini
[1] http://eieio.games/nonsense/hexagone-converting-hex-to-rgb-w...
https://github.com/facundoolano/rpg-cli
Saya sangat suka sikap “ini bisa dibuat lebih baik” yang mengalir di seluruh tulisan. Saya jadi membayangkan bagaimana kalau semua developer punya pola pikir seperti ini
Tentu saja kalau semua orang begitu, karena selalu ada yang bisa dibuat lebih baik, tidak akan ada yang selesai; kadang kita butuh seseorang yang mengatakan “ini sudah cukup baik”
Dalam proyek hobi seperti ini, lebih mudah mempertahankan sisi “ini bisa dibuat lebih baik”. Di pekerjaan, biasanya kita harus memikirkan apakah nilai menghabiskan satu hari lagi untuk memolesnya lebih besar daripada semua hal lain yang ingin kita lakukan
Tapi di Flappy Dird, saya ingin terus maju sampai saya puas dengan proyeknya, atau sampai saya percaya bahwa gamenya tidak bisa didorong lebih jauh lagi
Dari judulnya, awalnya saya mengira ini akan berupa cara menggerakkan ikon seperti sprite dengan AppleScript di tampilan ikon jendela Finder
Ternyata implementasinya di tampilan daftar, dan awalnya dengan melakukan polling “tanggal terakhir dibuka” direktori; saya terkejut, dan itu jelas pendekatan yang kreatif
Saya sangat menikmati semangat tulisan ini yang membawa game kecil ke semua tempat yang memungkinkan
Ini mengingatkan saya pada widget taskbar Fortune Teller fish yang dulu bisa dipakai di GNOME
Terlepas dari itu, melihat kapitalisasi “MacOS Finder” awalnya membuat saya mengira ini ekstensi untuk MacOS 8 atau 9, tetapi hasil sebenarnya jauh lebih menyenangkan
Tantangan berikutnya adalah menjalankan Game of Life di dalam Finder, lalu mengemulasi Mac yang menjalankan Finder di dalamnya
Pertama, buat folder sesuai pola lalu klik kanan untuk mengurutkan, sehingga folder-folder itu ditempatkan di grid. Setelah itu, skrip membaca posisi folder dari file DS_Store, lalu memperbaruinya ke posisi baru untuk frame berikutnya, atau membuat dan menghapus folder bila perlu