Ich habe in einigen anderen einfachen 2D-Spielen gesehen, dass eine "Tick" -Methode verwendet wird, um Spielelogik und Grafik-Rendering zu synchronisieren. Mein Hauptgrund für die Verwendung ist eine Fehlfunktion meiner Kollisionserkennung, da die Player-Entität vollständig durch feste Objekte läuft, es sei denn, ich tippe fast pixelweise auf die Steuerelemente, um die Rechtecke zu kollidieren.
Ich gehe davon aus, dass eine Tick-Methode die schnelle Aktualisierung der Spiele so verlangsamt, dass diese leichten Kollisionserkennungen erfasst werden.
Wie erstelle ich eine solche Methode? Ich habe den Code ausprobiert, den Sie unten sehen können. Er führt jedoch dazu, dass das gesamte Spiel einfriert:
Die Aktualisierungsmethode:
@Override
public void update(float deltaTime) {
List<TouchEvent> touchEvents = game.getInput().getTouchEvents();
long lastTime = 0;
long currentTime = 0;
if (state == GameState.Ready) {
updateReady(touchEvents);
}
while (state == GameState.Running) {
lastTime = currentTime;
currentTime = System.currentTimeMillis();
deltaTime = currentTime - lastTime;
player.update(touchEvents, deltaTime);
repaint(deltaTime);
}
if (state == GameState.Paused) {
updatePaused(touchEvents);
}
if (state == GameState.GameOver) {
updateGameOver(touchEvents);
}
}
Die Repaint-Methode, die aktualisiert wird, während die reguläre Paint-Methode nichts bewirkt. Ich habe die while-Schleife mit beiden ausprobiert, scheint nicht aufzuhören zu frieren:
public void repaint(float deltaTime) {
Graphics g = game.getGraphics();
if(deltaTime >= 60) {
g.drawRect(0, 0, 805, 485, color.black);
map.loadMap(getXScroll(), getYScroll(), g, game);
map.loadEntities(getXScroll(), getYScroll(), g, game, map);
g.drawImage(Assets.bitSoldier, 448, 256);
g.drawImage(Assets.dpad, 0, 280);
// Secondly, draw the UI above the game elements.
if (state == GameState.Ready)
drawReadyUI();
if (state == GameState.Running)
drawRunningUI();
if (state == GameState.Paused)
drawPausedUI();
if (state == GameState.GameOver)
drawGameOverUI();
}
}
Antworten:
Im Allgemeinen gibt es zwei Möglichkeiten, die Spielelogik zum "Ticken" zu bringen:
Beide Ansätze haben ihre Vor- und Nachteile, je nachdem, was Sie erreichen möchten.
Für Bewegungen und dergleichen sind dynamische Zeitschritte möglicherweise besser, aber für Dinge wie das Animieren von Pixelgrafiken sind feste Zeitschritte möglicherweise interessanter. Sie können beide nach Belieben kombinieren.
Im Allgemeinen sollten Sie, wie in den Kommentaren erwähnt, Ihr Programm niemals in der Spieleschleife schlafen lassen. Dies verlangsamt das Spiel ohne Grund und kann sogar zu Stottern führen, obwohl der Computer leistungsstark genug ist, um das Spiel tatsächlich ohne Verlangsamung auszuführen.
Ihre generische Spieleschleife sollte ungefähr so aussehen (Pseudocode):
Dies zu tun - egal ob Sie feste Zeitschritte verwenden oder nicht - hat den Vorteil, dass Ihr Spiel immer mit der gleichen Geschwindigkeit läuft, egal ob Sie mit 60 fps, 100 fps, 5000 fps oder nur 15 fps laufen fps.
Der einzige Grund, den Schlaf zu erzwingen, könnte darin bestehen, Batterie zu sparen (z. B. Handyspiele), aber ich würde auch hier nicht für eine feste Zeit schlafen, sondern nur basierend auf der Zeit, die diese Iteration dauerte, und der Zeit, die Sie für einen Frame geplant haben. Wenn das Aktualisieren Ihres Spiels und Zeichnens beispielsweise 10 ms dauert und Sie mit 60 Bildern pro Sekunde arbeiten möchten, schlafen Sie 5 ms (insgesamt 15 ms; die perfekte Zeit wäre 16,67 ms, aber Sie lassen etwas Platz für andere Dinge wie den Kontext Schalter und dergleichen).
quelle
time_per_timestep = 1000.0 / 100;
.update()
und falsch verstandenrepaint()
. Diese funktionieren im Wesentlichen bereits so, wie ich es in meinem Beispiel beschrieben habe (dies sind im WesentlichenupdateDynamicStep()
unddrawStuff()
). Sie versuchen im Wesentlichen, eine Hauptschleife in einer Hauptschleife zu verschachteln, was nicht funktioniert. Sie müssenupdate()
das Framework verlassen, um Ereignisse neu zu zeichnen und zu akzeptieren.