Spielstatusverwaltung (Spiel, Menü, Titelbildschirm usw.)

11

Grundsätzlich habe ich in jedem einzelnen Spiel, das ich bisher gemacht habe, immer eine Variable wie "current_state", die "game", "titlecreen", "gameoverscreen" usw. sein kann.

Und dann habe ich auf meiner Update-Funktion eine riesige:

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

Ich bin jedoch nicht der Meinung, dass dies eine professionelle / saubere Art ist, mit Zuständen umzugehen. Irgendwelche Ideen, wie man das besser machen kann? Oder ist das der Standardweg?

David Gomes
quelle
Welche Sprache, welches Framework usw. verwenden Sie?
Petr Abdulin
Normalerweise Lua + LIEBE. Ich habe auch gerade entdeckt, dass verschiedene Frameworks unterschiedliche Möglichkeiten haben, damit umzugehen. SFML scheint eine sehr schöne Screen-Klasse zu haben.
David Gomes
1
Haben Sie sich mit Zustandsautomaten befasst?
Darcara
1
Sie können auch in der Suchleiste oben rechts nach Spielzuständen suchen. Sollte einige Ergebnisse geben.
TravisG
Muss Darcara unterstützen - genau dafür scheinen Staatsmaschinen eingesetzt zu werden.
Balajeerc

Antworten:

14

Da es sich um Bildschirme handelt, ist es meiner Meinung nach am besten, diese gesamte Logik in verschiedene Bildschirme zu unterteilen. Was ich normalerweise mache:

Definieren Sie eine Schnittstelle namens screen und lassen Sie sie von mehreren Bildschirmen implementieren. Wie LoadingScreen, MainMenuScreen, GameScreen, GameOverScreen, HighScoreScreen usw. In Ihrem Spiel fügen Sie eine Variable ein, die den aktuellen Bildschirm enthält. In jeder Schleife rufen Sie screen.update () auf und rendern den aktuellen Bildschirm. Dies erspart Ihnen viel "wenn dieser Status dies tut", da Ihr Status durch den aktuellen Bildschirm definiert wird.

Dies wird Ihre Logik sehr schön trennen.

Beispielcode:

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

Oder abhängig von Ihrem Spiel-Setup haben Sie möglicherweise eine Endlosschleife als Ihr Spiel.

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

Wenn Sie Middleclass bereits verwenden, gibt es eine ausgezeichnete State-Machine-Bibliothek namens Statefull . Es ist einfach zu bedienen und basiert auf denselben Ideen, die Matsemann vorgeschlagen hat.

WuTangTan
quelle
2

Wenn Ihre current_stateVariable eine Zeichenfolge ist, ist dies in Lua wirklich einfach:

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]()
John Calsbeek
quelle
1

Was ich mache, ist ungefähr wie folgt:

Ich habe eine gerichtete azyklische Graphdatenstruktur , die im Wesentlichen nur eine Reihe von Knoten ist, die aufeinander zeigen. Jeder Knoten repräsentiert ein Spielsystem. zB die Benutzeroberfläche, die Welt, die Eingabe, das Rendering. Und jeder Knoten zeigt auf andere Knoten, die davor oder danach kommen. Sobald alle Knoten vorhanden sind, können Sie sie leicht zu einer einfachen Liste zusammenfassen. Das Einrichten dieser DAG ist das erste, was ich beim Start des Spiels mache. Jedes Mal, wenn ich ein neues System hinzufügen möchte, z. B. KI, kann ich einfach sagen, dass ich diesen Code schreibe und dann meinem Spiel sage, wovon es abhängt und was davon abhängen sollte.

Danach kommt meine Hauptspielschleife und führt einfach jedes System der Reihe nach aus. Zuerst werden Eingaben verarbeitet, dann Weltaktualisierungen, dann andere Dinge ... Die Benutzeroberfläche ist fast zu Ende, und das Rendern ist das letzte. Wenn das Spiel zum ersten Mal gestartet wird, gibt es keine Welt, Physik oder KI, daher werden diese Schritte im Wesentlichen übersprungen und nur der Titelbildschirm wird angezeigt. Wenn Sie das Spiel richtig starten, sendet die Benutzeroberfläche eine Nachricht an das Weltsystem, um es einzuschalten, und es kümmert sich nur um sich selbst. Das Verwalten des Spielstatus bedeutet lediglich das Ein- und Ausschalten der verschiedenen Systeme. Jedes System verfügt über einen eigenen Satz von Statusinformationen, die mehr oder weniger unabhängig von allen anderen verarbeitet werden (das ist nicht vollständigTatsächlich handeln viele Systeme mit demselben Datensatz - das UI-System erfasst beispielsweise Daten aus der Welt, um beispielsweise Informationen anzuzeigen. Das KI-System muss auch Nachrichten betrachten und an Entitäten in der Welt senden.

Alex Ames
quelle
Diese Antwort ist eine gute Antwort auf eine andere Frage.
Matsemann
Wie? Er fragte, wie er seine verschiedenen Spielzustände einrichten soll, und meine Lösung besteht nicht darin, eine Zustandsmaschine wie jetzt zu verwenden, sondern die Bits in verschiedene Systeme aufzuteilen, die keine Zustandsmaschine, sondern eine DAG sind.
Alex Ames
1

So organisiere ich meine Staaten in Lua + Love2d. Es vermeidet die langen if / then-Anweisungen.

Zuerst erstelle ich eine Basisklasse, die die Methoden update (dt) und render () enthält. Sie können ihm auch Methoden zur Ereignisbehandlung geben, z. B. onKeyDown (Schlüssel). Ich nenne diese Klasse Stage, aber jedes Objekt, das die Methoden implementiert, funktioniert. Dann erstelle ich für jeden Spielstatus eine Instanz dieser Klasse und implementiere die erforderlichen Methoden. Ich erstelle dann eine Schlüssel- / Wertetabelle mit dem Namen des Staates und der Instanz des Staates. Verfolgen Sie dann den aktuellen Status im globalen Bereich, damit die Status ihn ändern können, wenn eine bestimmte Bedingung erfüllt ist.

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
h4tch
quelle
-1

Nun, obwohl es nicht schön ist, ist es in Ordnung, Zustände so zu behandeln, IMO. Sie können es viel sauberer machen, indem Sie Funktionen für jeden Zustand verwenden, wie zum Beispiel:

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

oder etwas anderes stört Sie bei diesem Ansatz (ich meine, außer dass die Update-Methode sehr lang ist)?

Petr Abdulin
quelle