Büyük oyunlarda girdi yönetimi teknikleri


16

Büyük oyunlarda girdileri yönetmek için standart bir teknik var mı? Şu anda, projemde, tüm giriş işlemleri oyun döngüsünde yapılır, şöyle:

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;
            }

(SDL kullanıyorum, ancak ana uygulamanın kütüphaneler ve çerçeveler de uygulanmasını bekliyorum). Büyük bir proje için bu en iyi çözüm gibi görünmüyor. Kullanıcının ne bastığını bilmek isteyen birkaç nesneye sahip olabilirim, bu yüzden bu nesnelerin girdiyi işlemesi daha mantıklı olur. Ancak, biri olayı aldıktan sonra olay arabelleğine itileceğinden, girdiyi işleyemezler, bu nedenle başka bir nesne bu girdiyi alamaz. Buna karşı koymak için en yaygın olarak hangi yöntem kullanılır?


Bir etkinlik yöneticisi ile girişte olayı tetikleyebilir ve oyununuzun diğer tüm bölümlerinin kayıt olmasına izin verebilirsiniz.
danijar

@danijar Bir etkinlik yöneticisi tarafından tam olarak ne demek istiyorsun, ne tür bir şeyden bahsettiğini göstermek için bazı iskelet sözde kodları sağlayabilirsen mümkün mü?
w4etwetewtwet


1
Benim için girdi işlemeye gitmenin yolu olan etkinlik yöneticileri hakkında ayrıntılı bir cevap yazdım.
danijar

Yanıtlar:


12

Konu başlatıcı tarafından sordum beri, olay yöneticileri üzerinde ayrıntılı. Bence bu bir oyunda girdileri ele almanın iyi bir yolu.

Bir olay yöneticisi, hem geri çağrı işlevlerinin tuşlara kaydedilmesini hem de bu geri çağrıların tetiklenmesini sağlayan global bir sınıftır. Olay yöneticisi, kayıtlı işlevleri anahtarlarına göre gruplandırılmış özel bir listede saklar. Bir tuş her başlatıldığında, kayıtlı tüm geri aramalar yürütülür.

Geri aramalar std::functionlambdaları tutabilen nesneler olabilir. Anahtarlar dize olabilir. Yönetici genel olduğundan, uygulamanızın bileşenleri diğer bileşenlerden tetiklenen anahtarlara kaydolabilir.

// 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;

Bu olay yöneticisini, değerleri ek argümanlar olarak iletmeye izin verecek şekilde genişletebilirsiniz. C ++ şablonları bunun için mükemmeldir. Böyle bir sistemi, örneğin bir "WindowResize"olayın yeni pencere boyutunu geçmesi için kullanabilirsiniz, böylece dinleme bileşenlerinin kendileri getirmesi gerekmez. Bu, kod bağımlılıklarını biraz azaltabilir.

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

Oyunum için böyle bir etkinlik yöneticisi uyguladım. Eğer ilgileniyorsanız, kodun bağlantısını buraya gönderirim.

Bir olay yöneticisi kullanarak, giriş bilgilerini uygulamanızda kolayca yayınlayabilirsiniz. Dahası, bu, kullanıcının anahtar bağlamaları özelleştirmesine izin vermek için güzel bir yol sağlar. Bileşenler, anahtarlar "PlayerJump"yerine doğrudan yerine semantik olayları dinler "KeyPressedSpace". Daha sonra "KeyPressedSpace", kullanıcının o anahtara bağlı olduğu eylemi dinleyen ve tetikleyen bir giriş eşleme bileşenine sahip olabilirsiniz .


4
Harika cevap, teşekkürler. Kodu görmek isterim, ancak kopyalamak istemiyorum, bu yüzden kendimi uygulayana kadar göndermenizi istemeyeceğim, çünkü bu şekilde daha fazla öğreneceğim.
w4etwetewtwet

Ben sadece bir şey düşündüm, böyle bir üye işlevini geçebilir miyim, ya da kayıt işlevi AClass :: func, bir sınıf üye işlevleri ile sınırlamak zorunda
kalmayacak mısın

Bu C ++ 'da lambda ifadeleri ile ilgili harika bir şey, bir yakalama cümlesi belirtebilirsiniz [=]ve lambda'dan erişilen tüm yerel değişkenlere referanslar kopyalanacaktır. Yani bu işaretçiyi ya da bunun gibi bir şeyi geçmek zorunda değilsiniz. Ancak eski C işlev işaretçilerinde yakalama yan tümcesiyle lambdasları depolayamayacağınızı unutmayın . Ancak, C ++ iyi std::functionçalışıyor.
danijar

std :: işlev çok yavaş
TheStatehz

22

Bunu birkaç katmana ayırın.

En alt katmanda, işletim sisteminden ham giriş olayları vardır. SDL klavye girişi, fare girişi, joystick girişi, vb. Birkaç platformunuz olabilir (SDL, örneğin daha sonra ilgilenebileceğiniz birkaç giriş formundan yoksun en az ortak paydadır).

Bunları "klavye düğmesi aşağı" veya benzeri çok düşük düzeyli bir özel etkinlik türüyle özetleyebilirsiniz. Platform katmanınız (SDL oyun döngüsü) girdi aldığında, bu düşük düzeyli olayları oluşturmalı ve ardından bunları bir giriş yöneticisine iletmelidir. Bunları basit yöntem çağrıları, geri arama fonksiyonları, karmaşık bir olay sistemi, en çok ne istersen yapabilir.

Giriş sistemi artık düşük seviyeli girişi yüksek seviyeli mantıksal olaylara dönüştürme görevine sahiptir. Oyun mantığı, SPACE tuşuna basılmasını hiç umursamıyor. JUMP'a basılması umurunda. Giriş yöneticisinin görevi bu düşük seviyeli giriş olaylarını toplamak ve yüksek seviyeli giriş olayları oluşturmaktır. Boşluk çubuğunun ve 'A' gamepad düğmesinin Jump mantıksal komutuyla eşleştiğini bilmek sorumludur. Gamepad vs fare bakmak kontrolleri ve benzeri ile ilgilenir. Düşük seviyeli kontrollerden mümkün olduğunca soyut olan yüksek seviyeli mantıksal olaylar yayar (burada bazı sınırlamalar vardır, ancak ortak durumda tamamen soyutlayabilirsiniz).

Karakter denetleyiciniz bu olayları alır ve gerçekte yanıt vermek için bu üst düzey girdi olaylarını işler. Platform katmanı "Key down spacebar" etkinliğini gönderdi. Girdi sistemi bunu aldı, eşleme tablolarına / mantığına bakar ve sonra "Basılı atlama" etkinliğini gönderir. Oyun mantığı / karakter denetleyicisi bu olayı alır, oyuncunun gerçekten atlamasına izin verilip verilmediğini kontrol eder ve daha sonra oyun mantığının geri kalanının her şeyi yapmak için kullandığı "Oyuncu atladı" etkinliğini yayar (ya da sadece doğrudan bir sıçramaya neden olur) .

Oyun mantığına bağlı olan her şey oynatıcı kontrolörüne girer. İşletim sistemine bağımlı her şey platform katmanına gider. Geri kalan her şey giriş yönetimi katmanına gider.

İşte bunu tanımlamak için bazı amatör ASCII sanatı:

-----------------------------------------------------------------------
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
-----------------------------------------------------------------------

Serin ASCII sanatı, ama gerekli değil, üzgünüm. Bunun yerine numaralı bir liste kullanmanızı öneririm. Neyse iyi bir cevap!
danijar

1
@danijar: Eh, deniyordum, daha önce hiç bir cevap çıkarmayı denememiştim. Değerinden daha fazla iş, ama bir boya programı ile uğraşmaktan çok daha az iş. :)
Sean Middleditch

Tamam, anlaşılabilir :-)
danijar

8
Şahsen, ASCII sanatını sıkıcı bir numara listesinden çok tercih ediyorum.
Jesse Emond

@JesseEmond Hey, sanat için mi buradasın?
danijar
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.