Kỹ thuật quản lý đầu vào trong các trò chơi lớn


16

Có một kỹ thuật tiêu chuẩn để quản lý đầu vào trong các trò chơi lớn. Hiện tại, trong dự án của tôi, tất cả xử lý đầu vào được thực hiện trong vòng lặp trò chơi, như vậy:

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

(Tôi đang sử dụng SDL, nhưng tôi hy vọng thực tế chính cũng áp dụng các thư viện và khung công tác). Đối với một dự án lớn, đây dường như không phải là giải pháp tốt nhất. Tôi có thể có một vài đối tượng muốn biết những gì người dùng đã nhấn, vì vậy sẽ có ý nghĩa hơn đối với những đối tượng đó để xử lý đầu vào. Tuy nhiên, tất cả chúng không thể xử lý đầu vào, vì sau khi một sự kiện, nó sẽ bị đẩy ra khỏi bộ đệm sự kiện, vì vậy một đối tượng khác sẽ không nhận được đầu vào đó. Phương pháp nào được sử dụng phổ biến nhất để chống lại điều này?


Với người quản lý sự kiện, bạn có thể kích hoạt sự kiện trong đầu vào và để tất cả các phần khác trong trò chơi của bạn đăng ký với họ.
danijar

@danijar Ý của bạn chính xác là gì bởi một người quản lý sự kiện, có thể nếu bạn có thể cung cấp một số mã giả bộ xương để hiển thị loại điều bạn đang nói về?
w4etwetewtwet


1
Tôi đã viết một câu trả lời để giải thích chi tiết về các nhà quản lý sự kiện, đó là cách để xử lý đầu vào cho tôi.
danijar

Câu trả lời:


12

Kể từ khi được hỏi bởi người bắt đầu chủ đề, tôi giải thích về các nhà quản lý sự kiện. Tôi nghĩ rằng đây là một cách tốt để xử lý đầu vào trong một trò chơi.

Trình quản lý sự kiện là một lớp toàn cầu cho phép cả hai đăng ký các hàm gọi lại vào các khóa và loại bỏ các cuộc gọi lại đó. Người quản lý sự kiện lưu trữ các chức năng đã đăng ký trong một danh sách riêng được nhóm theo khóa của họ. Mỗi khi một khóa được kích hoạt, tất cả các cuộc gọi lại đã đăng ký sẽ được thực thi.

Các cuộc gọi lại có thể là std::functioncác đối tượng có thể giữ lambdas. Các phím có thể là chuỗi. Vì trình quản lý là toàn cầu, các thành phần trong ứng dụng của bạn có thể đăng ký các khóa được bắn từ các thành phần khác.

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

Bạn thậm chí có thể mở rộng trình quản lý sự kiện này để cho phép truyền các giá trị dưới dạng đối số bổ sung. Các mẫu C ++ rất tốt cho việc này. Bạn có thể sử dụng một hệ thống như vậy để nói rằng, đối với một "WindowResize"sự kiện vượt qua kích thước cửa sổ mới, để các thành phần nghe không cần phải tự tìm nạp nó. Điều này có thể làm giảm sự phụ thuộc mã khá một chút.

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

Tôi đã triển khai một trình quản lý sự kiện như vậy cho trò chơi của mình. Nếu bạn quan tâm, tôi sẽ đăng liên kết tới mã ở đây.

Sử dụng trình quản lý sự kiện, bạn có thể dễ dàng phát thông tin đầu vào trong ứng dụng của mình. Hơn nữa, điều này cho phép một cách tốt đẹp để cho phép người dùng tùy chỉnh các ràng buộc chính. Các thành phần lắng nghe các sự kiện ngữ nghĩa thay vì các phím trực tiếp ( "PlayerJump"thay vì "KeyPressedSpace"). Sau đó, bạn có thể có một thành phần ánh xạ đầu vào lắng nghe "KeyPressedSpace"và kích hoạt bất kỳ hành động nào mà người dùng ràng buộc với khóa đó.


4
Câu trả lời tuyệt vời, cảm ơn. Mặc dù tôi rất thích xem mã, tôi không muốn sao chép nó, vì vậy tôi sẽ không yêu cầu bạn đăng nó cho đến khi tôi tự thực hiện, vì tôi sẽ tìm hiểu thêm theo cách đó.
w4etwetewtwet

Tôi chỉ nghĩ về một cái gì đó, tôi có thể vượt qua bất kỳ chức năng thành viên nào như thế này không, hoặc chức năng đăng ký sẽ phải lấy AClass :: func, giới hạn nó với một chức năng thành viên của lớp
w4etwetewtwet

Đó là điều tuyệt vời về biểu thức lambda trong C ++, bạn có thể chỉ định mệnh đề chụp [=]và tham chiếu đến tất cả các biến cục bộ được truy cập từ lambda sẽ được sao chép qua. Vì vậy, bạn không cần phải vượt qua một con trỏ hoặc một cái gì đó như thế này. Nhưng lưu ý rằng bạn không thể lưu trữ lambdas với mệnh đề bắt trong các con trỏ hàm C cũ . Tuy nhiên, C ++ std::functionhoạt động tốt.
danijar

chức năng std :: rất chậm
TheStatehz

22

Chia cái này thành nhiều lớp

Ở lớp thấp nhất, bạn có các sự kiện đầu vào thô từ HĐH. Đầu vào bàn phím SDL, đầu vào chuột, đầu vào cần điều khiển, v.v. Bạn có thể có một số nền tảng (SDL là mẫu số ít phổ biến nhất, thiếu một số biểu mẫu đầu vào, ví dụ, sau này bạn có thể quan tâm).

Bạn có thể trừu tượng những thứ này bằng một loại sự kiện tùy chỉnh ở mức độ rất thấp, như "nút bàn phím xuống" hoặc tương tự. Khi lớp nền tảng của bạn (vòng lặp trò chơi SDL) nhận đầu vào, nó sẽ tạo các sự kiện cấp thấp này và sau đó chuyển tiếp chúng đến trình quản lý đầu vào. Nó có thể thực hiện những điều này với các cuộc gọi phương thức đơn giản, các hàm gọi lại, một hệ thống sự kiện phức tạp, bất cứ điều gì bạn thích nhất.

Hệ thống đầu vào hiện có công việc dịch đầu vào cấp thấp thành các sự kiện logic cấp cao. Logic trò chơi hoàn toàn không quan tâm rằng SPACE đã được nhấn. Nó quan tâm rằng JUMP đã được nhấn. Công việc của người quản lý đầu vào là thu thập các sự kiện đầu vào cấp thấp này và tạo các sự kiện đầu vào cấp cao. Có trách nhiệm biết rằng phím cách và nút gamepad 'A' đều ánh xạ tới lệnh logic Nhảy. Nó liên quan đến điều khiển gamepad vs chuột và vân vân. Nó phát ra các sự kiện logic cấp cao càng trừu tượng càng tốt từ các điều khiển cấp thấp (có một số hạn chế ở đây, nhưng bạn có thể trừu tượng hóa mọi thứ hoàn toàn trong trường hợp phổ biến).

Bộ điều khiển nhân vật của bạn sau đó nhận các sự kiện này và xử lý các sự kiện đầu vào cấp cao này để thực sự phản hồi. Lớp nền tảng đã gửi sự kiện "Phím xuống phím cách." Hệ thống đầu vào nhận được điều đó, xem xét các bảng / logic ánh xạ của nó và sau đó gửi sự kiện "Nhấn nút nhảy". Bộ điều khiển logic / nhân vật trong trò chơi nhận được sự kiện đó, kiểm tra xem người chơi có thực sự được phép nhảy hay không, và sau đó phát ra sự kiện "Người chơi nhảy" (hoặc chỉ trực tiếp gây ra một cú nhảy), phần còn lại của logic trò chơi sử dụng để làm bất cứ điều gì .

Bất cứ điều gì phụ thuộc vào logic trò chơi đi vào bộ điều khiển người chơi. Bất cứ điều gì phụ thuộc hệ điều hành đi trong lớp nền tảng. Tất cả phần còn lại đi vào lớp quản lý đầu vào.

Đây là một số nghệ thuật ASCII nghiệp dư để mô tả điều này:

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

Nghệ thuật ASCII tuyệt vời, nhưng không cần thiết, tôi xin lỗi. Tôi đề nghị sử dụng một danh sách đánh số thay thế. Dù sao một câu trả lời tốt!
danijar

1
@danijar: Eh, tôi đã thử nghiệm, chưa bao giờ thử rút ra câu trả lời trước đó. Nhiều công việc hơn nó đáng giá, nhưng công việc ít hơn nhiều so với xử lý một chương trình sơn. :)
Sean Middleditch

Được rồi, dễ hiểu :-)
danijar

8
Cá nhân, tôi thích cách nghệ thuật ASCII hơn là một danh sách đánh số nhàm chán.
Jesse Emond

@JlieEmond Này, ở đây vì nghệ thuật?
danijar
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.