เทคนิคการจัดการสถานะเกม?


24

ก่อนอื่นฉันไม่ได้หมายถึงการจัดการฉาก ฉันกำหนดสถานะของเกมอย่างหลวม ๆ เหมือนกับสถานะใด ๆ ในเกมที่มีความหมายว่าควรเปิดใช้งานอินพุตของผู้ใช้หรือไม่หรือหากนักแสดงบางคนควรปิดการใช้งานชั่วคราว ฯลฯ

ยกตัวอย่างเช่นสมมติว่าเป็นเกมของ Battlechess คลาสสิก หลังจากที่ฉันขยับไปหยิบชิ้นส่วนของผู้เล่นคนอื่นการต่อสู้สั้น ๆ ก็จะเริ่มเล่น ในช่วงนี้ผู้เล่นไม่ควรได้รับอนุญาตให้ย้ายชิ้น ดังนั้นคุณจะติดตามการเปลี่ยนแปลงสถานะเช่นนี้ได้อย่างไร เครื่องสถานะ จำกัด ? ตรวจสอบบูลีนง่าย ๆ ? ดูเหมือนว่าเกมนี้จะใช้งานได้ดีสำหรับเกมที่มีการเปลี่ยนแปลงสถานะเพียงเล็กน้อยเท่านั้น

ฉันสามารถคิดถึงวิธีการจัดการที่ตรงไปตรงมาจำนวนมากโดยใช้เครื่องจักรสถานะ จำกัด แต่ฉันยังสามารถเห็นพวกเขาออกจากมือได้อย่างรวดเร็ว ฉันแค่อยากรู้ว่ามีวิธีที่สวยงามกว่านี้ในการติดตามสถานะเกม / ช่วงการเปลี่ยนภาพหรือไม่


คุณเคยลองดูgamedev.stackexchange.com/questions/1783/game-state-stackและgamedev.stackexchange.com/questions/2423/หรือไม่? มันคือการกระโดดไปรอบ ๆ แนวคิดเดียวกัน แต่ฉันไม่สามารถคิดอะไรที่จะดีกว่าเครื่องจักรรัฐสำหรับสถานะเกม
michael.bartnett

สำเนาซ้ำที่เป็นไปได้: gamedev.stackexchange.com/questions/12664/…
Tetrad

คำตอบ:


18

ฉันเคยเจอบทความที่แก้ปัญหาของคุณได้อย่างงดงาม เป็นการใช้งาน FSM ขั้นพื้นฐานที่เรียกว่าในลูปหลักของคุณ ฉันได้สรุปบทสรุปพื้นฐานของบทความในส่วนที่เหลือของคำตอบนี้

สถานะเกมพื้นฐานของคุณมีลักษณะดังนี้:

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

แต่ละสถานะของเกมจะแสดงโดยการใช้อินเทอร์เฟซนี้ สำหรับตัวอย่าง Battlechess ของคุณนี่อาจหมายถึงสถานะเหล่านี้:

  • นิเมชั่นบทนำ
  • เมนูหลัก
  • ภาพเคลื่อนไหวการตั้งค่ากระดานหมากรุก
  • ผู้เล่นย้ายอินพุต
  • ภาพเคลื่อนไหวย้ายผู้เล่น
  • นิเมชั่นย้ายฝ่ายตรงข้าม
  • เมนูหยุดชั่วคราว
  • หน้าจอเกม

สถานะถูกจัดการในเอ็นจิ้นรัฐของคุณ:

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();

        // ...
};

โปรดทราบว่าแต่ละรัฐต้องการตัวชี้ไปยัง CGameEngine ในบางจุดดังนั้นรัฐสามารถตัดสินใจได้ว่าควรป้อนสถานะใหม่หรือไม่ บทความแนะนำให้ส่งผ่าน CGameEngine เป็นพารามิเตอร์สำหรับ HandleEvents, Update และ Draw

ในที่สุดลูปหลักของคุณจะเกี่ยวข้องกับเอ็นจิ้นของรัฐเท่านั้น:

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

17
C สำหรับชั้นเรียน? Ew อย่างไรก็ตามนั่นเป็นบทความที่ดี - +1
The Duck คอมมิวนิสต์

จากสิ่งที่ฉันสามารถรวบรวมได้นี่คือสิ่งที่ประเภทของคำถามที่ชัดเจน - ไม่ - ถามเกี่ยวกับ ไม่ได้บอกว่าคุณไม่สามารถจัดการได้ด้วยวิธีนี้อย่างแน่นอน แต่ถ้าคุณต้องการปิดการใช้งานอินพุตชั่วคราวฉันคิดว่ามันทั้ง overkill และไม่ดีสำหรับการบำรุงรักษาเพื่อให้ได้ subclass ใหม่ของ CGameState ซึ่งจะไป เท่ากับ 99% เหมือนกับคลาสย่อยอื่น
Kylotan

ฉันคิดว่าสิ่งนี้ขึ้นอยู่กับว่าโค้ดทำงานควบคู่กันอย่างไร ฉันสามารถจินตนาการถึงการแยกที่สะอาดระหว่างการเลือกชิ้นส่วนและปลายทาง (ส่วนใหญ่เป็นตัวบ่งชี้ UI และการจัดการอินพุต) และภาพเคลื่อนไหวของตัวหมากรุกไปทางปลายทางนั้น (ภาพเคลื่อนไหวบนกระดานทั้งหมดที่ชิ้นส่วนอื่นเคลื่อนออกจากทางของพวกเขา ชิ้นส่วนอื่น ๆ ) ทำให้รัฐห่างไกลจากที่เหมือนกัน สิ่งนี้แยกความรับผิดชอบทำให้ง่ายต่อการบำรุงรักษาและแม้กระทั่งการนำกลับมาใช้ใหม่ได้ (สาธิตอินโทร, โหมดเล่นซ้ำ) ฉันคิดว่านี่เป็นคำตอบสำหรับคำถามที่แสดงว่าการใช้ FSM ไม่จำเป็นต้องยุ่งยาก
ผี

มันเยี่ยมมากจริงๆขอบคุณ จุดสำคัญที่คุณทำคือความคิดเห็นล่าสุดของคุณ: "การใช้ FSM ไม่จำเป็นต้องยุ่งยาก" ฉันนึกภาพผิดพลาดว่าการใช้ FSM จะเกี่ยวข้องกับการใช้คำสั่งสวิตช์ซึ่งไม่จำเป็นต้องเป็นเรื่องจริง การยืนยันที่สำคัญอีกประการหนึ่งคือแต่ละรัฐต้องการการอ้างอิงถึงเอ็นจิ้นเกม ฉันสงสัยว่ามันจะทำงานเป็นอย่างอื่นได้อย่างไร
vargonian

2

ฉันเริ่มต้นด้วยการจัดการสิ่งนี้เป็นวิธีที่ง่ายที่สุดที่เป็นไปได้

bool isPieceMoving;

จากนั้นฉันจะเพิ่มการตรวจสอบกับธงบูลีนนั้นในสถานที่ที่เกี่ยวข้อง

หากภายหลังฉันพบว่าฉันจำเป็นต้องมีกรณีพิเศษมากกว่านี้ - และเฉพาะ - ฉันอีกปัจจัยเป็นสิ่งที่ดีกว่า โดยปกติจะมี 3 วิธีที่ฉันจะทำ:

  • Refactor แฟล็กที่เป็นตัวแทนของสถานะย่อยใด ๆ เช่น. enum { PRE_MOVE, MOVE, POST_MOVE }และเพิ่มช่วงการเปลี่ยนภาพทุกที่ที่ต้องการ จากนั้นฉันสามารถตรวจสอบกับ enum นี้ที่ฉันเคยตรวจสอบกับธงบูลีน นี่เป็นการเปลี่ยนแปลงที่ง่าย แต่สิ่งที่ช่วยลดจำนวนสิ่งที่คุณต้องตรวจสอบช่วยให้คุณใช้คำสั่งสวิตช์เพื่อจัดการพฤติกรรมได้อย่างมีประสิทธิภาพ ฯลฯ
  • ปิดระบบย่อยแต่ละระบบตามที่คุณต้องการ หากแตกต่างระหว่างขั้นตอนการต่อสู้คือการที่คุณไม่สามารถเคลื่อนย้ายชิ้นคุณสามารถโทรหากันหรือคล้ายกันที่จุดเริ่มต้นของลำดับและpieceSelectionManager->disable() pieceSelectionManager->enable()คุณยังคงมีสถานะเป็นหลัก แต่ตอนนี้พวกเขาจะถูกเก็บไว้ใกล้กับวัตถุที่พวกเขาควบคุมและคุณไม่จำเป็นต้องรักษาสถานะพิเศษใด ๆ ในรหัสเกมของคุณ
  • ส่วนก่อนหน้าแสดงถึงการมีอยู่ของ PieceSelectionManager: โดยทั่วไปคุณสามารถแยกส่วนของสถานะเกมและพฤติกรรมของคุณออกเป็นวัตถุขนาดเล็กที่จัดการชุดย่อยของรัฐโดยรวมในลักษณะที่สอดคล้องกัน แต่ละวัตถุเหล่านี้จะมีสถานะเป็นของตัวเองซึ่งกำหนดพฤติกรรมของมัน แต่มันง่ายในการจัดการเนื่องจากแยกออกจากวัตถุอื่น ๆ ต่อต้านการกระตุ้นให้วัตถุ gamestate ของคุณหรือลูปหลักกลายเป็นพื้นทิ้งสำหรับหลอกหลอกและปัจจัยที่ออกมา!

โดยทั่วไปฉันไม่จำเป็นต้องไปไกลกว่านี้เมื่อพูดถึงกรณีพิเศษดังนั้นฉันไม่คิดว่ามีความเสี่ยงที่จะ


1
ใช่ฉันคิดว่ามีเส้นแบ่งระหว่างการออกไปข้างนอกกับรัฐและเพียงแค่ใช้บูล / enums เมื่อเหมาะสม แต่การที่รู้ถึงแนวโน้มที่อวดดีของฉันฉันอาจจะจบลงด้วยการทำให้รัฐเกือบทุกระดับเป็นของตัวเอง
vargonian

คุณทำให้มันฟังดูเหมือนว่าคลาสนั้นถูกต้องมากกว่าทางเลือกอื่น แต่จำไว้ว่ามันเป็นเรื่องส่วนตัว หากคุณเริ่มสร้างชั้นเรียนขนาดเล็กมากเกินไปสำหรับสิ่งต่าง ๆ ที่สามารถแสดงได้ง่ายขึ้นโดยการสร้างภาษาอื่น ๆ ก็สามารถปิดบังเจตนาของรหัส
Kylotan

1

http://www.ai-junkie.com/architecture/state_driven/tut_state1.htmlเป็นบทแนะนำที่น่ารักสำหรับการจัดการสถานะเกม! คุณสามารถใช้มันสำหรับเอนทิตีเกมหรือระบบเมนูเช่นด้านบน

เขาเริ่มสอนเกี่ยวกับรูปแบบการออกแบบของรัฐและจากนั้นก็นำไปใช้State Machineและขยายอย่างต่อเนื่องไปเรื่อย ๆ มันเป็นการอ่านที่ดีมาก! จะทำให้คุณเข้าใจอย่างถ่องแท้ว่าแนวคิดทั้งหมดทำงานอย่างไรและนำไปใช้กับปัญหาประเภทใหม่ได้อย่างไร!


1

ฉันพยายามไม่ใช้เครื่องสถานะและบูลีนเพื่อจุดประสงค์นี้เนื่องจากทั้งสองไม่สามารถปรับขนาดได้ ทั้งสองกลายเป็นระเบียบเมื่อจำนวนของรัฐเพิ่มขึ้น

ฉันมักจะออกแบบเกมเพลย์เป็นลำดับของการกระทำและผลที่ตามมาสถานะของเกมใด ๆ ก็เข้ามาอย่างเป็นธรรมชาติโดยไม่จำเป็นต้องแยกเกม

ตัวอย่างเช่นในกรณีของคุณด้วยการปิดการใช้งานอินพุตของผู้เล่น: คุณมีตัวจัดการอินพุตของผู้ใช้และตัวบ่งชี้ภาพในเกมที่อินพุตถูกปิดใช้งานคุณควรทำให้พวกเขาเป็นวัตถุหรือส่วนประกอบเดียวดังนั้นหากต้องการปิดการใช้งานอินพุต ประสานพวกเขาในเครื่องรัฐบางหรือตอบสนองต่อตัวบ่งชี้บูลีน

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.