Bagaimana komponen fisika atau grafik biasanya dibangun dalam sistem berorientasi komponen?

19

Saya telah menghabiskan 48 jam terakhir membaca tentang sistem Komponen Objek, dan merasa saya cukup siap untuk mulai mengimplementasikannya. Saya mendapat kelas Obyek dan Komponen dasar yang dibuat, tetapi sekarang saya harus mulai membuat komponen yang sebenarnya. Saya agak bingung. Ketika saya memikirkan mereka dalam hal HealthComponent atau sesuatu yang pada dasarnya hanya akan menjadi sebuah properti, itu masuk akal. Ketika itu adalah sesuatu yang lebih umum sebagai komponen Fisika / Grafik, saya agak bingung.

Kelas Object saya terlihat seperti ini sejauh ini (Jika Anda melihat ada perubahan yang harus saya buat, tolong beri tahu saya, masih baru dalam hal ini) ...

typedef unsigned int ID;

class GameObject
{
public:

    GameObject(ID id, Ogre::String name = "");
    ~GameObject();

    ID &getID();
    Ogre::String &getName();

    virtual void update() = 0;

    // Component Functions
    void addComponent(Component *component);
    void removeComponent(Ogre::String familyName);

    template<typename T>
    T* getComponent(Ogre::String familyName)
    {
        return dynamic_cast<T*>(m_components[familyName]);
    }

protected:

    // Properties
    ID m_ID;
    Ogre::String m_Name;
    float m_flVelocity;
    Ogre::Vector3 m_vecPosition;

    // Components
    std::map<std::string,Component*> m_components;
    std::map<std::string,Component*>::iterator m_componentItr;
};

Sekarang masalah yang saya hadapi adalah apa yang akan dimasukkan populasi umum ke dalam Komponen seperti Fisika / Grafik? Untuk Ogre (mesin render saya) Objek yang terlihat akan terdiri dari beberapa Ogre :: SceneNode (mungkin beberapa) untuk melampirkannya ke adegan, Ogre :: Entity (mungkin beberapa) untuk menampilkan jerat yang terlihat, dan sebagainya. Apakah lebih baik menambahkan beberapa GraphicComponent ke Object dan membiarkan setiap GraphicComponent menangani satu SceneNode / Entity atau apakah ide untuk memiliki satu dari setiap Komponen diperlukan?

Untuk Fisika saya bahkan lebih bingung. Saya kira mungkin membuat RigidBody dan melacak massa / interia / dll. akan masuk akal. Tetapi saya mengalami kesulitan memikirkan bagaimana sebenarnya menempatkan spesifik ke dalam Komponen.

Setelah saya menyelesaikan beberapa komponen "Diperlukan" ini, saya pikir itu akan lebih masuk akal. Sampai sekarang meskipun saya masih agak bingung.

Aidan Knight
sumber

Jawaban:

32

Pertama-tama, Anda ketika membangun sistem berbasis komponen, Anda tidak harus mengambil pendekatan untuk mengubah segala sesuatu menjadi komponen. Bahkan, Anda biasanya tidak boleh - itu adalah kesalahan orang baru. Dalam pengalaman saya, cara terbaik untuk menyatukan rendering dan sistem fisika dalam arsitektur berbasis komponen adalah membuat komponen-komponen itu sedikit lebih dari sekadar wadah bodoh untuk rendering nyata atau objek primitif fisika.

Ini memiliki keuntungan tambahan dari menjaga sistem rendering dan fisika dipisahkan dari sistem komponen.

Untuk sistem fisika, misalnya, simulator fisika umumnya menyimpan daftar besar benda-benda tubuh kaku yang dimanipulasi setiap kali kutu. Sistem komponen Anda tidak mengacaukannya - komponen fisika Anda hanya menyimpan referensi ke benda tegar yang mewakili aspek fisik objek milik komponen, dan menerjemahkan pesan apa pun dari sistem komponen menjadi manipulasi yang tepat dari benda tegar.

Demikian juga dengan rendering - renderer Anda akan memiliki sesuatu yang mewakili contoh data yang akan dirender, dan komponen visual Anda hanya berpegang pada referensi untuk itu.

Ini relatif mudah - untuk alasan apa pun tampaknya membingungkan orang, tetapi itu sering kali karena mereka memikirkannya terlalu keras. Tidak banyak keajaiban di sana. Dan karena itu yang paling penting untuk menyelesaikan sesuatu daripada menderita atas desain yang sempurna, Anda harus sangat mempertimbangkan pendekatan di mana setidaknya beberapa komponen telah mendefinisikan antarmuka dan merupakan anggota langsung dari objek game, seperti yang disarankan momboco. Itu hanya sebagai pendekatan yang valid dan cenderung lebih mudah untuk grok.

Komentar lain

Ini tidak terkait dengan pertanyaan langsung Anda, tetapi ini adalah hal-hal yang saya perhatikan ketika melihat kode Anda:

Mengapa Anda mencampurkan Ogre :: String dan std :: string dalam objek game Anda, yang seolah-olah merupakan objek non-grafis dan karenanya seharusnya tidak memiliki ketergantungan pada Ogre? Saya akan menyarankan menghapus semua referensi langsung ke Ogre kecuali dalam komponen rendering itu sendiri, di mana Anda harus melakukan transformasi.

update () adalah virtual murni, yang menyiratkan bahwa seseorang harus subkelas GameObject, yang sebenarnya agak kebalikan dari arah yang Anda inginkan dengan arsitektur komponen. Poin utama menggunakan komponen adalah untuk memilih agregasi fungsi daripada pewarisan.

Anda bisa mendapat manfaat dari beberapa penggunaan const(khususnya di mana Anda kembali dengan referensi). Juga saya tidak yakin bahwa Anda benar-benar ingin kecepatan menjadi skalar (atau jika itu, dan posisi, harus ada dalam objek game dalam jenis metodologi komponen yang terlalu umum yang tampaknya Anda tuju).

Pendekatan Anda menggunakan peta besar komponen dan update()panggilan dalam objek game cukup sub-optimal (dan perangkap umum bagi mereka yang pertama kali membangun sistem semacam ini). Itu membuat koherensi cache yang sangat buruk selama pembaruan dan tidak memungkinkan Anda untuk mengambil keuntungan dari konkurensi dan kecenderungan menuju proses gaya-SIMD dari kumpulan data besar atau perilaku sekaligus. Sering kali lebih baik menggunakan desain di mana objek game tidak memperbarui komponennya, tetapi subsistem yang bertanggung jawab atas komponen itu sendiri memperbarui semuanya sekaligus. Ini mungkin layak dibaca.


sumber
Jawaban ini sangat bagus. Saya masih belum menemukan cara untuk spasi paragraf dalam komentar (masukkan kunci hanya kirim) tetapi saya akan membahas tanggapan ini. Deskripsi Anda tentang sistem Objek / Komponen sangat masuk akal. Jadi hanya untuk memperjelas, dalam kasus PhysicsComponent, dalam konstruktor komponen, saya bisa memanggil fungsi CreateRigidBody () PhysicsManager saya, dan kemudian menyimpan referensi ke komponen tersebut?
Aidan Knight
Sejauh bagian "Komentar Lain", terima kasih untuk ini. Saya mencampur string karena saya biasanya lebih suka menggunakan semua fungsi Ogre sebagai dasar untuk proyek saya yang menggunakan Ogre, tapi saya mulai beralih di sistem Object / Component karena tidak memiliki peta / pair / etc. Anda benar dan saya telah mengkonversikan mereka ke std members untuk memisahkannya dari Ogre.
Aidan Knight
1
+1 Itulah jawaban waras pertama mengenai model berbasis komponen yang saya baca di sini untuk sementara waktu, kontras yang bagus dengan generalisasi
berlebihan
5
Ini memungkinkan koherensi yang lebih baik (baik data maupun dalam cache instruksi) dan memberi Anda kemungkinan untuk melakukan pemutakhiran yang hampir sepele pada thread yang berbeda atau proses pekerja (SPU, misalnya). Dalam beberapa kasus, komponen-komponen bahkan tidak perlu diperbarui, yang berarti Anda dapat menghindari overhead panggilan no-op ke metode pembaruan (). Juga membantu Anda membangun sistem berorientasi data - ini semua tentang pemrosesan massal, yang memanfaatkan tren modern dalam arsitektur CPU.
2
+1 untuk "paling penting untuk menyelesaikan sesuatu daripada menderita karena desain yang sempurna"
Trasplazio Garzuglio
5

Arsitektur komponen adalah alternatif untuk menghindari hierarki besar yang dicapai mewarisi semua elemen dari node yang sama, dan masalah kopling yang mengarah.

Anda menunjukkan arsitektur komponen dengan komponen "generik". Anda memiliki logika dasar untuk melampirkan dan melepaskan komponen "generik". Arsitektur dengan komponen generik lebih sulit dicapai karena Anda tidak tahu komponen mana. Manfaatnya adalah bahwa pendekatan ini dapat diperluas.

Arsitektur komponen yang valid dicapai dengan komponen yang ditentukan. Dengan didefinisikan maksudku, antarmuka didefinisikan, bukan implementasi. Misalnya, GameObject dapat memiliki IPhysicInstance, Transformation, IMesh, IAnimationController. Ini memiliki keuntungan karena Anda tahu antarmuka dan GameObject tahu cara menangani setiap komponen.

Menanggapi istilah generalisasi yang berlebihan

Saya pikir konsepnya tidak jelas. Ketika saya mengatakan komponen, tidak berarti bahwa semua komponen objek game harus diwariskan dari antarmuka komponen atau yang serupa. Namun ini adalah pendekatan untuk Komponen Generik, saya pikir itu adalah konsep yang Anda namakan sebagai komponen.

Arsitektur komponen adalah salah satu dari arsitektur yang ada untuk model objek runtime. Ini bukan satu-satunya dan bukan yang terbaik untuk semua kasus, tetapi pengalaman telah menunjukkan bahwa memiliki banyak manfaat, seperti kopling rendah.

Fitur utama yang membedakan arsitektur komponen adalah mengubah relasi is-a menjadi has-a. Misalnya arsitektur objek adalah-a:

is-a architecture

Alih-alih arsitektur objek memiliki-a (komponen):

memiliki-arsitektur

Ada juga arsitektur properti-sentris:

Misalnya, arsitektur objek normal adalah seperti ini:

  • Objek1

    • Posisi = (0, 0, 0)
    • Orientasi = (0, 43, 0)
  • Objek2

    • Posisi = (10, 0, 0)
    • Orientasi = (0, 0, 30)

Arsitektur properti-sentris:

  • Posisi

    • Object1 = (0, 0, 0)
    • Object2 = (10, 0, 0)
  • Orientasi

    • Object1 = (0, 43, 0)
    • Object2 = (0, 0, 30)

Lebih baik arsitektur model objek? Dalam perspektif kinerja, lebih baik properti-sentris karena lebih ramah-cache.

Komponen yang dimaksud adalah relasi is-a bukannya has-a. Komponen dapat berupa matriks Transformasi, angka empat, dll. Tetapi hubungan dengan objek game adalah relasi is-a, objek game tidak memiliki transformasi karena diwariskan. Game-objek memiliki (hubungan memiliki-a) transformasi. Transformasi kemudian menjadi komponen.

Objek permainan seperti hub. jika Anda menginginkan komponen yang selalu ada, maka mungkin komponen (seperti matriks) harus berada di dalam objek dan bukan pointer.

Tidak ada objek permainan yang sempurna untuk semua kasus, itu tergantung mesin, perpustakaan yang digunakan mesin. Mungkin saya ingin kamera yang tidak memiliki jala. Masalah dengan is-a architecture adalah bahwa jika objek-game memiliki mesh, maka kamera yang mewarisi dari objek-game harus memiliki mesh. Dengan komponen, kamera memiliki transformasi, pengontrol gerakan, dan apa pun yang Anda butuhkan.

Untuk informasi lebih lanjut, bab "14.2. Arsitektur Model Obyek Runtime" dari buku "Arsitektur Mesin Game" dari "Jason Gregory" membahas secara khusus subjek ini.

Diagram UML diekstraksi dari sana

momboco
sumber
Saya tidak setuju dengan paragraf terakhir, ada kecenderungan generalisasi yang berlebihan. Model berbasis komponen tidak berarti bahwa masing-masing dan setiap fungsionalitas harus menjadi komponen, masih harus mempertimbangkan hubungan fungsionalitas dan data. AnimationController tanpa Mesh tidak berguna, jadi letakkan di tempatnya, ke dalam mesh. Game-Object tanpa transformasi bukanlah Game-Object, itu milik definisi ke dunia 3D, jadi letakkan sebagai anggota normal ke dalam Game-Object. Aturan normal fungsionalitas enkapsulasi dan data masih berlaku.
Maik Semder
@Maik Semder: Saya setuju dengan Anda dalam generalisasi berlebihan secara umum, tapi saya pikir komponen istilahnya tidak jelas. Saya sudah mengedit jawaban saya. Baca dan beri saya kesan Anda.
momboco
@ momboco yah, Anda lebih kurang mengulangi poin yang sama;) Transform dan AnimationController masih digambarkan sebagai komponen, yang menurut saya terlalu banyak menggunakan model komponen. Kuncinya adalah untuk menentukan apa yang harus dimasukkan ke dalam komponen dan apa yang tidak:
Maik Semder
Jika sesuatu diperlukan untuk membuat Game-Object (GO) bekerja di tempat pertama, dengan kata lain jika itu tidak opsional, tetapi wajib, maka itu bukan komponen. Komponen seharusnya bersifat opsional. Transform adalah contoh yang baik, itu bukan optionl sama sekali untuk GO, GO tanpa Transform tidak masuk akal. Di sisi lain, tidak setiap GO membutuhkan fisika, sehingga dapat menjadi komponen.
Maik Semder
Jika ada hubungan yang kuat antara 2 funtionalities, seperti antara AnimationController (AC) dan Mesh, maka dengan cara apa pun jangan memecah hubungan ini dengan menekan AC ke dalam komponen, sebaliknya Mesh adalah komponen yang sempurna, tetapi AC adalah milik ke dalam Mesh.
Maik Semder