Apa yang ditanyakan seorang ibu! Saya mungkin mempermalukan diri sendiri mencoba yang ini dengan pikiran aneh saya (dan saya akan senang mendengar saran jika saya benar-benar tidak aktif). Tetapi bagi saya hal yang paling berguna yang saya pelajari belakangan ini di domain saya (yang termasuk game di masa lalu, sekarang VFX) adalah untuk menggantikan interaksi antara antarmuka abstrak dengan data sebagai mekanisme decoupling (dan akhirnya mengurangi jumlah informasi yang diperlukan antara hal-hal dan tentang satu sama lain sampai batas minimum absolut). Ini mungkin terdengar sangat gila (dan saya mungkin menggunakan segala macam istilah yang buruk).
Namun katakanlah saya memberi Anda pekerjaan yang cukup mudah dikelola. Anda memiliki file ini yang berisi data adegan dan animasi untuk disajikan. Ada dokumentasi yang mencakup format file. Satu-satunya tugas Anda adalah memuat file, membuat gambar-gambar cantik untuk animasi menggunakan path tracing, dan menampilkan hasilnya ke file gambar. Itu aplikasi skala yang cukup kecil yang mungkin tidak akan menjangkau lebih dari puluhan ribu LOC bahkan untuk perender yang cukup canggih (pasti bukan jutaan).
Anda memiliki dunia terisolasi Anda sendiri untuk penyaji ini. Itu tidak terpengaruh oleh dunia luar. Ini mengisolasi kompleksitasnya sendiri. Di luar kekhawatiran membaca file adegan ini dan mengeluarkan hasil Anda ke file gambar, Anda bisa fokus sepenuhnya hanya pada rendering. Jika ada yang salah dalam proses, Anda tahu itu ada di renderer dan tidak ada yang lain, karena tidak ada lagi yang terlibat dalam gambar ini.
Sementara itu, katakan saja Anda harus membuat penyaji Anda berfungsi dalam konteks perangkat lunak animasi besar yang sebenarnya memiliki jutaan LOC. Alih-alih hanya membaca format file yang ramping dan terdokumentasi untuk mendapatkan data yang diperlukan untuk rendering, Anda harus melalui semua jenis antarmuka abstrak untuk mengambil semua data yang perlu Anda lakukan:
Tiba-tiba penyaji Anda tidak lagi berada di dunianya sendiri yang kecil dan terisolasi. Ini terasa begitu, jauh lebih kompleks. Anda harus memahami desain keseluruhan dari keseluruhan perangkat lunak sebagai satu kesatuan organik dengan bagian yang berpotensi banyak bergerak, dan mungkin bahkan kadang-kadang harus memikirkan implementasi hal-hal seperti jerat atau kamera jika Anda menekan bottleneck atau bug di salah satu fungsi.
Fungsionalitas vs. Data yang Efisien
Dan salah satu alasannya adalah karena fungsionalitas jauh lebih kompleks daripada data statis. Ada juga banyak cara pemanggilan fungsi bisa salah dengan cara yang tidak bisa membaca data statis. Ada begitu banyak efek samping tersembunyi yang dapat terjadi ketika memanggil fungsi-fungsi tersebut meskipun secara konseptual hanya mengambil data read-only untuk rendering. Itu juga dapat memiliki banyak alasan untuk berubah. Beberapa bulan dari sekarang, Anda mungkin menemukan antarmuka mesh atau tekstur mengubah atau mencela bagian-bagian dengan cara yang mengharuskan Anda untuk menulis ulang bagian yang besar dan kuat dari penyaji Anda dan mengikuti perubahan itu, meskipun Anda mengambil data yang sama persis, meskipun input data ke renderer Anda belum berubah sama sekali (hanya fungsionalitas yang diperlukan untuk akhirnya mengakses semuanya).
Jadi, jika mungkin, saya telah menemukan bahwa data yang disederhanakan adalah mekanisme pelepasan jenis yang sangat baik yang benar-benar memungkinkan Anda menghindari keharusan untuk memikirkan keseluruhan sistem secara keseluruhan dan memungkinkan Anda hanya berkonsentrasi pada satu bagian yang sangat spesifik dari sistem untuk membuat perbaikan, tambahkan fitur baru, perbaiki hal-hal, dll. Ini mengikuti pola pikir I / O yang sangat besar untuk bagian-bagian besar yang membentuk perangkat lunak Anda. Input ini, lakukan hal Anda, output itu, dan tanpa melalui puluhan antarmuka abstrak panggilan fungsi tak berujung sepanjang jalan. Dan itu mulai menyerupai, sampai taraf tertentu, pemrograman fungsional.
Jadi ini hanya satu strategi dan mungkin tidak berlaku untuk semua orang. Dan tentu saja jika Anda terbang sendiri, Anda masih harus mempertahankan semuanya (termasuk format data itu sendiri), tetapi perbedaannya adalah ketika Anda duduk untuk melakukan perbaikan pada renderer itu, Anda dapat benar-benar hanya fokus pada renderer sebagian besar dan tidak ada yang lain. Ia menjadi sangat terisolasi di dunianya sendiri yang kecil - sekitar terisolasi seperti halnya dengan data yang diperlukan untuk input yang disederhanakan.
Dan saya menggunakan contoh format file tetapi tidak harus berupa file yang menyediakan data streamline yang menarik untuk input. Itu bisa berupa basis data dalam memori. Dalam kasus saya ini adalah sistem entitas-komponen dengan komponen yang menyimpan data yang menarik. Namun saya telah menemukan prinsip dasar decoupling terhadap data yang disederhanakan (namun Anda melakukannya) sehingga jauh lebih sedikit pada kapasitas mental saya daripada sistem sebelumnya yang saya kerjakan yang berputar di sekitar abstraksi dan banyak dan banyak dan banyak interaksi yang terjadi di antara semua ini antarmuka abstrak yang membuatnya tidak mungkin hanya duduk dengan satu hal dan hanya memikirkan hal itu dan sedikit hal lainnya. Otak saya memenuhi hampir semua jenis sistem sebelumnya dan ingin meledak karena ada begitu banyak interaksi yang terjadi di antara begitu banyak hal,
Decoupling
Jika Anda ingin meminimalkan berapa banyak basis kode yang lebih besar membebani otak Anda, maka buatlah itu jadi setiap bagian yang kuat dari perangkat lunak (keseluruhan sistem rendering, seluruh sistem fisika, dll) hidup di dunia yang paling terpencil mungkin. Minimalkan jumlah komunikasi dan interaksi yang berjalan ke minimum minimum melalui data yang paling efisien. Anda bahkan mungkin menerima beberapa redundansi (beberapa pekerjaan yang berlebihan untuk prosesor, atau bahkan untuk diri Anda sendiri) jika pertukaran adalah sistem yang jauh lebih terisolasi yang tidak harus berbicara dengan banyak hal lain sebelum dapat melakukan pekerjaannya.
Dan ketika Anda mulai melakukan itu, rasanya seperti Anda memelihara selusin aplikasi skala kecil alih-alih satu aplikasi raksasa. Dan saya menemukan itu jauh lebih menyenangkan juga. Anda dapat duduk dan hanya bekerja pada satu sistem sesuka hati Anda tanpa melibatkan diri dengan dunia luar. Itu hanya menjadi menginput data yang tepat dan mengeluarkan data yang tepat di akhir ke suatu tempat di mana sistem lain dapat melakukannya (pada titik mana beberapa sistem lain mungkin memasukkan itu dan melakukan hal itu, tetapi Anda tidak perlu peduli tentang itu ketika bekerja pada sistem Anda). Tentu saja Anda masih harus berpikir tentang bagaimana semuanya terintegrasi dalam antarmuka pengguna, misalnya (saya masih harus memikirkan desain semuanya sebagai keseluruhan untuk GUI), tetapi setidaknya tidak ketika Anda duduk dan bekerja pada sistem yang ada atau memutuskan untuk menambahkan yang baru.
Mungkin saya sedang menggambarkan sesuatu yang jelas bagi orang-orang yang selalu mengetahui metode rekayasa terbaru. Saya tidak tahu Tapi itu tidak jelas bagiku. Saya ingin mendekati desain perangkat lunak di sekitar objek yang saling berinteraksi dan fungsi dipanggil untuk perangkat lunak skala besar. Dan buku-buku yang saya baca awalnya pada desain perangkat lunak skala besar yang berfokus pada desain antarmuka di atas hal-hal seperti implementasi dan data (mantra pada waktu itu adalah bahwa implementasi tidak terlalu penting, hanya antarmuka, karena yang pertama dapat dengan mudah diganti atau diganti ). Pada awalnya saya tidak berpikir secara intuitif untuk memikirkan interaksi perangkat lunak sebagai pendahuluan untuk hanya memasukkan dan mengeluarkan data antara subsistem besar yang nyaris tidak berbicara satu sama lain kecuali melalui data yang disederhanakan ini. Namun ketika saya mulai mengalihkan fokus saya untuk mendesain konsep itu, itu membuat segalanya jadi lebih mudah. Saya bisa menambahkan lebih banyak kode tanpa otak saya meledak. Rasanya seperti saya sedang membangun pusat perbelanjaan bukannya menara yang bisa runtuh jika saya menambahkan terlalu banyak atau jika ada patah di bagian mana pun.
Implementasi Kompleks vs. Interaksi Kompleks
Ini adalah satu lagi yang harus saya sebutkan karena saya menghabiskan sebagian besar bagian awal karir saya mencari implementasi yang paling sederhana. Jadi saya menguraikan hal-hal menjadi potongan-potongan paling muda dan paling sederhana, berpikir saya meningkatkan rawatan.
Kalau dipikir-pikir, saya gagal menyadari bahwa saya menukar satu jenis kompleksitas dengan yang lain. Dalam mengurangi semuanya menjadi potongan-potongan yang paling sederhana, interaksi yang terjadi di antara potongan-potongan kecil berubah menjadi web interaksi yang paling kompleks dengan pemanggilan fungsi yang kadang-kadang masuk hingga 30 level ke dalam callstack. Dan tentu saja, jika Anda melihat salah satu fungsi, itu sangat, sangat sederhana dan mudah untuk mengetahui apa fungsinya. Tetapi Anda tidak mendapatkan banyak informasi yang bermanfaat pada saat itu karena setiap fungsi hanya melakukan sedikit. Anda kemudian harus menelusuri semua jenis fungsi dan melompati segala macam lingkaran untuk benar-benar mengetahui apa yang mereka semua lakukan dengan cara yang dapat membuat otak Anda ingin meledak lebih dari satu lebih besar,
Itu bukan untuk menyarankan benda dewa atau semacamnya. Tapi mungkin kita tidak perlu memotong objek mesh kita menjadi benda terkecil seperti objek vertex, objek tepi, objek wajah. Mungkin kita bisa menyimpannya di "mesh" dengan implementasi yang lebih kompleks di belakangnya dengan imbalan interaksi kode yang lebih sedikit secara radikal. Saya dapat menangani implementasi yang cukup kompleks di sana-sini. Saya tidak dapat menangani trilyun interaksi dengan efek samping yang terjadi pada siapa-tahu-di mana dan dalam urutan apa.
Setidaknya saya menemukan bahwa, jauh lebih sedikit pajak pada otak, karena interaksi itulah yang membuat otak saya terluka dalam basis kode yang besar. Tidak ada satu hal khusus.
Generalitas vs. Spesifisitas
Mungkin terkait dengan hal di atas, saya dulu suka generalisasi dan penggunaan kembali kode, dan dulu berpikir tantangan terbesar merancang antarmuka yang baik adalah memenuhi berbagai kebutuhan terluas karena antarmuka akan digunakan oleh segala macam hal yang berbeda dengan kebutuhan yang berbeda. Dan ketika Anda melakukan itu, Anda pasti harus memikirkan seratus hal sekaligus, karena Anda mencoba menyeimbangkan kebutuhan seratus hal sekaligus.
Menggeneralisasi banyak hal membutuhkan banyak waktu. Lihat saja perpustakaan standar yang menyertai bahasa kita. Pustaka standar C ++ berisi fungsionalitas yang sangat sedikit, namun itu membutuhkan tim orang untuk memelihara dan menyesuaikan dengan seluruh komite orang yang berdebat dan membuat proposal tentang desainnya. Itu karena sedikit fungsionalitas sedang mencoba untuk menangani kebutuhan seluruh dunia.
Mungkin kita tidak perlu mengambil banyak hal sejauh ini. Mungkin tidak apa-apa untuk hanya memiliki indeks spasial yang hanya digunakan untuk deteksi tabrakan antara jerat diindeks dan tidak ada yang lain. Mungkin kita bisa menggunakan yang lain untuk jenis permukaan lainnya, dan yang lain untuk rendering. Dulu saya terlalu fokus untuk menghilangkan redudansi semacam ini, tetapi sebagian alasannya adalah karena saya berurusan dengan struktur data yang sangat tidak efisien yang diterapkan oleh banyak orang. Tentu saja jika Anda memiliki satu octree yang membutuhkan 1 gigabyte untuk mesh segitiga 300k belaka, Anda tidak ingin memiliki satu lagi dalam memori.
Tetapi mengapa oktaf begitu tidak efisien? Saya dapat membuat octrees yang hanya membutuhkan 4 byte per node dan membutuhkan kurang dari satu megabyte untuk melakukan hal yang sama seperti versi gigabyte sambil membangun di sebagian kecil dari waktu dan melakukan permintaan pencarian yang lebih cepat. Pada titik itu, beberapa redundansi benar-benar dapat diterima.
Efisiensi
Jadi ini hanya relevan dengan bidang-bidang yang kritis-kinerja tetapi semakin baik Anda mendapatkan hal-hal seperti efisiensi memori, semakin banyak Anda mampu membuang sedikit lebih banyak (mungkin menerima redundansi sedikit lebih dalam pertukaran untuk mengurangi generalisasi atau decoupling) yang mendukung produktivitas . Dan itu membantu untuk menjadi cukup baik dan nyaman dengan profiler Anda dan belajar tentang arsitektur komputer dan hirarki memori, karena Anda dapat membuat lebih banyak pengorbanan untuk efisiensi dengan imbalan produktivitas karena kode Anda sudah sangat efisien dan mampu menjadi sedikit kurang efisien bahkan di area kritis sambil tetap mengungguli kompetisi. Saya telah menemukan bahwa peningkatan dalam bidang ini juga memungkinkan saya untuk pergi dengan implementasi yang lebih sederhana dan lebih sederhana,
Keandalan
Ini agak jelas tetapi mungkin juga menyebutkannya. Hal-hal yang paling andal Anda membutuhkan overhead intelektual minimum. Anda tidak perlu terlalu memikirkan mereka. Mereka hanya bekerja. Sebagai hasilnya, semakin besar Anda menumbuhkan daftar suku cadang ultra andal yang juga "stabil" (tidak perlu diubah) melalui pengujian menyeluruh, semakin sedikit yang harus Anda pikirkan.
Spesifik
Jadi semua itu di atas mencakup beberapa hal umum yang telah membantu saya, tetapi mari kita beralih ke aspek yang lebih spesifik untuk area Anda:
Dalam proyek saya yang lebih kecil, mudah untuk mengingat peta mental tentang bagaimana setiap bagian dari program ini bekerja. Dengan melakukan ini, saya dapat sepenuhnya menyadari bagaimana perubahan apa pun akan memengaruhi program lainnya dan menghindari bug dengan sangat efektif serta melihat dengan tepat bagaimana fitur baru harus masuk ke dalam basis kode. Namun, ketika saya mencoba membuat proyek yang lebih besar, saya merasa tidak mungkin untuk menyimpan peta mental yang baik yang mengarah pada kode yang sangat berantakan dan banyak bug yang tidak diinginkan.
Bagi saya ini cenderung terkait dengan efek samping yang kompleks dan aliran kontrol yang kompleks. Itu adalah pandangan tingkat yang agak rendah tentang hal-hal tetapi semua antarmuka yang tampak terbaik dan semua decoupling dari beton ke abstrak tidak dapat membuatnya lebih mudah untuk berpikir tentang efek samping kompleks yang terjadi dalam aliran kontrol yang kompleks.
Sederhanakan / kurangi efek samping dan / atau sederhanakan aliran kontrol, idealnya keduanya. dan umumnya Anda akan merasa jauh lebih mudah untuk berpikir tentang apa yang dilakukan sistem yang jauh lebih besar, dan juga apa yang akan terjadi sebagai tanggapan terhadap perubahan Anda.
Selain masalah "peta mental" ini, saya merasa sulit untuk menjaga kode saya dipisahkan dari bagian lain dari dirinya sendiri. Misalnya, jika dalam permainan multipemain ada kelas yang menangani fisika gerakan pemain dan yang lain menangani jaringan, maka saya tidak melihat ada cara agar salah satu kelas ini tidak bergantung pada yang lain untuk mendapatkan data gerakan pemain ke sistem jaringan. untuk mengirimnya melalui jaringan. Kopling ini merupakan sumber signifikan dari kompleksitas yang mengganggu peta mental yang baik.
Secara konseptual Anda harus memiliki beberapa kopling. Ketika orang berbicara tentang decoupling, mereka biasanya berarti mengganti satu jenis dengan jenis lain, jenis yang lebih diinginkan (biasanya menuju abstraksi). Bagi saya, mengingat domain saya, cara otak saya bekerja, dll. Jenis yang paling diinginkan untuk mengurangi persyaratan "peta mental" menjadi minimum adalah bahwa data efisien yang dibahas di atas. Satu kotak hitam memuntahkan data yang dimasukkan ke dalam kotak hitam lain, dan keduanya sama-sama tidak menyadari keberadaan satu sama lain. Mereka hanya mengetahui tempat sentral di mana data disimpan (mis: sistem file pusat atau basis data pusat) tempat mereka mengambil input, melakukan sesuatu, dan mengeluarkan output baru yang kemudian dimasukkan oleh kotak hitam lain.
Jika Anda melakukannya dengan cara ini, sistem fisika akan bergantung pada database pusat dan sistem jaringan akan bergantung pada database pusat, tetapi mereka tidak akan tahu apa-apa tentang satu sama lain. Mereka bahkan tidak perlu tahu bahwa ada satu sama lain. Mereka bahkan tidak perlu tahu bahwa antarmuka abstrak untuk satu sama lain ada.
Terakhir, saya sering menemukan diri saya datang dengan satu atau lebih "manajer" kelas yang mengoordinasikan kelas lain. Misalnya, dalam sebuah game, sebuah kelas akan menangani loop tick utama dan akan memanggil metode pembaruan di kelas jaringan dan pemain. Ini bertentangan dengan filosofi dari apa yang saya temukan dalam penelitian saya bahwa setiap kelas harus dapat diuji unit dan dapat digunakan secara terpisah dari yang lain, karena setiap kelas manajer seperti itu dengan tujuan bergantung pada sebagian besar kelas lain dalam proyek. Selain itu, kelas manajer orkestrasi dari sisa program adalah sumber signifikan dari kompleksitas non-mental-mappable.
Anda cenderung membutuhkan sesuatu untuk mengatur semua sistem dalam gim Anda. Central mungkin setidaknya kurang kompleks dan lebih mudah dikelola daripada seperti sistem fisika yang menerapkan sistem rendering setelah selesai. Tapi di sini kita pasti membutuhkan beberapa fungsi yang dipanggil, dan lebih disukai mereka abstrak.
Jadi, Anda dapat membuat antarmuka abstrak untuk sistem dengan update
fungsi abstrak . Kemudian dapat mendaftar sendiri dengan mesin pusat dan sistem jaringan Anda dapat mengatakan, "Hei, saya adalah sistem dan ini adalah fungsi pembaruan saya. Silakan hubungi saya dari waktu ke waktu." Dan kemudian mesin Anda dapat loop melalui semua sistem seperti itu dan memperbaruinya tanpa panggilan fungsi hard-coding ke sistem tertentu.
Itu memungkinkan sistem Anda untuk hidup lebih seperti dunia terisolasi mereka sendiri. Mesin permainan tidak perlu tahu tentang mereka secara khusus (dengan cara yang nyata) lagi. Dan kemudian sistem fisika Anda mungkin memiliki fungsi pembaruan yang disebut, pada saat itu ia memasukkan data yang dibutuhkan dari database pusat untuk gerakan segalanya, menerapkan fisika, lalu mengeluarkan kembali gerakan yang dihasilkan.
Setelah itu sistem jaringan Anda mungkin memiliki fungsi pembaruan yang disebut, pada saat itu ia memasukkan data yang dibutuhkan dari basis data pusat dan output, katakanlah, socket data ke klien. Sekali lagi tujuannya seperti yang saya lihat adalah mengisolasi setiap sistem sebanyak mungkin sehingga ia dapat hidup di dunia kecilnya sendiri dengan sedikit pengetahuan tentang dunia luar. Itu pada dasarnya jenis pendekatan yang diadopsi dalam ECS yang populer di kalangan mesin game.
ECS
Saya kira saya harus membahas ECS sedikit karena banyak pemikiran saya di atas berputar di sekitar ECS dan mencoba merasionalisasi mengapa pendekatan berorientasi data ini untuk decoupling telah membuat pemeliharaan jauh lebih mudah daripada sistem berorientasi objek dan sistem berbasis COM yang telah saya pertahankan. di masa lalu meskipun telah melanggar hampir semua yang saya anggap sakral yang saya pelajari tentang SE. Juga mungkin masuk akal bagi Anda jika Anda ingin membangun gim berskala lebih besar. Jadi ECS bekerja seperti ini:
Dan seperti pada diagram di atas, MovementSystem
mungkin update
fungsinya disebut. Pada titik ini mungkin permintaan database pusat untuk PosAndVelocity
komponen sebagai data untuk dimasukkan (komponen hanya data, tidak ada fungsi). Maka mungkin akan mengulanginya, memodifikasi posisi / kecepatan, dan secara efektif menampilkan hasil baru. Kemudian RenderingSystem
mungkin memiliki fungsi pembaruan yang disebut, di mana ia meminta database untuk PosAndVelocity
dan Sprite
komponen, dan menampilkan gambar ke layar berdasarkan data itu.
Semua sistem sama sekali tidak menyadari keberadaan satu sama lain, dan mereka bahkan tidak perlu memahami apa a Car
adalah. Mereka hanya perlu mengetahui komponen spesifik dari minat masing-masing sistem yang menyusun data yang diperlukan untuk mewakili satu. Setiap sistem seperti kotak hitam. Input data dan output data dengan pengetahuan minimal tentang dunia luar, dan dunia luar juga memiliki pengetahuan minimal tentang itu. Mungkin ada beberapa peristiwa yang mendorong dari satu sistem dan muncul dari yang lain sehingga, katakanlah, tabrakan dua entitas dalam sistem fisika dapat menyebabkan audio untuk melihat peristiwa tabrakan yang menyebabkannya memainkan suara, tetapi sistem masih dapat dilupakan. tentang satu sama lain. Dan saya telah menemukan sistem seperti itu jauh lebih mudah untuk dipikirkan. Mereka tidak membuat otak saya ingin meledak bahkan jika Anda memiliki lusinan sistem, karena masing-masing sangat terisolasi. Kamu tidak Anda tidak perlu memikirkan kompleksitas segalanya secara keseluruhan ketika Anda memperbesar dan mengerjakan yang diberikan. Dan karena itu, juga sangat mudah untuk memprediksi hasil perubahan Anda.