Ini adalah artikel tentang programing, jika kalian mencari yang bukan programing, mohon klik di sini untuk membaca artikel dengan judul serupa.
Orang yang besar di zaman 90an pastinya tahu konsol game Nintendo Entertainment System (NES) alias Nintendo jadul dengan pionirnya Super Mario Bros. Intinya saya ingin membeberkan seluruh konsol game yang permainannya masih pada pixelated itu, terutama sebelum era Playstation 1.
Banyak sekali permainan yang mungkin masih dapat dikenang dari mulai Mario, Mega Man, Sonic, Contra, Castlevania, Arkanoid, dan puluhan lainnya.
Saya dulu pernah memodifikasi game jadul, Mega Man 5, dan mengodingnya ulang menjadi sebuah game baru dengan bahasa Assembly. Benar, bahasa Assembly NMOS 6502 yang commandnya masih LDA, STA, JSR, dst.
Saya benar-benar mengoding hingga membuat musiknya hanya dengan heksadesimal, saya mencoba memahami satu per satu kodenya seperti βA9 08 9D 00 03β (5 byte kode) itu maksudnya apa, untuk apa, cara kerjanya bagaimana.
Ekstrem.
Lalu apa yang saya dapat?
Permainan Mega Man yang saya remodel adalah murni buatan Capcom yang hingga sekarang pun industri besar tersebut masih mengeluarkan game-game legendarisnya.
Artinya, semua yang saya dekode atau kode-kode saya baca tersebut bukanlah kode sembarangan. Secara, perusahaan game ternama yang membuatnya.
Saya di sana belajar beberapa hal yang hingga sekarang saya masih begitu takjub untuk senantiasa diterapkan.
Fungsi atau subrutin, ternyata sering saya jumpai di tumpukan kodingan yang masih seperti manuskrip kuno ala-ala hieroglif aka ukiran-ukiran gambar yang menyampaikan pesan tersirat di dinding-dinding piramid Mesir. Oke gajebo.
Maksud saya, meski di bahasa pemrograman yang masih hanya kode-kode heksadesimal, penggunaan fungsi sangatlah intens atau begitu sering digunakan.
Jadi saat ada efek suara muncul, mereka memanggil subrutin di alamat tertentu yang dikhususkan untuk pemanggilan efek suara. Begitu pun dengan mengganti musik, mengganti warna, mengganti senjata, mengecek jika terkena dinding, dan puluhan lainnya.
Seluruh aktivitas game dari yang paling remeh sekali pun ternyata semuanya sudah memiliki fungsi sendiri. Jadi saya hanya tinggal panggil fungsi yang saya inginkan tanpa harus koding ulang.
Ajaibnya, jika saya ingin memberikan perintah misalnya mengganti senjata, saya hanya perlu menaruh angka yang menjadi indeks senjata yang saya inginkan sebagai argumen lalu memanggil fungsi atau subrutinnya. Setelah itu senjata terganti dengan yang saya inginkan. Saya tidak menyangka akan semudah ini.
Atau misalnya, saat saya ingin senjata Mega Man bergerak di kecepatan 4 piksel per frame, saya cukup menaruh angka 4 dan memanggil subrutin/fungsi kecepatannya. Kemudian bukan sulap dan bukan sihir, senjatanya benar-benar bergerak dengan kecepatan tersebut.
Saya tidak perlu pusing-pusing lagi membuat kodingan dari awal mengenai bagaimana sebuah objek dapat bergerak pada kecepatan 4 piksel per frame setiap objek yang ingin saya gerakkan.
Praktis, saya benar-benar bahagia di sini. Benar-benar praktis.
Maka dari itu saya membuat algoritma AI tentang bagaimana rajanya menyerang Mega Man segila mungkin karena yang saya lakukan hanyalah menyusun blok-blok subrutinnya.
Sumpah, mengapa saya tidak menyadari betapa dahsyatnya manfaat dari fungsi dari awal?
Belajar dari sini, saya kedepannya akan membuat library atau pustaka fungsi-fungsi saya sendiri yang mungkin akan saya gunakan setiap saya membuat proyek koding entah itu permainan, aplikasi, atau hanya iseng.
Jika kalian belajar bahasa C atau C++, maka salah satu istilah yang mungkin kalian sering jumpai adalah pointer. Sebenarnya apa sih pointer itu?
Begini, saat saya meremodel permainan yang ukurannya hanya 512KB (benar, KaBe alias Kilobyte) dan itu pun sudah termasuk grafik, level, dan musiknya, saya benar-benar menghadapi keterbatasan memory yang sangat dahsyat.
Masalahnya, seluruh apa yang terlihat di permainan tersebut, termasuk di dalamnya grafik, musik, variabel-variabel yang sedang berjalan, semuanya dipadatkan di RAM permainan yang hanya 8bit atau hanya berjumlah 65536 slot atau 64KB.
Bahkan untuk variabel-variabel objeknya itu sendiri hanya disediakan 2048 slot saja atau RAM untuk objek hanya tersedia 2KB saja. Sisanya untuk musik, level data, dan tentu saja, grafik yang ditampilkan atau dirender.
Sekarang bayangkan sebuah objek, misalnya Mega Man itu sendiri. Doi punya kecepatan horizontal, kecepatan vertikal saat melompat, posisi X dan Ynya, kemudian warna bodynya, animasinya, senjata yang ia pegang, dan variabel lainnya.
Apalagi jika variabel tersebut nilainya tidak bulat alias desimal yang pastinya memerlukan slot tambahan untuk si desimalnya.
Misalnya, kecepatan Mega Man saat berjalan adalah 1,5 piksel per frame. Artinya, variabel kecepatan itu menempati 2 slot di RAM. Slot pertama untuk nilai bilangan bulat/integernya (1), dan slot kedua untuk nilai desimalnya (0,5).
Nah, untuk hanya untuk Mega Man saja slot RAM yang dibutuhkan sudah 10 buah. Sekarang pikirkan bagaimana sistem akan menghandle seluruh objek musuh, senjata, dan objek sekunder seperti pijakan berjalan, elevator, dan lain-lain yang totalnya seabrek-abrek itu?
Apakah muat seluruh variabel permainan sekompleks itu hanya ditaruh di RAM yang hanya berjumlah 2048 slot?
Di sinilah saya memahami pointer ini ditujukan untuk βmemesanβ ruangan khusus dari grup variabel.
Misalnya, grup variabel kecepatan horizontal utama untuk seluruh objek memiliki 16 slot dari alamat RAM 300 hingga 30F (ingat, heksadesimal).
Begitu pun dengan variabel animasi seluruh objek, warna seluruh objek, dan lain sebagainya, semua sudah memiliki kluster sendiri di RAMnya demi memastikan permainan dapat berjalan di RAM yang sebegitu kecilnya.
Jadi seluruh kegiatan kecepatan horizontal di permainan terekam hanya kluster alamat RAM tersebut.
Jika slotnya ternyata tidak memadai, misalnya dari 16 slot tersebut ternyata yang dibutuhkan adalah 20, seorang programer harus pintar-pintar mengakalinya.
Tadi sudah disebutkan bahwa untuk keseluruhan variabel objek permainan, semuanya hanya dibatasi 16 slot RAM. Artinya, hanya diizinkan maksimal ada 16 objek dalam satu kali render permainan per frame.
Berarti jika dibagi lagi, hanya boleh ada 1 objek Mega Man, 3 objek senjatanya, 4 objek partikel, dan 8 objek musuh/objek sekunder.
Wew! Bagaimana jika kelebihan? Inilah yang dinamakan overflow, atau objeknya luber sampai-sampai tidak dapat ditampilkan di permainan karena memang tidak dapat tempat.
Yang buat saya terkejut, beberapa permainan Nintendo ternyata menggunakan trik flickering atau muncul dalam satu frame dan hilang dalam frame berikutnya jadi seakan terlihat berkedip dengan cepat akibat terlalu banyak objek.
Tapi kedipan tersebut tidak terlalu mengganggu sebab dalam permainan digital biasanya mereka menggunakan kecepatan 60 fps atau 60 frame per detik.
Kerennya, saat objek tersebut sedang hilang dalam satu frame, maka objek yang overflow akan ditempatkan sementara objek yang sedang hilang di frame tersebut di alamat RAM yang sama.
Saya juga saat mengoding bahasa assembly tersebut benar-benar dituntut untuk mengefisiensi setiap variabel yang saya buat/deklarasi karena itu akan memakan satu slot RAM khusus untuk variabel objek yang hanya berjumlah 16 slot tersebut.
Ekstrem.
Tapi di sinilah seninya, tanpa sadar saya mengikuti budaya koding eksotis tersebut untuk berhemat variabel dan memikirkan arsitektur aplikasi dengan matang sebelum saya membuat sebuah modul aplikasi yang akan saya terapkan di aplikasi saya.
Baru saja saya sebutkan jika kebanyakan partisi RAM yang ditujukan khusus untuk menampung keseluruhan objek permainan yang sedang ditampilkan di layar hanya berjumlah 16 slot. Itu pun 1 untuk pemain utama, 3 untuk senjata, 4 untuk partikel, dan 8 untuk objek musuh atau sekunder.
Benar-benar terbatas untuk menampung permainan yang kompleks.
Jika hal di atas belum cukup mengejutkan, Nintendo jadul (NES) hanya mengizinkan 8×8 piksel objek untuk dirender secara horizontal.
Misalnya coba bayangkan, sebuah garis horizontal yang panjangnya 100px. Dalam Nintendo jadul, garis tersebut hanya dapat dirender secara sempurna sepanjang 64px saja. Sisa 36pxnya? Flickering, masing-masing berebut slot RAM di bagian grafis yang tersisa untuk mendapat bagian.
Sekarang, coba bayangkan bagaimana permainan Nintendo jadul dapat menampung objek yang banyak? Di sinilah para developer game dipaksa untuk memecahkan masalah ini.
Coba lihat game Mario Bros pertama, bagaimana mereka menampilkan banyak koin dalam sekali render tanpa flickering? Atau bagaimana permainan Nintendo jadul lainnya dapat membuat raja/boss yang sangat besar juga tanpa flickering?
Triknya mengejutkan, mereka menggunakan background sebagai pengganti keterbatasan slot objek.
Objek koin yang ditampilkan di Mario jadul sebenarnya adalah background. Begitu Mario berhasil mengambil koin tersebut, maka tile background koinnya diganti dengan background langit. Adapun untuk animasi koinnya, sebenarnya itu hanya animasi warna piksel dari slot background yang masih tersedia.
Intinya, koin yang kalian lihat di permainan Mario jadul adalah background, bukan objek.
Begitu pun dengan raja/boss yang besar, itu hanya gabungan dari beberapa background yang dianimasikan.
Saya benar-benar belajar bahwa inilah problem solving yang seharusnya dikuasai para programmer.
Seperti misalnya ketika ada game 3D yang settingnya saat hujan, sebenarnya rintik hujannya itu hanya animasi partikel yang ditampilkan di kamera permainan, kemudian developer mengganti tekstur groundnya dengan tekstur basah. Hal ini dimaksudkan agar meminimalisir lag karena terlalu banyak perintah dan objek yang harus dieksekusi.
Yup, itulah kurang lebihnya.