Okey, apa yang saya ketahui sejauh ini; Entitas mengandung komponen (penyimpanan data) yang menyimpan informasi seperti; - Tekstur / sprite - Shader - dll
Dan kemudian saya memiliki sistem renderer yang menarik semua ini. Tapi yang tidak saya mengerti adalah bagaimana renderer harus dirancang. Haruskah saya memiliki satu komponen untuk setiap "tipe visual". Satu komponen tanpa shader, satu dengan shader, dll?
Hanya perlu beberapa masukan tentang apa "cara yang benar" untuk melakukan ini. Kiat dan perangkap yang harus diperhatikan.
Jawaban:
Ini adalah pertanyaan yang sulit dijawab karena setiap orang memiliki ide sendiri tentang bagaimana sistem komponen entitas harus disusun. Hal terbaik yang dapat saya lakukan adalah membagikan kepada Anda beberapa hal yang menurut saya paling berguna bagi saya.
Kesatuan
Saya mengambil pendekatan kelas lemak untuk ECS, mungkin karena saya menemukan metode pemrograman ekstrim menjadi sangat tidak efisien (dalam hal produktivitas manusia). Untuk itu, entitas bagi saya adalah kelas abstrak untuk diwarisi oleh kelas yang lebih khusus. Entitas memiliki sejumlah properti virtual dan bendera sederhana yang memberi tahu saya apakah entitas ini harus ada atau tidak. Jadi relatif terhadap pertanyaan Anda tentang sistem render, seperti inilah
Entity
tampilannya:Komponen
Komponen "bodoh" karena mereka tidak melakukan atau tahu apa - apa. Mereka tidak memiliki referensi ke komponen lain, dan mereka biasanya tidak memiliki fungsi (saya bekerja di C #, jadi saya menggunakan properti untuk menangani getter / setter - jika mereka memiliki fungsi, mereka didasarkan pada pengambilan data yang mereka pegang).
Sistem
Sistem kurang "bodoh", tetapi masih merupakan robot bodoh. Mereka tidak memiliki konteks sistem keseluruhan, tidak memiliki referensi ke sistem lain dan tidak memiliki data kecuali untuk beberapa buffer yang mereka mungkin perlu melakukan pemrosesan masing-masing. Bergantung pada sistemnya, ia mungkin memiliki spesialisasi
Update
, atauDraw
metode, atau dalam beberapa kasus, keduanya.Antarmuka
Antarmuka adalah struktur utama dalam sistem saya. Mereka digunakan untuk mendefinisikan apa yang
System
bisa diproses, dan apa yangEntity
mampu dilakukan. Antarmuka yang relevan untuk rendering adalah:IRenderable
danIAnimatable
.Antarmuka hanya memberitahu sistem komponen mana yang tersedia. Misalnya, sistem rendering perlu mengetahui kotak pembatas entitas dan gambar yang akan digambar. Dalam kasus saya, itu akan menjadi
SpatialComponent
danImageComponent
. Jadi sepertinya ini:Sistem Rendering
Jadi bagaimana sistem rendering menarik entitas? Ini sebenarnya cukup sederhana, jadi saya hanya akan menunjukkan kepada Anda kelas telanjang untuk memberi Anda ide:
Melihat kelas, sistem render bahkan tidak tahu apa
Entity
itu. Yang ia tahu hanyalahIRenderable
dan hanya diberi daftar mereka untuk menggambar.Bagaimana Ini Semua Bekerja
Mungkin juga membantu untuk memahami bagaimana saya membuat objek game baru dan bagaimana saya memasukkannya ke sistem.
Menciptakan Entitas
Semua objek game mewarisi dari Entity, dan semua antarmuka yang berlaku yang menjelaskan apa yang bisa dilakukan objek game itu. Semua yang dianimasikan di layar terlihat seperti ini:
Memberi Makan Sistem
Saya menyimpan daftar semua entitas yang ada di dunia game dalam satu daftar bernama
List<Entity> gameObjects
. Setiap frame, saya kemudian menyaring daftar itu dan menyalin referensi objek ke lebih banyak daftar berdasarkan jenis antarmuka, sepertiList<IRenderable> renderableObjects
, danList<IAnimatable> animatableObjects
. Dengan cara ini, jika sistem yang berbeda perlu memproses entitas yang sama, mereka bisa. Kemudian saya hanya menyerahkan daftar itu ke masing-masing sistemUpdate
atauDraw
metode dan membiarkan sistem melakukan pekerjaan mereka.Animasi
Anda mungkin ingin tahu bagaimana sistem animasi bekerja. Dalam kasus saya, Anda mungkin ingin melihat antarmuka IAnimatable:
Hal utama yang perlu diperhatikan di sini adalah
ImageComponent
aspekIAnimatable
antarmuka tidak hanya baca; ia memiliki setter .Seperti yang sudah Anda duga, komponen animasi hanya menyimpan data tentang animasi; daftar frame (yang merupakan komponen gambar), frame saat ini, jumlah frame per detik yang akan diambil, waktu yang berlalu sejak peningkatan frame terakhir, dan opsi lainnya.
Sistem animasi mengambil keuntungan dari sistem rendering dan hubungan komponen gambar. Itu hanya mengubah komponen gambar entitas karena ia menambah bingkai animasi. Dengan begitu, animasi diberikan secara tidak langsung oleh sistem rendering.
sumber
Lihat jawaban ini untuk melihat jenis sistem yang saya bicarakan.
Komponen harus berisi detail untuk apa yang akan menggambar dan cara menggambarnya. Sistem rendering akan mengambil detail tersebut dan menggambar entitas dengan cara yang ditentukan oleh komponen. Hanya jika Anda menggunakan teknologi gambar yang sangat berbeda maka Anda memiliki komponen terpisah untuk gaya yang berbeda.
sumber
Alasan utama untuk memisahkan logika menjadi komponen adalah untuk membuat satu set data yang bila digabungkan dalam suatu entitas mereka menghasilkan perilaku yang berguna dan dapat digunakan kembali. Misalnya memisahkan Entitas ke dalam PhysicsComponent dan RenderComponent masuk akal karena kemungkinan tidak semua entitas memiliki Physics dan beberapa entitas mungkin tidak memiliki Sprite.
Untuk menjawab pertanyaan Anda, Anda perlu melihat arsitektur Anda dan bertanya pada diri sendiri dua pertanyaan:
Ketika membagi komponen, penting untuk menanyakan pertanyaan ini, jika jawaban untuk 1. adalah ya, maka Anda mungkin memiliki kandidat yang baik untuk membuat dua komponen terpisah, satu dengan Shader dan satu dengan Texture. Jawaban 2. biasanya ya untuk komponen seperti Posisi di mana banyak komponen dapat menggunakan posisi.
Misalnya, Fisika dan Audio mungkin menggunakan posisi yang sama, daripada kedua komponen menyimpan posisi duplikat Anda refactor mereka menjadi satu PositionComponent dan mengharuskan entitas yang menggunakan PhysicsComponent / AudioComponent juga memiliki PositionComponent.
Berdasarkan informasi yang Anda berikan kepada kami, sepertinya RenderComponent Anda tidak cocok untuk dipecah menjadi TextureComponent dan ShaderComponent karena shader sepenuhnya bergantung pada Texture dan tidak ada yang lain.
Dengan asumsi Anda menggunakan sesuatu yang mirip dengan T-Machine: Entity Systems contoh implementasi dari RenderComponent & RenderSystem di C ++ akan terlihat seperti ini:
sumber
Jebakan # 1: kode overdesigned. Pikirkan apakah Anda benar-benar membutuhkan setiap hal yang Anda terapkan karena Anda harus hidup dengannya untuk beberapa waktu.
Lubang # 2: terlalu banyak objek. Saya tidak akan menggunakan sistem dengan objek terlalu banyak (satu untuk setiap jenis, subtipe dan apa pun) karena hanya membuat pemrosesan otomatis lebih sulit. Menurut pendapat saya, itu jauh lebih baik untuk memiliki setiap objek mengontrol set fitur tertentu (sebagai lawan dari satu fitur). Misalnya, membuat komponen untuk setiap bit data yang termasuk dalam rendering (komponen tekstur, komponen shader) terlalu dibagi - Anda biasanya tetap harus memiliki semua komponen itu bersama-sama, bukankah Anda setuju?
Kesalahan # 3: kontrol eksternal yang terlalu ketat. Lebih suka mengubah nama menjadi objek shader / tekstur karena objek dapat berubah dengan renderer / tipe tekstur / format shader / apa pun. Nama-nama adalah pengidentifikasi sederhana - tergantung pada penyaji untuk memutuskan apa yang dihasilkan dari itu. Suatu hari Anda mungkin ingin memiliki bahan alih-alih shader polos (tambahkan shader, tekstur dan mode campuran dari data, misalnya). Dengan antarmuka berbasis teks, lebih mudah untuk mengimplementasikannya.
Adapun renderer, itu bisa berupa antarmuka sederhana yang membuat / menghancurkan / memelihara / membuat objek yang dibuat oleh komponen. Representasi paling primitif dari itu bisa menjadi seperti ini:
Ini akan memungkinkan Anda untuk mengelola objek-objek ini dari komponen Anda dan menjaganya cukup jauh untuk memungkinkan Anda membuat mereka dengan cara apa pun yang Anda inginkan.
sumber