游戏状态管理(游戏,菜单,标题屏幕等)


11

基本上,到目前为止,在我制作的每个游戏中,我总是有一个变量,例如“ current_state”,可以是“ game”,“ titlescreen”,“ gameoverscreen”等。

然后在我的Update函数上,我有一个庞大的功能:

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

但是,我觉得这不是处理状态的专业/干净方法。关于如何更好地执行此操作的任何想法?还是这是标准方式?


您使用哪种语言,框架等?
彼得·阿卜杜林

通常是Lua + LOVE。我还发现不同的框架具有不同的处理方式。SFML似乎有一个非常不错的Screen类。
大卫·戈麦斯

1
您是否研究过状态机?
达拉卡拉

1
您也可以在右上方的搜索栏中查找游戏状态。应该给出一些结果。
TravisG 2012年

必须第二次加入Darcara-这似乎正是State Machine的用途。
balajeerc 2012年

Answers:


14

由于您在谈论屏幕,因此我觉得最好将所有逻辑分离到不同的屏幕中。我通常做什么:

定义一个名为screen的接口,并有多个屏幕实现它。像LoadingScreen,MainMenuScreen,GameScreen,GameOverScreen,HighScoreScreen等。在游戏中,您放置了一个保存当前屏幕的变量。在每个循环中,您调用screen.update()并渲染当前屏幕。因为您的状态是由当前屏幕定义的,所以这将为您节省很多“如果此状态就可以这样做”。

这将很好地分隔您的逻辑。

示例代码:

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

或者根据您的游戏设置,您的游戏可能会陷入无限循环。

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

5

如果您已经在使用Middleclass,那么还有一个很棒的状态机库与它一起被称为Statefull。它易于使用,并且提出了与Matsemann提出的相同的想法。


2

如果您的current_state变量是字符串,那么在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

我所做的大致如下:

我有一个有向无环图数据结构,它实际上只是一堆相互指向的节点。每个节点代表一个游戏系统。例如,UI,世界,输入,渲染。每个节点都指向之前或之后的其他节点。一旦所有节点都就位,就很容易将其展平为一个简单列表。设置此DAG是我在游戏启动期间要做的第一件事。每当我想添加一个新系统(例如AI)时,我都可以说编写代码,然后告诉我的游戏它依赖于什么以及应该依赖于什么。

我的主要游戏循环在那之后,并且仅按顺序运行每个系统。首先处理输入,然后更新世界,然后进行其他操作... UI即将结束,渲染最后。当游戏首次开始时,没有世界,物理学或人工智能,因此基本上跳过了这些步骤,仅显示了标题屏幕。当您正确启动游戏时,UI会向世界系统发送一条消息以将其打开,并且它会自行处理。管理游戏状态仅意味着打开和关闭各种系统。每个系统都有自己的一组状态信息,这些状态信息或多或少地独立于所有其他状态进行处理(这并非完全实际上,许多系统都作用于同一组数据-例如,UI系统从世界中获取数据以显示信息。AI系统还需要查看并向世界上的实体发送消息。


此答案是对其他问题的很好答案。
Matsemann

怎么会这样?他问如何设置他的各种游戏状态,而我的解决方案不是像现在那样使用状态机,而是将这些位拆分成各种系统,这些系统不是状态机而是DAG。
亚历克斯·艾姆斯

1

这是我在Lua + Love2d中组织状态的方式。它避免了长的if / then语句。

首先,我创建一个包含update(dt)和render()方法的基本类。您还可以为其提供事件处理方法,例如onKeyDown(key)。我将此类称为Stage,但是实现该方法的任何对象都可以使用。然后,为每个游戏状态创建该类的实例,实现必要的方法。然后,使用状态名称和状态实例创建键/值表。然后在全局范围内跟踪currentState,以便在满足特定条件时各州可以对其进行更改。

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

好吧,虽然不是很漂亮,但可以通过这种方式处理状态,IMO。您可以使用每种状态的函数来使其更加清洁,例如:

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

或其他问题困扰您(我的意思是,除了Update方法很长之外)?

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.