Quản lý trạng thái trò chơi (Trò chơi, Menu, Màn hình tiêu đề, v.v.)


11

Về cơ bản, trong mỗi trò chơi tôi đã thực hiện cho đến nay, tôi luôn có một biến như "current_state", có thể là "trò chơi", "màn hình tiêu đề", "màn hình trò chơi", v.v.

Và sau đó trên chức năng Cập nhật của tôi, tôi có một số lượng lớn:

if current_state == "game" 
  game stuf
  ...
else if current_state == "titlescreen"
  ...

Tuy nhiên, tôi không cảm thấy như đây là một cách xử lý trạng thái chuyên nghiệp / sạch sẽ. Bất kỳ ý tưởng về cách làm điều này một cách tốt hơn? Hay đây là cách tiêu chuẩn?


Những ngôn ngữ, khuôn khổ, vv bạn đang sử dụng?
Petr Abdulin

Thường là Lua + YÊU. Tôi cũng chỉ phát hiện ra rằng các khung khác nhau có cách xử lý khác nhau. SFML dường như có một lớp Screen rất đẹp.
David Gomes

1
Bạn đã nhìn vào máy nhà nước?
Darcara

1
Bạn cũng có thể tìm kiếm các trò chơi trong thanh tìm kiếm ở phía trên bên phải. Nên cho một số kết quả.
TravisG

Phải thứ hai Darcara - đây có vẻ chính xác là những gì State Machines được sử dụng cho.
balajeerc

Câu trả lời:


14

Vì bạn đang nói về màn hình, tôi cảm thấy tốt nhất nên tách tất cả logic đó thành các Màn hình khác nhau. Những gì tôi thường làm:

Xác định một giao diện được gọi là màn hình và có nhiều màn hình thực hiện nó. Giống như LoadingScreen, MainMothyScreen, GameScreen, GameOverScreen, HighScoreScreen, v.v. Trong trò chơi của bạn, bạn đặt một biến giữ màn hình hiện tại. Mỗi vòng lặp, bạn gọi screen.update () và hiển thị màn hình hiện tại. Điều này sẽ giúp bạn tiết kiệm rất nhiều "nếu trạng thái này làm điều đó" vì trạng thái của bạn được xác định bởi màn hình hiện tại.

Điều này sẽ tách logic của bạn rất độc đáo.

Mã ví dụ:

### Screen interface ###
public interface Screen {

    public void show();

    public void update(float delta);

    public void render(float delta);

    public void hide ();
}

### An implementation of screen ###
public class MainMenuScreen implements Screen {

    private Game game;

    public MainMenuScreen(Game game) {
        this.game = game;
    }

    public void show() {
        // init stuff
    }

    public void update(float delta) {
        // react to clicks, update animations etc.
        if (buttonwasclicked) {
            game.setScreen(new GameScreen(game)); // change the screen
        }
    }

    public void render(float delta) {
        // draw everything
    }

    public void hide() {
        // release all resources, as the screen is being hidden
    }
}

### Game, drawing the appropriate screen ###
public class Game {

    public Screen screen;

    public void update() {
        screen.update(getDeltaTime);
        screen.render();
    }

    public void setScreen(Screen screen) {
        this.screen.hide();

        this.screen = screen;
        this.screen.show();
    }
}

Hoặc tùy thuộc vào thiết lập trò chơi của bạn, bạn có thể có một vòng lặp vô hạn như trò chơi của bạn.

while(true) {
    calculatetimesincelastframe()
    screen.update(time);
    screen.render(time);
}

5

Nếu bạn đã sử dụng Middleclass, có một thư viện máy trạng thái tuyệt vời để đi cùng với nó được gọi là Statefull . Thật dễ dàng để sử dụng và thực hiện những ý tưởng tương tự mà Matsemann đề xuất.


2

Nếu current_statebiến của bạn là một chuỗi, thì điều này thực sự dễ dàng trong Lua:

game_states = {}
function game_states.game()
    -- game stuff
end
function game_states.titlescreen()
    -- title screen stuff
end

-- then, inside the Update function:
game_states[current_state]()

1

Những gì tôi làm là đại khái như sau:

Tôi có một đồ thị theo chu kỳ cấu trúc dữ liệu , về cơ bản chỉ là một loạt các nút trỏ vào nhau. Mỗi nút đại diện cho một hệ thống trò chơi. ví dụ: UI, thế giới, đầu vào, kết xuất. Và mỗi nút chỉ vào các nút khác đến trước hoặc sau nó. Khi tất cả các nút được đặt đúng chỗ, thật dễ dàng để làm phẳng nó thành một danh sách đơn giản. Thiết lập DAG này là điều đầu tiên tôi làm trong quá trình khởi động trò chơi. Bất cứ khi nào tôi muốn thêm một hệ thống mới, nói AI, tôi chỉ có thể nói viết mã đó sau đó nói cho trò chơi của tôi biết nó phụ thuộc vào cái gì và cái gì nên phụ thuộc vào nó.

Vòng lặp trò chơi chính của tôi xuất hiện sau đó và chỉ cần chạy từng hệ thống theo thứ tự. Đầu vào đầu tiên được xử lý, sau đó cập nhật thế giới, sau đó các công cụ khác ... UI ở gần cuối và kết xuất là cuối cùng. Khi trò chơi bắt đầu, không có thế giới hay vật lý hay AI, vì vậy những bước đó về cơ bản bị bỏ qua và chỉ màn hình tiêu đề được hiển thị. Khi bạn bắt đầu trò chơi đúng cách, UI sẽ gửi một thông báo tới hệ thống thế giới để bật và nó chỉ tự chăm sóc bản thân. Quản lý trạng thái trò chơi chỉ có nghĩa là bật và tắt các hệ thống khác nhau. Mỗi hệ thống có tập thông tin trạng thái riêng được xử lý độc lập ít nhiều với tất cả các hệ thống khác (Không phải vậy hoàn toànđúng thực tế, nhiều hệ thống hoạt động trên cùng một bộ dữ liệu - ví dụ hệ thống UI lấy dữ liệu từ thế giới để hiển thị thông tin chẳng hạn. Hệ thống AI cũng cần xem xét và gửi tin nhắn đến các thực thể trên thế giới).


Câu trả lời này là một câu trả lời tốt cho một câu hỏi khác .
Matsemann

Làm sao vậy Anh ấy hỏi làm thế nào để thiết lập các trạng thái trò chơi khác nhau của mình và giải pháp của tôi không sử dụng máy trạng thái như bây giờ, mà thay vào đó, chia các bit thành các hệ thống khác nhau không phải là máy trạng thái mà là DAG.
Alex Ames

1

Đây là cách tôi tổ chức các trạng thái của mình trong Lua + Love2d. Nó tránh các câu lệnh if / then dài.

Đầu tiên, tôi tạo một lớp cơ bản chứa các phương thức update (dt) và render (). Bạn cũng có thể cung cấp cho nó các phương thức xử lý sự kiện, như onKeyDown (khóa). Tôi gọi lớp Giai đoạn này, nhưng bất kỳ đối tượng nào thực hiện các phương thức sẽ hoạt động. Sau đó, tôi tạo một thể hiện của lớp đó cho từng trạng thái trò chơi, thực hiện các phương thức cần thiết. Sau đó tôi tạo một bảng khóa / giá trị với tên của trạng thái và thể hiện của trạng thái. Sau đó theo dõi currentState ở phạm vi toàn cầu để các trạng thái có thể thay đổi nó khi một điều kiện nhất định được đáp ứng.

states = {}
states["title"] = title   -- Where title implements Stage class.
states["game"] = game     -- You could create the instance of 'game' lazily too.
currentState = "title"

function love.update(dt)
    if states[currentState] ~= nil then
       states[currentState]:update(dt) 
    end
end

-1

Chà, mặc dù nó không đẹp nhưng vẫn ổn để xử lý các trạng thái theo cách này, IMO. Bạn có thể làm cho nó sạch hơn nhiều bằng cách sử dụng các chức năng cho từng trạng thái, như:

if current_state == "game" 
  game()
else if current_state == "titlescreen"
  titlescreen()

hoặc một cái gì đó làm phiền bạn trong cách tiếp cận này (ý tôi là ngoại trừ phương pháp Cập nhật rất dài)?

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.