Trò chơi kỹ thuật quản lý nhà nước?


24

Trước hết, tôi không đề cập đến quản lý cảnh; Tôi đang xác định trạng thái trò chơi một cách lỏng lẻo như bất kỳ loại trạng thái nào trong trò chơi có liên quan đến việc có nên bật đầu vào của người dùng hay không, hoặc nếu một số diễn viên nhất định nên tạm thời bị vô hiệu hóa, v.v.

Một ví dụ cụ thể, giả sử đó là một trò chơi của Battlechess cổ điển. Sau khi tôi di chuyển để lấy quân cờ của người chơi khác, một chuỗi trận chiến ngắn sẽ diễn ra. Trong chuỗi này, người chơi không được phép di chuyển quân cờ. Vì vậy, làm thế nào bạn sẽ theo dõi loại chuyển đổi trạng thái này? Một máy trạng thái hữu hạn? Một kiểm tra boolean đơn giản? Có vẻ như cái sau chỉ hoạt động tốt cho một trò chơi với rất ít thay đổi trạng thái của loại này.

Tôi có thể nghĩ ra rất nhiều cách đơn giản để xử lý việc này bằng cách sử dụng các máy trạng thái hữu hạn, nhưng tôi cũng có thể thấy chúng nhanh chóng thoát khỏi tầm tay. Tôi chỉ tò mò nếu có một cách thanh lịch hơn để theo dõi trạng thái / chuyển đổi trò chơi.


Bạn đã kiểm tra gamedev.stackexchange.com/questions/1783/game-state-stackgamedev.stackexchange.com/questions/2423/ cáp ? Tất cả đều nhảy xung quanh cùng một khái niệm, nhưng tôi không thể nghĩ ra thứ gì tốt hơn một cỗ máy trạng thái cho trạng thái trò chơi.
michael.bartnett

Câu trả lời:


18

Tôi đã từng bắt gặp một bài viết giải quyết vấn đề của bạn khá thanh lịch. Đây là một triển khai FSM cơ bản, được gọi trong vòng lặp chính của bạn. Tôi đã phác thảo danh sách cơ bản của bài viết trong phần còn lại của câu trả lời này.

Trạng thái trò chơi cơ bản của bạn trông như thế này:

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

Mỗi trạng thái trò chơi được thể hiện bằng cách thực hiện giao diện này. Đối với ví dụ Battlechess của bạn, điều này có thể có nghĩa là các trạng thái sau:

  • hoạt hình giới thiệu
  • thực đơn chính
  • thiết lập bàn cờ hoạt hình
  • người chơi di chuyển đầu vào
  • người chơi di chuyển hình ảnh động
  • đối thủ di chuyển hoạt hình
  • menu tạm dừng
  • màn hình kết thúc

Các tiểu bang được quản lý trong công cụ trạng thái của bạn:

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

        // ...
};

Lưu ý rằng mỗi trạng thái cần một con trỏ tới CGameEngine tại một thời điểm nào đó, vì vậy chính trạng thái đó có thể quyết định liệu có nên nhập trạng thái mới hay không. Bài viết đề nghị chuyển vào CGameEngine như một tham số cho HandEvents, Cập nhật và Vẽ.

Cuối cùng, vòng lặp chính của bạn chỉ giao dịch với công cụ trạng thái:

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 cho lớp? Ew. Tuy nhiên, đó là một bài viết tốt - +1.
Vịt Cộng sản

Từ những gì tôi có thể thu thập, đây là loại câu hỏi rõ ràng - không - hỏi về. Điều đó không có nghĩa là bạn không thể xử lý theo cách này, như bạn chắc chắn có thể, nhưng nếu tất cả những gì bạn muốn làm là tạm thời vô hiệu hóa đầu vào thì tôi nghĩ rằng cả hai đều quá mức và không tốt cho việc bảo trì để tạo ra một lớp con mới của CGameState. giống hệt 99% với một lớp con khác.
Kylotan

Tôi nghĩ điều này phụ thuộc rất nhiều vào cách mã được ghép với nhau. Tôi có thể tưởng tượng một sự tách biệt rõ ràng giữa việc chọn một quân cờ và đích (chủ yếu là các chỉ số UI và xử lý đầu vào) và một hình ảnh động của quân cờ về đích đó (một hoạt hình toàn bộ bảng trong đó các quân cờ khác di chuyển ra ngoài, tương tác với việc di chuyển mảnh vv), làm cho các tiểu bang xa giống hệt nhau. Điều này phân tách trách nhiệm, cho phép bảo trì dễ dàng và thậm chí có thể sử dụng lại (giới thiệu giới thiệu, chế độ phát lại). Tôi nghĩ rằng điều này cũng trả lời câu hỏi cho thấy rằng sử dụng một FSM không cần phải là một rắc rối.
ma

Điều này thực sự tuyệt vời, cảm ơn bạn. Một điểm quan trọng bạn đã đưa ra là trong nhận xét mới nhất của bạn: "sử dụng một FSM không cần phải là một rắc rối." Tôi đã tưởng tượng sai lầm rằng sử dụng một FSM sẽ liên quan đến việc sử dụng các câu lệnh chuyển đổi, điều này không nhất thiết đúng. Một xác nhận quan trọng khác là mỗi tiểu bang cần một tài liệu tham khảo cho công cụ trò chơi; Tôi tự hỏi làm thế nào điều này sẽ làm việc khác.
vargonia

2

Tôi bắt đầu bằng cách xử lý loại điều này theo cách đơn giản nhất có thể.

bool isPieceMoving;

Sau đó, tôi sẽ thêm các kiểm tra đối với cờ boolean đó ở những nơi có liên quan.

Nếu sau này tôi thấy tôi cần nhiều trường hợp đặc biệt hơn thế này - và chỉ có nó - tôi tái xác nhận thành một điều tốt hơn. Thường có 3 cách tiếp cận tôi sẽ thực hiện:

  • Tái cấu trúc bất kỳ cờ đại diện độc quyền nào thành enum. ví dụ. enum { PRE_MOVE, MOVE, POST_MOVE }và thêm các chuyển tiếp bất cứ nơi nào cần thiết. Sau đó, tôi có thể kiểm tra enum này nơi tôi đã sử dụng để kiểm tra cờ boolean. Đây là một thay đổi đơn giản nhưng một trong số đó làm giảm số lượng những thứ bạn phải kiểm tra, cho phép bạn sử dụng các câu lệnh chuyển đổi để quản lý hành vi hiệu quả, v.v.
  • Tắt các hệ thống con riêng lẻ khi bạn cần. Nếu sự khác biệt duy nhất trong chuỗi trận chiến là bạn không thể di chuyển quân cờ, bạn có thể gọi pieceSelectionManager->disable()hoặc tương tự khi bắt đầu chuỗi, và pieceSelectionManager->enable(). Về cơ bản bạn vẫn có cờ, nhưng hiện tại chúng được lưu trữ gần hơn với đối tượng mà chúng kiểm soát và bạn không cần duy trì bất kỳ trạng thái bổ sung nào trong mã trò chơi của mình.
  • Phần trước ngụ ý sự tồn tại của PieceSelectionManager: nói chung, bạn có thể đưa các phần của trạng thái trò chơi và hành vi của mình vào các đối tượng nhỏ hơn để xử lý một tập hợp con của trạng thái tổng thể theo cách mạch lạc. Mỗi đối tượng này sẽ có một số trạng thái riêng xác định hành vi của nó nhưng thật dễ quản lý vì nó cách ly với các đối tượng khác. Chống lại sự thôi thúc cho phép đối tượng chơi trò chơi hoặc vòng lặp chính của bạn trở thành bãi rác cho các giả toàn cầu và yếu tố giải quyết!

Nói chung, tôi không bao giờ cần phải đi xa hơn thế này khi nói đến các trường hợp đặc biệt, vì vậy tôi không nghĩ rằng có nguy cơ nó 'nhanh chóng thoát khỏi tầm tay'.


1
Vâng, tôi tưởng tượng có một ranh giới giữa việc đi ra ngoài với các trạng thái và chỉ sử dụng bool / enums khi thích hợp. Nhưng biết được xu hướng phạm vi của mình, có lẽ cuối cùng tôi sẽ biến gần như mọi tiểu bang thành lớp học của riêng mình.
vargonia

Bạn làm cho nó có vẻ như một lớp học là chính xác hơn so với các lựa chọn thay thế, nhưng hãy nhớ rằng đó là chủ quan. Nếu bạn bắt đầu tạo quá nhiều lớp nhỏ cho những thứ có thể được biểu diễn dễ dàng hơn bởi các cấu trúc ngôn ngữ khác thì nó có thể che khuất ý định của mã.
Kylotan

1

http://www.ai-junkie.com/arch architecture / state_driven / tut_state1.html là một hướng dẫn đáng yêu để quản lý nhà nước trò chơi! Bạn có thể sử dụng nó cho các thực thể trò chơi hoặc cho một hệ thống menu như trên.

Ông bắt đầu giảng dạy về Mẫu thiết kế nhà nước , và sau đó tiếp tục thực hiện State Machinevà tiếp tục mở rộng nó hơn nữa. Đó là một đọc rất tốt! Sẽ cung cấp cho bạn một sự hiểu biết vững chắc về cách toàn bộ khái niệm hoạt động và làm thế nào để áp dụng nó vào các loại vấn đề mới!


1

Tôi cố gắng không sử dụng máy trạng thái và booleans cho mục đích này, vì cả hai đều không thể mở rộng. Cả hai biến thành lộn xộn khi số lượng nhà nước phát triển.

Tôi thường thiết kế lối chơi như một chuỗi các hành động và hậu quả, bất kỳ trạng thái trò chơi nào chỉ diễn ra một cách tự nhiên mà không cần phải xác định nó một cách riêng biệt.

Ví dụ: trong trường hợp của bạn với việc vô hiệu hóa đầu vào trình phát: bạn có một số trình xử lý đầu vào của người dùng và một số dấu hiệu trực quan ingame rằng đầu vào bị vô hiệu hóa, bạn nên biến chúng thành một đối tượng hoặc thành phần, vì vậy, để vô hiệu hóa đầu vào, bạn chỉ cần vô hiệu hóa toàn bộ đối tượng, không cần phải đồng bộ hóa chúng trong một số máy trạng thái hoặc phản ứng với một số chỉ báo boolean.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.