Mengapa ide buruk untuk menyimpan metode dalam Entitas dan Komponen? (Bersamaan dengan beberapa pertanyaan Sistem Entitas lainnya.)


16

Ini adalah tindak lanjut dari pertanyaan ini , yang saya jawab, tetapi yang satu ini membahas topik yang jauh lebih spesifik.

Jawaban ini membantu saya memahami Sistem Entitas lebih baik daripada artikel.

Saya telah membaca (ya,) artikel tentang Sistem Entitas, dan itu memberi tahu saya hal berikut:

Entitas hanyalah id dan array komponen (artikel mengatakan bahwa menyimpan entitas dalam komponen bukanlah cara yang baik untuk melakukan sesuatu, tetapi tidak memberikan alternatif).
Komponen adalah potongan data, yang menunjukkan apa yang dapat dilakukan dengan entitas tertentu.
Sistem adalah "metode", mereka melakukan manipulasi data pada entitas.

Ini tampaknya sangat praktis dalam banyak situasi, tetapi bagian tentang komponen yang hanya kelas data mengganggu saya. Sebagai contoh, bagaimana saya bisa mengimplementasikan kelas Vector2D (Posisi) saya di Sistem Entitas?

Kelas Vector2D menyimpan data: koordinat x dan y, tetapi juga memiliki metode , yang sangat penting untuk kegunaannya dan membedakan kelas dari hanya dua elemen array. Metode contoh adalah: add(), rotate(point, r, angle), substract(), normalize(), dan semua standar lainnya, metode yang berguna, dan benar-benar diperlukan bahwa posisi (yang merupakan contoh dari kelas Vector2D) harus memiliki.

Jika komponen itu hanya pemegang data, itu tidak akan dapat memiliki metode ini!

Salah satu solusi yang mungkin muncul adalah dengan mengimplementasikannya di dalam sistem, tetapi itu tampaknya sangat kontra-intuitif. Metode-metode ini adalah hal-hal yang ingin saya lakukan sekarang , membuatnya lengkap dan siap digunakan. Saya tidak ingin menunggu MovementSystemsampai membaca beberapa set pesan mahal yang menginstruksikannya untuk melakukan perhitungan pada posisi suatu entitas!

Dan, artikel itu dengan sangat jelas menyatakan bahwa hanya sistem yang harus memiliki fungsionalitas apa pun , dan satu-satunya penjelasan untuk itu, yang dapat saya temukan, adalah "untuk menghindari OOP". Pertama-tama, saya tidak mengerti mengapa saya harus menahan diri dari menggunakan metode dalam entitas dan komponen. Memori overhead praktis sama, dan ketika digabungkan dengan sistem ini harus sangat mudah diimplementasikan dan digabungkan dengan cara yang menarik. Sistem, misalnya, hanya bisa memberikan logika dasar untuk entitas / komponen, yang mengetahui implementasinya sendiri. Jika Anda bertanya kepada saya - ini pada dasarnya mengambil barang dari ES dan OOP, sesuatu yang tidak dapat dilakukan menurut penulis artikel, tetapi bagi saya sepertinya praktik yang baik.

Pikirkan seperti ini; ada banyak jenis objek yang dapat digambar dalam game. Gambar lama yang polos, animasi ( update(),, getCurrentFrame()dll), kombinasi dari tipe primitif ini, dan semuanya dapat dengan mudah menyediakan draw()metode untuk sistem render, yang kemudian tidak perlu peduli tentang bagaimana sprite dari suatu entitas diimplementasikan, hanya saja tentang antarmuka (menggambar) dan posisinya. Dan kemudian, saya hanya akan membutuhkan sistem animasi yang akan memanggil metode khusus animasi yang tidak ada hubungannya dengan rendering.

Dan satu hal lagi ... Apakah benar-benar ada alternatif untuk array ketika datang untuk menyimpan komponen? Saya tidak melihat tempat lain untuk menyimpan komponen selain array di dalam kelas Entity ...

Mungkin, ini adalah pendekatan yang lebih baik: menyimpan komponen sebagai properti entitas yang sederhana. Misalnya, komponen posisi akan dilem entity.position.

Satu- satunya cara lain adalah dengan memiliki semacam tabel pencarian yang aneh di dalam sistem, yang mereferensikan entitas yang berbeda. Tapi itu tampaknya sangat tidak efisien dan lebih rumit untuk dikembangkan daripada sekadar menyimpan komponen dalam entitas.


Alexandre apakah Anda hanya melakukan banyak pengeditan untuk mendapatkan lencana lainnya? Karena itu nakal, itu terus menabrak satu ton benang kuno.
jhocking

Jawaban:


25

Saya pikir itu benar-benar baik untuk memiliki metode sederhana untuk mengakses, memperbarui atau memanipulasi data dalam komponen. Saya pikir fungsionalitas yang harus di luar komponen adalah fungsionalitas logis. Fungsi utilitas baik-baik saja. Ingat, sistem entitas-komponen hanyalah panduan, bukan aturan ketat yang perlu Anda ikuti. Jangan keluar dari jalan Anda untuk mengikuti mereka. Jika Anda pikir itu lebih masuk akal untuk melakukannya satu arah, maka lakukanlah seperti itu :)

EDIT

Untuk memperjelas, tujuan Anda bukan untuk menghindari OOP . Itu akan sangat sulit di sebagian besar bahasa umum yang digunakan hari ini. Anda mencoba meminimalkan warisan , yang merupakan aspek besar dari OOP, tetapi tidak diharuskan. Anda ingin menyingkirkan Object-> MobileObject-> Creature-> Bipedal-> Human type inheritance.

Namun, tidak masalah memiliki warisan! Anda berhadapan dengan bahasa yang sangat dipengaruhi oleh warisan, sangat sulit untuk tidak menggunakannya. Misalnya, Anda dapat memiliki Componentkelas atau antarmuka yang diperluas atau diterapkan oleh semua komponen Anda yang lain. Kesepakatan yang sama dengan Systemkelas Anda . Ini membuat banyak hal lebih mudah. Saya sangat menyarankan Anda melihat kerangka Artemis . Ini open source dan memiliki beberapa proyek contoh. Buka hal-hal itu dan lihat cara kerjanya.

Untuk Artemis, entitas disimpan dalam array, sederhana. Namun, komponennya disimpan dalam array atau array (terpisah dari entitas). Array level atas mengelompokkan array level bawah berdasarkan tipe komponen. Jadi setiap tipe komponen memiliki susunannya sendiri. Array level bawah diindeks oleh entitas ID. (Sekarang saya tidak yakin apakah saya akan melakukannya dengan cara itu, tetapi itulah yang dilakukan di sini). Artemis menggunakan kembali ID entitas, sehingga ID entitas maks tidak menjadi lebih besar dari jumlah entitas Anda saat ini, tetapi Anda masih dapat memiliki array jarang jika komponen tersebut bukan komponen yang sering digunakan. Lagi pula, saya tidak akan memilih terlalu banyak. Metode ini untuk menyimpan entitas dan komponennya tampaknya berfungsi. Saya pikir ini akan menjadi langkah pertama yang bagus untuk mengimplementasikan sistem Anda sendiri.

Entitas dan komponen disimpan dalam manajer yang terpisah.

Strategi yang Anda sebutkan, membuat entitas menyimpan komponen mereka sendiri ( entity.position), agak bertentangan dengan tema komponen entitas, tetapi itu benar-benar dapat diterima jika Anda merasa yang paling masuk akal.


1
Hmm, itu sangat menyederhanakan situasi, terima kasih! Kupikir ada keajaiban "kau akan menyesal nanti", dan aku tidak bisa melihatnya!
jcora

1
Na, saya benar-benar menggunakannya dalam sistem komponen entitas saya. Saya bahkan memiliki beberapa komponen yang mewarisi dari orang tua biasa, terkesiap . Saya pikir satu-satunya penyesalan yang akan Anda lakukan adalah jika Anda berusaha untuk tidak menggunakan metode seperti itu. Ini semua tentang melakukan apa yang paling masuk akal bagi Anda. Jika masuk akal untuk menggunakan warisan atau memasukkan beberapa metode dalam suatu komponen, lakukanlah.
MichaelHouse

2
Saya telah belajar dari jawaban terakhir saya tentang hal ini. Disclaimer: Saya tidak mengatakan ini adalah yang cara untuk melakukannya. :)
MichaelHouse

1
Ya, saya tahu betapa sulitnya mempelajari paradigma baru. Untungnya, Anda dapat menggunakan aspek-aspek paradigma lama untuk membuat segalanya lebih mudah! Saya telah memperbarui jawaban saya dengan info penyimpanan. Jika Anda melihat Artemis, periksa EntityManagerdi situlah barang disimpan.
MichaelHouse

1
Bagus! Itu akan menjadi mesin yang cukup manis ketika dilakukan. Semoga berhasil! Terima kasih telah mengajukan pertanyaan menarik.
MichaelHouse

10

Artikel 'itu' bukan yang saya setujui secara khusus, jadi saya kira jawaban saya agak kritis.

Ini tampaknya sangat praktis dalam banyak situasi, tetapi bagian tentang komponen yang hanya kelas data mengganggu saya. Sebagai contoh, bagaimana saya bisa mengimplementasikan kelas Vector2D (Posisi) saya di Sistem Entitas?

Idenya bukan untuk memastikan bahwa tidak ada dalam program Anda selain ID entitas, komponen, atau sistem - itu untuk memastikan bahwa data entitas dan perilaku dibuat melalui penulisan objek daripada menggunakan pohon warisan yang kompleks atau lebih buruk lagi mencoba untuk letakkan semua fungsionalitas yang mungkin menjadi satu objek. Untuk mengimplementasikan komponen dan sistem ini, Anda tentu akan memiliki data normal seperti vektor yang, dalam sebagian besar bahasa, direpresentasikan sebagai kelas.

Abaikan sedikit dalam artikel yang menunjukkan ini bukan OOP - itu hanya sebagai OOP seperti pendekatan lainnya. Ketika kebanyakan kompiler atau runtime bahasa mengimplementasikan metode objek, pada dasarnya sama seperti fungsi lainnya, kecuali ada argumen tersembunyi yang disebut thisatau self, yang merupakan penunjuk ke tempat di memori tempat data objek disimpan. Dalam sistem berbasis komponen, ID entitas dapat digunakan untuk menemukan di mana komponen yang relevan (dan dengan demikian data) untuk entitas yang diberikan. Dengan demikian ID entitas setara dengan pointer / self ini, dan konsep-konsep pada dasarnya adalah hal yang sama, hanya disusun ulang sedikit.

Dan, artikel itu dengan sangat jelas menyatakan bahwa hanya sistem yang harus memiliki fungsionalitas apa pun, dan satu-satunya penjelasan untuk itu, yang dapat saya temukan, adalah "untuk menghindari OOP". Pertama-tama, saya tidak mengerti mengapa saya harus menahan diri dari menggunakan metode dalam entitas dan komponen.

Baik. Metode adalah cara yang efektif untuk mengatur kode Anda. Hal penting untuk diambil dari gagasan "avoid OOP" adalah untuk menghindari penggunaan warisan di mana-mana untuk memperluas fungsionalitas. Sebagai gantinya, pisahkan fungsionalitas menjadi komponen yang dapat digabungkan untuk melakukan hal yang sama.

Pikirkan seperti ini; ada banyak jenis objek yang dapat digambar dalam game. Gambar lama polos, animasi (pembaruan (), getCurrentFrame (), dll), kombinasi dari tipe primitif ini, dan semuanya dapat dengan mudah menyediakan metode draw () ke sistem render [...]

Gagasan sistem berbasis komponen adalah bahwa Anda tidak akan memiliki kelas terpisah untuk ini, tetapi akan memiliki kelas Obyek / Entitas tunggal, dan gambar akan menjadi Obyek / Entitas yang memiliki ImageRenderer, Animasi akan menjadi Obyek / Entity yang memiliki AnimationRenderer, dll. Sistem yang relevan akan tahu cara merender komponen ini sehingga tidak perlu ada kelas dasar dengan metode Draw ().

[...] yang kemudian tidak perlu peduli tentang bagaimana sprite suatu entitas diimplementasikan, hanya tentang antarmuka (draw) dan posisinya. Dan kemudian, saya hanya akan membutuhkan sistem animasi yang akan memanggil metode khusus animasi yang tidak ada hubungannya dengan rendering.

Tentu, tetapi ini tidak berhasil dengan komponen. Anda memiliki 3 pilihan:

  • Setiap komponen mengimplementasikan antarmuka ini dan memiliki metode Draw (), bahkan jika tidak ada yang ditarik. Jika Anda melakukan ini untuk setiap bit fungsionalitas maka komponen akan terlihat sangat jelek.
  • Hanya komponen yang memiliki sesuatu untuk menggambar, mengimplementasikan antarmuka - tetapi siapa yang memutuskan komponen mana untuk memanggil Draw ()? Apakah suatu sistem harus entah bagaimana meminta setiap komponen untuk melihat antarmuka apa yang didukung? Itu akan rawan kesalahan dan berpotensi sulit untuk diterapkan dalam beberapa bahasa.
  • Komponen hanya ditangani oleh sistem yang dimilikinya (yang merupakan gagasan dalam artikel tertaut). Dalam hal ini, antarmuka tidak relevan karena suatu sistem tahu persis kelas atau tipe objek apa yang bekerja dengannya.

Dan satu hal lagi ... Apakah benar-benar ada alternatif untuk array ketika datang untuk menyimpan komponen? Saya tidak melihat tempat lain untuk menyimpan komponen selain array di dalam kelas Entity ...

Anda dapat menyimpan komponen dalam sistem. Array bukan masalah, tetapi di mana Anda menyimpan komponen.


+1 Terima kasih atas sudut pandang lain. Adalah baik untuk mendapatkan beberapa ketika berhadapan dengan subjek yang ambigu! Jika Anda menyimpan komponen dalam sistem, apakah itu berarti bahwa komponen hanya dapat dimodifikasi oleh satu sistem? Misalnya, sistem gambar dan sistem pergerakan akan mengakses komponen posisi. Di mana Anda menyimpannya?
MichaelHouse

Yah mereka hanya akan menyimpan pointer ke komponen-komponen itu, yang bisa, selama saya khawatir berada di suatu tempat ... Juga, mengapa Anda menyimpan komponen dalam sistem? Apakah ada manfaatnya?
jcora

Apakah saya benar, @Kylotan? Begitulah cara saya melakukannya, sepertinya masuk akal ...
jcora

Dalam contoh Adam / T-Machine, tujuannya adalah bahwa ada 1 sistem per komponen, tetapi sistem tentu saja dapat mengakses dan memodifikasi komponen lainnya. (Ini menghambat manfaat multithreading komponen, tapi itu masalah yang berbeda.)
Kylotan

1
Menyimpan komponen dalam sistem memungkinkan lokalitas referensi yang lebih baik untuk sistem itu - sistem itu hanya (umumnya) bekerja dengan data itu, jadi mengapa pergi ke seluruh memori komputer Anda dari entitas ke entitas untuk mendapatkannya? Ini juga membantu dengan konkurensi karena Anda bisa meletakkan seluruh sistem dan data pada satu inti atau prosesor (atau bahkan komputer terpisah, dalam MMO). Sekali lagi, manfaat ini berkurang ketika 1 sistem mengakses lebih dari 1 jenis komponen, sehingga harus diperhitungkan ketika memutuskan di mana harus membagi tanggung jawab komponen / sistem.
Kylotan

2

Vektor adalah data. Fungsi-fungsinya lebih seperti fungsi utilitas - mereka tidak spesifik untuk instance data, mereka dapat diterapkan ke semua vektor secara independen. Cara berpikir yang baik adalah: Dapatkah fungsi-fungsi ini ditulis ulang sebagai metode statis? Jika demikian, itu hanya utilitas.


Saya tahu itu, tetapi masalahnya adalah metode memanggil lebih cepat dan dapat dilakukan di tempat oleh suatu sistem, atau apa pun yang mungkin perlu memanipulasi posisi entitas. Saya menjelaskan bahwa, lihatlah, juga, pertanyaannya memiliki lebih dari itu, saya percaya.
jcora
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.