Tip untuk penanganan pesan Sistem Entitas Berbasis Komponen

8

Saya mencoba menerapkan sistem entitas berbasis komponen tetapi saya agak bingung tentang bagaimana saya harus menangani perpesanan. Ada dua masalah yang ingin saya selesaikan sehingga saya dapat menguji sistem. Di bawah ini adalah kode yang saya miliki sejauh ini,

Kelas Entitas:

class Entity{
public:
    Entity(unsigned int id):
    id_(id)
    {};
    void handleMessage(BaseMessage &message){
        for(auto element: components_){
            element.second->handleMessage(message);
        }
    }
    template<class T>
    void attachComponent(T *component){
        //Consider making safer in case someone tries to attach same component type twice
        components_[typeid(T).hash_code()] = component;
    }
    template<class T>
    void detachComponent(void){
        components_.erase(typeid(T).hash_code());
    }
    template<class T>
    T* getComponent(void)const{
        return *components_.find(typeid(T).hash_code());
    }
    unsigned int getInstanceID(void)const{
        return id_;
    }
private:
    unsigned int id_;
    std::map<size_t, BaseComponent*> components_;
};

Kelas Komponen dan Pesan Dasar:

class BaseComponent{
public:
    virtual void handleMessage(BaseMessage &message){};
};

class BaseMessage{
public:
    virtual int getType(void) = 0;
};

1. Penanganan Jenis Pesan

Pertanyaan pertama saya adalah bagaimana saya harus menangani berbagai jenis pesan (berasal dari BaseMessage).

Saya telah memikirkan dua cara untuk menangani jenis pesan dari jenis pesan yang diturunkan. Salah satunya adalah untuk menghasilkan hash (yaitu menggunakan FNV) dari string yang menamai jenis pesan dan menggunakan hash untuk menentukan jenis pesan. Jadi handleMessage(BaseMessage &message)fungsinya, pertama-tama akan mengekstrak hash ini dari pesan dan kemudian melakukan static_cast ke tipe yang sesuai.

Metode kedua adalah menggunakan templat sebagai berikut (mirip dengan attachComponentmetode kelas entitas),

template<class T>
handleMessage(T& message){};

dan membuat spesialisasi untuk setiap jenis pesan yang akan dibuat komponen tertentu.

Apakah ada kekurangan menggunakan metode kedua? Bagaimana dengan kinerja, mengapa saya tidak melihat penggunaan semacam ini lebih sering?

2. Penanganan Input

Pertanyaan kedua saya adalah apa yang akan menjadi cara optimal (dalam hal latensi dan kemudahan penggunaan) untuk menangani input?

Pikiran saya adalah membuat InputHandlerComponentregister yang dengan kelas keyboard untuk mendengarkan penekanan tombol tertentu yang mungkin ditentukan dalam beberapa file. Sebagai contoh

keyboard.register( player.getComponent<InputHandler>() , 'W')

Saya berharap ada panduan yang lebih ringkas untuk sistem berbasis komponen tetapi saya kira ada banyak cara berbeda untuk melakukan hal yang sama. Saya memiliki lebih banyak pertanyaan, tetapi saya pikir akan lebih bijaksana untuk mencoba menerapkan apa yang saya bisa.

Grieverheart
sumber

Jawaban:

3

Dalam sistem entitas tipikal, Anda meninggalkan sistem untuk memiliki semua logika permainan dan komponen Anda untuk menyimpan data saja , dan entitas Anda hanyalah pengidentifikasi sederhana. Penanganan input akan lebih mudah seperti itu dan belum lagi fleksibilitas permainan Anda.

Ada banyak cara untuk menangani peristiwa, seperti: pola pengamat, atau sinyal / slot. Anda bisa melakukan penanganan pesan dengan templat / fungsi virtual atau pointer fungsi, namun mungkin akan lebih mudah menggunakan boost :: signal , bahkan diketahui itu tidak terlalu bagus untuk kinerja. Jika Anda menginginkan kinerja *, saya sarankan Anda menggunakan templat dan fungsi virtual atau pointer fungsi.

* Saya perhatikan bahwa kode Anda benar-benar dapat dioptimalkan. Ada alternatif untuk typeidoperator, yang sebenarnya lebih cepat daripada menggunakan typeid. Seperti menggunakan templat / makro dan bilangan bulat sederhana untuk menentukan ID kelas.

Anda dapat melihat Sistem Entitas saya jika Anda memerlukan inspirasi (yang terinspirasi dari kerangka kerja Java Artemis ). Meskipun saya belum menerapkan cara untuk menangani acara (selain acara yang berhubungan dengan entitas), saya menyerahkannya kepada pengguna, tetapi setelah memilah perpustakaan entitasx , yang saya temukan di reddit. Saya pikir saya mungkin bisa menambahkan sistem acara ke perpustakaan saya. Harap perhatikan bahwa sistem entitas saya tidak 100% lengkap pada fitur apa yang saya inginkan, tetapi berfungsi dan memiliki kinerja yang layak (tapi saya bisa mengoptimalkan, terutama dengan cara saya menyimpan entitas).

Inilah yang dapat Anda lakukan untuk menangani acara (terinspirasi dari entitasx ):

BaseReceiver / BaseListener

  • Kelas dasar sehingga Anda dapat menyimpan pendengar / penerima.

Penerima / Pendengar

  • Ini adalah kelas templat dan mengharuskan Anda untuk menimpa fungsi virtual recieve(const T&), di mana T adalah informasi acara.
  • Kelas yang ingin diberitahukan oleh peristiwa yang mengirim informasi spesifik ketika suatu peristiwa terjadi harus mewarisi dari kelas ini.

EventHandler

  • Memancarkan / memadamkan acara
  • Memiliki daftar objek Penerima / Pendengar yang akan diberitahukan oleh acara yang dipecat

Saya sudah mengimplementasikan desain ini di C ++, barusan, tanpa menggunakan boost :: signal . Anda bisa melihatnya di sini .

Pro

  • Diketik secara statis
  • Fungsi virtual (lebih cepat daripada dorongan :: sinyal, well seharusnya)
  • Kompilasi kesalahan waktu jika Anda tidak memancarkan pemberitahuan dengan benar
  • Kompatibel dengan C ++ 98 (saya percaya)

Cons

  • Membutuhkan Anda untuk mendefinisikan functors (Anda tidak dapat menangani acara dalam fungsi global)
  • Tidak ada antrian acara; cukup daftar & tembak pergi. (yang mungkin bukan yang Anda inginkan)
  • Bukan panggilan langsung (tidak boleh seburuk itu untuk acara)
  • Tidak ada beberapa kejadian yang menangani pada kelas yang sama (ini dapat dimodifikasi sehingga memungkinkan banyak kejadian, melalui pewarisan berganda, tetapi mungkin perlu waktu untuk diimplementasikan)

Juga, saya punya contoh dalam sistem entitas saya, yang tidak menangani rendering dan penanganan input, cukup sederhana tetapi menyajikan banyak konsep untuk memahami perpustakaan saya.

miguel.martin
sumber
Bisakah Anda memberikan contoh cara lebih cepat untuk menemukan tipe tanpa typeid?
Luke B.
"Dalam sistem entitas tipikal, Anda meninggalkan sistem untuk memiliki semua logika untuk permainan Anda dan komponen untuk menyimpan data saja" - tidak ada yang namanya sistem entitas 'tipikal'.
Den
@ LukasB. Lihatlah kode sumber sistem entitas saya, saya tidak menggunakan typeid(lihat Component and ComponentContainer). Pada dasarnya, saya menyimpan "tipe" komponen sebagai integer, saya memiliki integer global yang saya tambahkan per jenis komponen. Dan saya menyimpan komponen dalam array 2d, di mana indeks pertama adalah ID entitas dan indeks ke-2 adalah ID tipe komponen, yaitu komponen [entityId] [componentTypeId]
miguel.martin
@ miguel.martin. Terima kasih atas jawaban Anda. Saya pikir kebingungan saya sebagian besar berasal dari kenyataan bahwa ada beberapa cara untuk menerapkan sistem entitas dan bagi saya semua memiliki kelemahan dan manfaatnya. Misalnya, dalam pendekatan yang Anda gunakan, Anda harus mendaftarkan tipe komponen baru menggunakan CRTP, yang saya pribadi tidak suka karena persyaratan ini tidak eksplisit. Saya pikir saya harus mempertimbangkan kembali implementasi saya.
Grieverheart
1
@MarsonMao diperbaiki
miguel.martin
1

Saya sedang mengerjakan sistem entitas berbasis komponen di C #, tetapi gagasan dan pola umum masih berlaku.

Cara saya menangani jenis pesan adalah bahwa setiap subkelas komponen memanggil RequestMessage<T>(Action<T> action) where T : IMessagemetode yang dilindungi Komponen . Dalam bahasa Inggris, itu berarti bahwa subkelas komponen meminta jenis pesan tertentu dan menyediakan metode untuk dipanggil ketika komponen menerima pesan jenis itu.

Metode itu akan disimpan dan ketika komponen menerima pesan, ia menggunakan refleksi untuk mendapatkan jenis pesan, yang kemudian digunakan untuk mencari metode terkait dan memintanya.

Anda dapat mengganti refleksi dengan sistem apa pun yang Anda inginkan, hash adalah taruhan terbaik Anda. Lihatlah beberapa pola pengiriman dan pengunjung untuk beberapa ide lain.

Sebagai masukan, saya memilih untuk melakukan sesuatu yang sama sekali berbeda dari apa yang Anda pikirkan. Input handler akan secara terpisah mengonversi input keyboard / mouse / gamepad menjadi enumerasi bendera (bitfield) dari tindakan yang mungkin (MoveForward, MoveBackwards, StrafeLeft, Use, dll.) Berdasarkan pada pengaturan pengguna / apa yang telah dicolokkan pemain ke komputer. Setiap komponen mendapatkan UserInputMessagesetiap bingkai yang berisi bitfield dan sumbu tampilan pemain sebagai vektor 3d (mengarahkan pengembangan menuju penembak orang pertama). Ini juga membuatnya mudah untuk membiarkan pemain mengubah binding utama mereka.

Seperti yang Anda katakan di akhir pertanyaan Anda, ada banyak cara berbeda untuk menciptakan sistem entitas berbasis komponen. Saya telah mengarahkan sistem saya ke permainan yang saya buat, jadi tentu saja beberapa hal yang saya lakukan mungkin tidak masuk akal dalam konteks, katakanlah, sebuah RTS, tapi itu masih sesuatu yang telah diterapkan dan telah bekerja untuk saya sejauh ini.

Robert Rouhani
sumber