Komposisi OOP berat vs sistem komponen entitas murni? [Tutup]

13

Saya akui, saya telah berbuat dosa karena terlalu banyak menggunakan, dan bahkan menyalahgunakan warisan. Proyek game (teks) pertama yang saya buat ketika saya mengambil kursus OOP saya berjalan sejauh "Locked door" dan "unlocked door" dari "Door" dan "Room with one door", "Room with two door", dan seterusnya dari "Kamar".

Dengan game (grafis) yang saya garap baru-baru ini, saya pikir saya telah mempelajari pelajaran saya dan membatasi penggunaan warisan. Namun saya perhatikan masalah segera mulai muncul. Kelas root saya mulai membengkak semakin banyak, dan kelas daun saya penuh dengan kode duplikat.

Saya pikir saya masih melakukan hal-hal yang salah, dan setelah mencari secara online saya menemukan bahwa saya bukan satu-satunya yang memiliki masalah ini. Begitulah akhirnya saya menemukan sistem Entity setelah beberapa penelitian menyeluruh (baca: googlefu)

Ketika saya mulai membacanya, saya bisa melihat betapa jelasnya ia bisa menyelesaikan masalah yang saya alami dengan hierarki OOP tradisional dengan komponen. Namun ini ada dalam bacaan pertama. Ketika saya menemukan lebih banyak ... pendekatan "radikal" ES, seperti yang ada di T-machine .

Saya mulai tidak setuju dengan metode yang mereka gunakan. Sistem komponen murni tampak berlebihan, atau lebih tepatnya, tidak intuitif, yang mungkin merupakan kekuatan OOP. Penulis melangkah lebih jauh dengan mengatakan bahwa sistem ES adalah kebalikan dari OOP, dan sementara itu dapat digunakan di sepanjang OOP, itu sebenarnya tidak seharusnya. Saya tidak mengatakan itu salah, tapi saya tidak merasa seperti solusi yang ingin saya terapkan.

Jadi bagi saya, dan untuk menyelesaikan masalah yang saya alami di awal posting, tanpa bertentangan dengan intuisi saya, adalah masih menggunakan hierarki, namun itu tidak akan menjadi hierarki monolitik seperti yang saya gunakan sebelumnya, melainkan sebuah polylithic (saya tidak dapat menemukan kata yang berlawanan dengan monolitik), yang terdiri dari beberapa pohon yang lebih kecil.

Contoh berikut menunjukkan apa yang saya maksud (ini terinspirasi oleh contoh yang saya temukan di Arsitektur Mesin Game, Bab 14).

Saya akan memiliki pohon kecil untuk kendaraan. Kelas root kendaraan akan memiliki komponen rendering, komponen tabrakan, komponen posisi dll.

Kemudian sebuah tangki, subkelas kendaraan akan mewarisi komponen-komponen itu darinya, dan diberi komponen "meriam" itu sendiri.

Hal yang sama berlaku untuk Karakter. Karakter akan memiliki komponennya sendiri, maka Kelas Pemain akan mewarisinya, dan diberi pengontrol Input, sedangkan kelas musuh lainnya akan mewarisi dari kelas Karakter dan diberi pengontrol AI.

Saya tidak benar-benar melihat masalah dengan desain ini. Meskipun tidak menggunakan Sistem Pengendali Entitas murni, masalah dengan efek menggelegak, dan kelas akar besar diselesaikan dengan menggunakan hierarki multi-pohon, dan masalah berat, daun duplikasi kode hilang karena daun tidak memiliki kode untuk memulai, hanya komponen. Jika perubahan perlu dilakukan ke level daun, maka itu semudah mengubah satu komponen, daripada menyalin paste kode di mana-mana.

Tentu saja, karena tidak berpengalaman seperti saya, saya tidak melihat masalah ketika saya mulai menggunakan hirarki tunggal, model berat warisan, jadi jika ada masalah dengan model yang saat ini saya pikirkan untuk diterapkan, saya tidak akan dapat melihatnya.

Pendapat Anda?

PS: Saya menggunakan Java, jadi menggunakan multiple inheritance untuk mengimplementasikan ini daripada menggunakan komponen normal tidak mungkin.

PPS: Komunikasi antar komponen akan dilakukan dengan menghubungkan komponen-komponen yang saling bergantung satu sama lain. Ini akan mengarah pada penggandengan, tapi saya pikir ini merupakan trade off yang baik.

Midori Ryuu
sumber
2
Ini semua tampak baik bagi saya. Apakah ada contoh spesifik dalam hierarki atau sistem entitas Anda yang "berbau" bagi Anda? Pada akhirnya, ini semua tentang menyelesaikan permainan lebih dari sekadar rasa kemurnian.
drhayes
Saya tidak, tetapi seperti yang saya katakan, saya hampir tidak memiliki pengalaman dan saya jauh dari hakim terbaik untuk ini.
Midori Ryuu
3
Saya memilih untuk menutup pertanyaan ini karena bersifat subjektif dan terbuka untuk diskusi yang luas. Saya menghargai itu diminta dari posisi yang tulus - tetapi memilih bagaimana melanjutkan di sini sepenuhnya masalah pendapat pribadi. Satu-satunya downside nyata dari memperbaiki komponen Anda menjadi subclass adalah bahwa pengaturan komponen tidak dapat diubah saat runtime - tetapi itu pasti harus jelas bagi Anda dan itu adalah pilihan yang Anda buat.
Kylotan
4
Saya memilih untuk menutup juga. Ini pertanyaan yang bagus dan ditulis dengan baik, tetapi tidak sesuai untuk GDSE. Anda dapat mencoba memposting ulang ini di GameDev.net.
Sean Middleditch
Seperti yang tertulis, dengan pertanyaan 'Pendapat Anda?', Pertanyaan itu memang terbuka dan tidak dapat dijawab. Namun, itu bisa dijawab jika Anda menjawabnya seolah-olah pertanyaannya adalah 'Apakah ada jebakan menganga yang bisa saya jatuhkan tanpa menyadarinya?' (Setidaknya, jika Anda berpikir bahwa sebenarnya ada perbedaan empiris antara berbagai arsitektur.)
John Calsbeek

Jawaban:

8

Pertimbangkan contoh ini:

Anda sedang membuat RTS. Dalam logika lengkap, Anda memutuskan untuk membuat kelas dasar GameObject, dan kemudian dua subclass, Buildingdan Unit. Ini berfungsi dengan baik, tentu saja, dan Anda berakhir dengan sesuatu yang terlihat seperti ini:

  • GameObject
    • ModelComponent
    • CollisionComponent
    • ...
  • Building
    • ProductionComponent
    • ...
  • Unit
    • AimingAIComponent
    • WeaponComponent
    • ParticleEmitterComponent
    • AnimationComponent
    • ...

Sekarang setiap subkelas yang UnitAnda buat sudah memiliki semua komponen yang Anda butuhkan. Dan sebagai bonus, Anda memiliki satu tempat untuk meletakkan semua kode pengaturan membosankan yang news sebuah WeaponComponent, menghubungkan ke sebuah AimingAIComponentdan ParticleEmitterComponent-indah!

Dan tentu saja, karena masih memasukkan logika ke dalam komponen, ketika Anda akhirnya memutuskan bahwa Anda ingin menambahkan Towerkelas yang merupakan bangunan tetapi memiliki senjata, Anda dapat mengelolanya. Anda dapat menambahkan AimingAIComponent, a WeaponComponent, dan ParticleEmitterComponentke subkelas Anda dari Building. Tentu saja, maka Anda harus melalui dan menggali kode inisialisasi dari Unitkelas dan meletakkannya di tempat lain, tetapi itu bukan kerugian besar.

Dan sekarang, tergantung pada berapa banyak tinjauan ke depan yang Anda miliki, Anda mungkin atau mungkin tidak berakhir dengan bug halus. Ternyata mungkin ada beberapa kode di tempat lain dalam game yang terlihat seperti ini:

if (myGameObject instanceof Unit)
    doSomething(((Unit)myGameObject).getWeaponComponent());

Itu diam-diam tidak bekerja untuk Towerbangunan Anda , meskipun Anda benar-benar ingin itu bekerja untuk apa pun dengan senjata. Sekarang harus terlihat seperti ini:

WeaponComponent weapon = myGameObject.getComponent(WeaponComponent.class);
if (weapon)
    doSomething(weapon);

Jauh lebih baik! Terjadi pada Anda setelah mengubah ini bahwa salah satu metode accessor yang Anda tulis dapat berakhir dalam situasi ini. Jadi hal paling aman untuk dilakukan adalah menghapus semuanya, dan mengakses setiap komponen melalui getComponentmetode yang satu ini , yang dapat bekerja dengan subkelas apa pun. (Sebagai alternatif, Anda dapat menambahkan setiap jenis tunggal get*Component()ke superclass GameObjectdan menimpanya dalam subkelas untuk mengembalikan komponen. Namun, saya akan menganggap yang pertama.)

Sekarang kelas Buildingdan Anda Unitcukup banyak kantong komponen, bahkan tanpa aksesor pada mereka. Dan tidak ada inisialisasi yang bagus juga, karena sebagian besar dari itu perlu diseret untuk menghindari duplikasi kode dengan satu objek khusus lainnya di suatu tempat.

Jika Anda mengikuti ini secara ekstrem, Anda selalu membuat subclass Anda benar-benar tas komponen lembam, pada titik mana bahkan tidak ada titik memiliki subclass di sekitar. Anda dapat memperoleh hampir semua manfaat memiliki hierarki kelas dengan hierarki metode yang menciptakan komponen yang tepat. Alih-alih memiliki Unitkelas, Anda memiliki addUnitComponentsmetode yang membuat AnimationComponentdan juga panggilan addWeaponComponents, yang membuat AimingAIComponent, sebuah WeaponComponent, dan ParticleEmitterComponent. Hasilnya adalah bahwa Anda memiliki semua manfaat dari sistem entitas, semua penggunaan kembali kode hierarki, dan tidak ada godaan untuk memeriksa instanceofatau melemparkan ke tipe kelas Anda.

John Calsbeek
sumber
Kata baik. Menambah ini saya pikir solusi ideal adalah menginisialisasi setiap entitas dengan skrip atau dengan file descriptor. Saya menggunakan file teks untuk inisialisasi entitas. Cepat dan memungkinkan non programmer untuk menguji parameter yang berbeda.
Coyote
Wow membaca posting Anda memberi saya mimpi buruk! Tentu saja, maksudku, dengan cara yang baik, melihat saat kau membuka mataku untuk mimpi buruk macam apa yang akan kudapat! Saya berharap saya bisa membalas lebih banyak untuk jawaban yang begitu terperinci, tetapi tidak banyak yang bisa ditambahkan!
Midori Ryuu
Pada cara yang lebih sederhana. Anda akhirnya menggunakan Waris sebagai pola pabrik pria miskin.
Zardoz89
2

Komposisi atas Warisan adalah terminologi OOP (setidaknya cara saya mempelajarinya / diajarkan). Untuk memilih antara komposisi dan warisan Anda mengatakan dengan suara keras "Mobil adalah jenis roda" (warisan) dan "Mobil memiliki roda" (komposisi). Satu harus terdengar lebih benar daripada yang lain (komposisi dalam hal ini), dan jika Anda tidak dapat memutuskan, default komposisi sampai Anda memutuskan sebaliknya.

Daniel Carlsson
sumber
1

Ketika datang untuk mengintegrasikan sistem berbasis komponen ke permainan yang ada berdasarkan objek permainan ada artikel di pemrograman koboi http://cowboyprogramming.com/2007/01/05/evolve-your-heirachy/ yang mungkin memberi Anda beberapa petunjuk tentang cara transisi dapat dilakukan.

Mengenai masalah, model Anda masih harus menangani pemain secara berbeda dari musuh lain. Anda tidak akan dapat beralih status (yaitu pemain menjadi dikendalikan AI) ketika pemain memutus atau dalam kasus khusus tanpa memegangnya secara berbeda dari karakter lain.

Terlepas dari penggunaan kembali komponen yang banyak di seluruh entitas yang berbeda, gagasan permainan berbasis komponen adalah keseragaman dari keseluruhan sistem. Setiap entitas memiliki komponen yang dapat dilampirkan dan dilepas sesuka hati. Semua komponen dari jenis yang sama ditangani dengan cara yang persis sama dengan satu set panggilan yang dibuat oleh antarmuka (komponen dasar) dari masing-masing jenis (posisi, render, skrip ...)

Memadukan pewarisan objek game dengan pendekatan berbasis komponen memberi Anda beberapa keuntungan dari pendekatan berbasis komponen dengan kelemahan dari kedua pendekatan tersebut.

Ini bisa bekerja untuk Anda. Tetapi saya tidak akan mencampur keduanya di luar periode transisi dari satu arsitektur ke arsitektur lainnya.

PS ditulis di telepon jadi saya kesulitan menghubungkan ke sumber daya eksternal.

Anjing hutan
sumber
Terima kasih atas balasan Anda meskipun Anda sedang menelepon! Saya mengerti maksud Anda. Hal-hal berbasis komponen pemindahan adalah, semakin banyak fleksibilitas yang saya harus ubah. Itulah alasan mengapa saya akan terus maju dan mencoba pendekatan komposisi dengan pewarisan minimal. (Bahkan lebih dari yang saya usulkan.)
Midori Ryuu
@MidoriRyuu menghindari semua warisan di objek entitas. Anda cukup beruntung menggunakan bahasa dengan refleksi (dan introspeksi) bawaan. Anda dapat menggunakan file (txt atau XML) untuk menginisialisasi objek Anda dengan parameter yang tepat. Ini tidak terlalu banyak pekerjaan dan itu akan memungkinkan untuk fine tuning permainan yang ditingkatkan tanpa kompilasi ulang. Tetapi pertahankan kelas Entity, setidaknya untuk komunikasi antar-komponen dan tugas utilitas lainnya. PS masih di telepon :(
Coyote