Teknik manajemen keadaan gim?

24

Pertama, saya tidak mengacu pada manajemen adegan; Saya mendefinisikan keadaan permainan secara longgar sebagai segala bentuk keadaan dalam permainan yang memiliki implikasi tentang apakah input pengguna harus diaktifkan atau tidak, atau jika aktor tertentu harus dinonaktifkan sementara, dll.

Sebagai contoh konkret, katakanlah ini adalah permainan Battlechess klasik. Setelah saya bergerak untuk mengambil bagian pemain lain, urutan pertempuran pendek dimainkan. Selama urutan ini, pemain seharusnya tidak diizinkan untuk memindahkan potongan. Jadi bagaimana Anda melacak transisi negara semacam ini? Mesin negara terbatas? Cek boolean sederhana? Tampaknya yang terakhir hanya akan bekerja dengan baik untuk game dengan sedikit perubahan kondisi seperti ini.

Saya bisa memikirkan banyak cara langsung untuk menangani ini menggunakan mesin negara yang terbatas, tetapi saya juga bisa melihat mereka dengan cepat lepas kendali. Saya hanya ingin tahu apakah ada cara yang lebih elegan untuk melacak status / transisi game.

Vargonian
sumber
Sudahkah Anda memeriksa gamedev.stackexchange.com/questions/1783/game-state-stack dan gamedev.stackexchange.com/questions/2423/… ? Agak semua melompat di sekitar konsep yang sama, tapi saya tidak bisa memikirkan apa pun yang akan lebih baik daripada mesin negara untuk keadaan game.
michael.bartnett
Kemungkinan rangkap: gamedev.stackexchange.com/questions/12664/…
Tetrad

Jawaban:

18

Saya pernah menemukan sebuah artikel yang menyelesaikan masalah Anda dengan cukup elegan. Ini adalah implementasi FSM dasar, yang disebut dalam loop utama Anda. Saya telah menguraikan rundown dasar artikel di sisa jawaban ini.

Status permainan dasar Anda terlihat seperti ini:

class CGameState
{
    public:
        // Setup and destroy the state
        void Init();
        void Cleanup();

        // Used when temporarily transitioning to another state
        void Pause();
        void Resume();

        // The three important actions within a game loop
        void HandleEvents();
        void Update();
        void Draw();
};

Setiap kondisi permainan diwakili oleh implementasi antarmuka ini. Sebagai contoh Battlechess Anda, ini bisa berarti keadaan ini:

  • animasi intro
  • menu utama
  • animasi pengaturan papan catur
  • input pemain pindah
  • animasi bergerak pemain
  • animasi bergerak lawan
  • menu jeda
  • layar endgame

Negara dikelola di mesin negara Anda:

class CGameEngine
{
    public:
        // Creating and destroying the state machine
        void Init();
        void Cleanup();

        // Transit between states
        void ChangeState(CGameState* state);
        void PushState(CGameState* state);
        void PopState();

        // The three important actions within a game loop
        // (these will be handled by the top state in the stack)
        void HandleEvents();
        void Update();
        void Draw();

        // ...
};

Perhatikan bahwa setiap negara membutuhkan penunjuk ke CGameEngine di beberapa titik, sehingga negara itu sendiri dapat memutuskan apakah negara baru harus dimasukkan. Artikel menyarankan untuk meneruskan CGameEngine sebagai parameter untuk HandleEvents, Pembaruan, dan Gambar.

Pada akhirnya, loop utama Anda hanya berurusan dengan engine keadaan:

int main ( int argc, char *argv[] )
{
    CGameEngine game;

    // initialize the engine
    game.Init( "Engine Test v1.0" );

    // load the intro
    game.ChangeState( CIntroState::Instance() );

    // main loop
    while ( game.Running() )
    {
        game.HandleEvents();
        game.Update();
        game.Draw();
    }

    // cleanup the engine
    game.Cleanup();
    return 0;
}
hantu
sumber
17
C untuk Kelas? Ew. Namun, itu artikel yang bagus - +1.
Bebek Komunis
Dari apa yang bisa saya kumpulkan, ini adalah jenis pertanyaan yang secara eksplisit -tidak- ditanyakan. Itu bukan untuk mengatakan Anda tidak bisa menanganinya dengan cara ini, seperti yang Anda tentu bisa, tetapi jika semua yang Anda ingin lakukan adalah menonaktifkan input untuk sementara waktu, saya pikir itu berlebihan dan buruk untuk pemeliharaan untuk mendapatkan subkelas baru dari CGameState yang akan menjadi 99% identik dengan subclass lain.
Kylotan
Saya akan berpikir ini sangat tergantung pada bagaimana kode digabungkan bersama. Saya bisa membayangkan pemisahan yang bersih antara memilih bagian dan tujuan (terutama indikator UI dan penanganan input), dan animasi potongan catur ke tujuan itu (animasi papan keseluruhan di mana potongan lain bergerak keluar dari jalan mereka, berinteraksi dengan bergerak bagian dll), membuat negara jauh dari identik. Ini memisahkan tanggung jawab, memungkinkan perawatan yang mudah dan bahkan usabilitas ulang (demo intro, mode replay). Saya pikir ini juga menjawab pertanyaan dalam menunjukkan bahwa menggunakan FSM tidak perlu merepotkan.
hantu
Ini sangat bagus, terima kasih. Poin penting yang Anda buat adalah dalam komentar terakhir Anda: "menggunakan FSM tidak perlu merepotkan." Saya telah salah membayangkan bahwa menggunakan FSM akan melibatkan menggunakan pernyataan switch, yang belum tentu benar. Konfirmasi kunci lainnya adalah bahwa setiap negara membutuhkan referensi ke mesin permainan; Saya bertanya-tanya bagaimana ini akan bekerja sebaliknya.
vargonian
2

Saya mulai dengan menangani hal semacam ini dengan cara sesederhana mungkin.

bool isPieceMoving;

Lalu saya akan menambahkan cek terhadap bendera boolean di tempat-tempat yang relevan.

Jika nanti saya menemukan saya perlu lebih banyak kasus khusus dari ini - dan hanya itu - saya faktor ulang menjadi sesuatu yang lebih baik. Biasanya ada 3 pendekatan yang akan saya ambil:

  • Refactor setiap bendera yang mewakili substate eksklusif ke dalam enum. misalnya.enum { PRE_MOVE, MOVE, POST_MOVE }dan tambahkan transisi di mana pun dibutuhkan. Lalu aku bisa mengecek terhadap enum ini di mana aku dulu mengecek bendera boolean. Ini adalah perubahan sederhana tetapi yang mengurangi jumlah hal yang harus Anda periksa, memungkinkan Anda untuk menggunakan pernyataan pergantian untuk mengelola perilaku secara efektif, dll.
  • Matikan masing-masing subsistem sesuai kebutuhan. Jika satu-satunya perbedaan selama urutan pertempuran adalah bahwa Anda tidak dapat memindahkan potongan, Anda dapat memanggil pieceSelectionManager->disable()atau serupa di awal urutan, dan pieceSelectionManager->enable(). Pada dasarnya Anda masih memiliki bendera, tetapi sekarang disimpan lebih dekat ke objek yang mereka kontrol, dan Anda tidak perlu mempertahankan status tambahan apa pun dalam kode permainan Anda.
  • Bagian sebelumnya menyiratkan keberadaan PieceSelectionManager: lebih umum, Anda dapat memfaktorkan bagian dari kondisi permainan Anda dan perilaku menjadi objek yang lebih kecil yang menangani subset dari keseluruhan negara secara koheren. Masing-masing objek ini akan memiliki beberapa keadaan sendiri yang menentukan perilakunya tetapi mudah dikelola karena terisolasi dari objek lain. Tahan keinginan untuk membiarkan objek gamestate Anda atau loop utama menjadi tempat pembuangan untuk pseudo-global dan faktor yang menjebak!

Secara umum saya tidak perlu melangkah lebih jauh dari ini ketika datang ke substrat kasus khusus, jadi saya tidak berpikir ada risiko "cepat keluar dari tangan".

Kylotan
sumber
1
Ya, saya membayangkan ada batas antara habis-habisan dengan negara dan hanya menggunakan bool / enum saat yang tepat. Tetapi mengetahui kecenderungan saya yang luar biasa, saya mungkin akan membuat hampir setiap negara bagian kelasnya sendiri.
vargonian
Anda membuatnya terdengar seperti kelas lebih benar daripada alternatif, tetapi ingat itu subjektif. Jika Anda mulai membuat terlalu banyak kelas kecil untuk hal-hal yang dapat direpresentasikan dengan lebih mudah oleh konstruksi bahasa lain maka itu dapat mengaburkan maksud kode.
Kylotan
1

http://www.ai-junkie.com/architecture/state_driven/tut_state1.html adalah tutorial yang bagus untuk manajemen keadaan gim! Anda dapat menggunakannya baik untuk entitas game atau untuk sistem menu seperti di atas.

Dia mulai mengajar tentang Pola Desain Negara , dan kemudian menerapkan State Machine, dan terus mengembangkannya. Ini bacaan yang sangat bagus! Akan memberi Anda pemahaman yang kuat tentang bagaimana keseluruhan konsep bekerja dan bagaimana menerapkannya pada jenis masalah baru!

Zolomon
sumber
1

Saya mencoba untuk tidak menggunakan mesin negara dan boolean untuk tujuan ini, karena keduanya tidak dapat diskalakan. Keduanya berubah menjadi berantakan ketika jumlah negara tumbuh.

Saya biasanya mendesain gameplay sebagai urutan tindakan dan konsekuensi, setiap kondisi permainan datang secara alami tanpa perlu mendefinisikannya secara terpisah.

Misalnya dalam kasus Anda dengan menonaktifkan input pemain: Anda memiliki beberapa handler input pengguna dan beberapa indikasi visual ingame bahwa input dinonaktifkan, Anda harus menjadikan mereka objek atau komponen satu, jadi untuk menonaktifkan input Anda cukup menonaktifkan seluruh objek, tidak perlu menyinkronkannya dalam beberapa mesin keadaan atau bereaksi terhadap beberapa indikator boolean.

Filipp Keks
sumber