Masukkan teknik manajemen dalam gim besar

16

Apakah ada teknik standar untuk mengelola input dalam game besar. Saat ini, dalam proyek saya, semua penanganan input dilakukan dalam loop game, seperti:

while(SDL_PollEvent(&event)){
            switch(event.type){
                case SDL_QUIT:
                    exit = 1;
                    break;
                case SDL_KEYDOWN:
                    switch(event.key.keysym.sym){
                        case SDLK_c:
                            //do stuff
                            break;
                    }
                    break;
                case SDL_MOUSEBUTTONDOWN:
                    switch(event.button.button){
                        case SDL_BUTTON_MIDDLE:
                                //do stuff
                                break;
                            }
                    }
                    break;
            }

(Saya menggunakan SDL, tapi saya berharap praktik utama berlaku pustaka dan kerangka kerja juga). Untuk proyek besar ini sepertinya bukan solusi terbaik. Saya mungkin memiliki beberapa objek yang ingin tahu apa yang ditekan oleh pengguna, jadi akan lebih masuk akal jika objek tersebut menangani input. Namun, mereka tidak semua dapat menangani input, karena setelah seseorang mendapatkan suatu peristiwa, itu akan dikeluarkan dari buffer acara, sehingga objek lain tidak akan menerima input itu. Metode apa yang paling umum digunakan untuk mengatasi ini?

w4etwetewtwet
sumber
Dengan seorang manajer acara, Anda dapat memecat acara dalam masukan dan membiarkan semua bagian lain dari permainan Anda mendaftar ke mereka.
danijar
@danijar apa sebenarnya yang Anda maksud dengan seorang manajer acara, apakah mungkin jika Anda bisa memberikan beberapa kode pseudo kerangka untuk menunjukkan hal seperti apa yang Anda bicarakan?
w4etwetewtwet
1
Saya menulis jawaban untuk menguraikan manajer acara, yang merupakan cara untuk mencari penanganan input bagi saya.
danijar

Jawaban:

12

Sejak ditanya oleh thread starter, saya menguraikan manajer acara. Saya pikir ini adalah cara yang baik untuk menangani input dalam game.

Seorang manajer acara adalah kelas global yang memungkinkan untuk mendaftarkan fungsi panggilan balik ke tombol dan untuk mematikan panggilan balik itu. Manajer acara menyimpan fungsi terdaftar dalam daftar pribadi yang dikelompokkan berdasarkan kunci mereka. Setiap kali kunci dipecat, semua panggilan balik yang terdaftar dijalankan.

Callback bisa menjadi std::functionobjek yang bisa menampung lambdas. Kuncinya bisa berupa string. Karena pengelola bersifat global, komponen aplikasi Anda dapat mendaftar ke kunci yang dipecat dari komponen lain.

// in character controller
// at initialization time
Events->Register("Jump", [=]{
    // perform the movement
});

// in input controller
// inside the game loop
// note that I took the code structure from the question
case SDL_KEYDOWN:
    switch(event.key.keysym.sym) {
    case SDLK_c:
        Events->Fire("Jump");
        break;
    }
    break;

Anda bahkan dapat memperluas pengelola acara ini untuk memungkinkan nilai yang lewat sebagai argumen tambahan. Templat C ++ bagus untuk ini. Anda dapat menggunakan sistem seperti itu untuk, katakanlah, untuk suatu "WindowResize"peristiwa lulus ukuran jendela baru, sehingga komponen mendengarkan tidak perlu mengambilnya sendiri. Ini dapat sedikit mengurangi ketergantungan kode.

Events->Register<int>("LevelUp", [=](int NewLevel){ ... });

Saya telah mengimplementasikan palungan acara untuk permainan saya. Jika Anda tertarik, saya akan memposting tautan ke kode di sini.

Dengan menggunakan manajer acara, Anda dapat dengan mudah menyiarkan informasi input dalam aplikasi Anda. Selain itu, ini memungkinkan cara yang bagus untuk memungkinkan pengguna menyesuaikan binding kunci. Komponen mendengarkan acara semantik alih-alih kunci secara langsung ( "PlayerJump"bukan "KeyPressedSpace"). Kemudian Anda dapat memiliki komponen pemetaan input yang mendengarkan "KeyPressedSpace"dan memicu tindakan apa pun yang terikat pengguna pada kunci itu.

danijar
sumber
4
Jawaban yang bagus, terima kasih. Meskipun saya ingin melihat kode, saya tidak ingin menyalinnya, jadi saya tidak akan meminta Anda untuk mempostingnya sampai saya menerapkannya sendiri, karena saya akan belajar lebih banyak dengan cara itu.
w4etwetewtwet
Saya hanya memikirkan sesuatu, dapatkah saya melewati fungsi anggota apa pun seperti ini, atau tidakkah fungsi register harus mengambil AClass :: func, membatasi ke fungsi satu anggota kelas
w4etwetewtwet
Itulah hal hebat tentang ekspresi lambda di C ++, Anda dapat menentukan klausa tangkapan [=]dan referensi ke semua variabel lokal yang diakses dari lambda akan disalin. Jadi Anda tidak harus melewati pointer ini atau yang seperti ini. Tetapi perhatikan bahwa Anda tidak dapat menyimpan lambdas dengan klausa penangkapan di pointer fungsi C lama . Namun, C ++ std::functionberfungsi dengan baik.
danijar
std :: function sangat lambat
TheStatehz
22

Membagi ini menjadi beberapa lapisan.

Pada lapisan terendah Anda memiliki peristiwa input mentah dari OS. Input keyboard SDL, input mouse, input joystick, dll. Anda mungkin memiliki beberapa platform (SDL adalah penyebut yang paling tidak umum yang tidak memiliki beberapa bentuk input, misalnya, yang mungkin nanti Anda pedulikan).

Anda dapat mengabstraksi ini dengan jenis acara khusus tingkat sangat rendah, seperti "tombol keyboard bawah" atau sejenisnya. Ketika lapisan platform Anda (loop game SDL) menerima input, itu harus membuat acara tingkat rendah ini dan kemudian meneruskannya ke manajer input. Hal ini dapat dilakukan dengan panggilan metode sederhana, fungsi panggilan balik, sistem acara yang rumit, apa pun yang paling Anda sukai.

Sistem input sekarang memiliki tugas menerjemahkan input tingkat rendah ke peristiwa logis tingkat tinggi. Logika game sama sekali tidak peduli bahwa SPACE ditekan. Itu peduli bahwa JUMP ditekan. Tugas manajer input adalah mengumpulkan acara input tingkat rendah ini dan menghasilkan acara input tingkat tinggi. Ini bertanggung jawab untuk mengetahui bahwa spasi dan tombol gamepad 'A' keduanya memetakan ke perintah logis Jump. Ini berkaitan dengan kontrol tampilan gamepad vs mouse dan sebagainya. Ini memancarkan peristiwa logis tingkat tinggi yang seabstrak mungkin dari kontrol tingkat rendah (ada beberapa batasan di sini, tetapi Anda bisa mengabstraksi berbagai hal secara menyeluruh dalam kasus umum).

Pengontrol karakter Anda kemudian menerima peristiwa ini dan memproses peristiwa input tingkat tinggi ini untuk benar-benar merespons. Lapisan platform mengirim acara "Key down spacebar." Sistem input menerima itu, melihat tabel pemetaan / logikanya, dan kemudian mengirimkan acara "Lompatan yang ditekan." Kontroler logika / karakter permainan menerima peristiwa itu, memeriksa apakah pemain benar-benar diizinkan untuk melompat, dan kemudian memancarkan peristiwa "Pemain melompat" (atau hanya secara langsung menyebabkan lompatan terjadi), yang digunakan oleh sisa logika permainan untuk melakukan apa pun .

Apa pun yang bergantung pada logika game masuk ke pengontrol pemain. Apa pun yang bergantung pada OS berlaku di lapisan platform. Semua sisanya masuk ke lapisan manajemen input.

Inilah beberapa seni ASCII amatir untuk menggambarkan ini:

-----------------------------------------------------------------------
Platform Abstraction | Collect and forward OS input events
-----------------------------------------------------------------------
                          | |
                          | |
                         \   /
                          \_/
-----------------------------------------------------------------------
    Input Manager    | Translate OS input events into logical events
-----------------------------------------------------------------------
                          | |
                          | |
                         \   /
                          \_/
-----------------------------------------------------------------------
Character Controller | React to logical events and affect game play
-----------------------------------------------------------------------
                          | |
                          | |
                         \   /
                          \_/
-----------------------------------------------------------------------
      Game Logic     | React to player actions and provides feedback
-----------------------------------------------------------------------
Sean Middleditch
sumber
Seni ASCII keren, tapi tidak perlu, maaf. Saya sarankan menggunakan daftar bernomor sebagai gantinya. Pokoknya jawaban yang bagus!
danijar
1
@danijar: Eh, saya sedang bereksperimen, belum pernah mencoba menggambar jawaban sebelumnya. Lebih banyak pekerjaan daripada nilainya, tetapi jauh lebih sedikit pekerjaan daripada berurusan dengan program cat. :)
Sean Middleditch
Baiklah, bisa dimengerti :-)
danijar
8
Secara pribadi, saya lebih suka cara seni ASCII lebih dari daftar bernomor membosankan.
Jesse Emond
@JesseEmond Hei, di sini untuk seni?
danijar