2016-09-06 2 views
1

Ich habe die Grundlagen des Spieldesigns gelernt und habe diese Spielschleife mit Hilfe vieler Tutorials in Java erstellt. Alles, was es jetzt tut, ist auf ein JFrame ein Grasfliese zu machen: es bei einem konstanten 60 FPS, das einzige Problem ist, es frisst läuft, kannSehr hohe CPU-Auslastung mit einfachem Java Game Loop

public class Game extends JPanel implements Runnable { 

String title; 
int height; 
int width; 

boolean ps; 

private int ticks; 
private JPanel p; 
private BufferStrategy bs; 
private Graphics g; 
private boolean running = false; 
private Thread thread; 
private Frame frame; 
private KeyHandler keyHandler; 

public Game(String title, int width, int height){ 
    this.width = width; 
    this.height = height; 
    this.title = title; 
    keyHandler = new KeyHandler(); 
} 

private void init() { 
    TileHandler.init(); 
    frame = new Frame(title, height, width); 
    frame.getFrame().addKeyListener(keyHandler); 
} 

private void tick() { 
    // KeyHandler.tick(); 

    // updates game logic  
} 

private void render() { 
    render(g);  
} 

private void render (Graphics g) { 
    bs = frame.getCanvas().getBufferStrategy(); 
    if(bs == null){ 
     frame.getCanvas().createBufferStrategy(3); 
     return; 
    } 
    g = bs.getDrawGraphics(); 
    g.clearRect(0, 0, width, height); 

    g.drawImage(TileHandler.grass, keyHandler.getPlayerX(), 
    keyHandler.getPlayerY(), null); 

    bs.show(); 
    g.dispose(); 
} 

@Override 
public void run() { 

    int x = 4; 
    init(); 
    int fps = 60; // target 
    double tickTime = 1000000000/fps; // time per 1 frame 
    double delta = 0; 
    long timer = 0; 

    long startTime = System.nanoTime(); 
    long endTime; 

    while (x == 4) { 
     endTime = System.nanoTime(); 
     delta += (endTime - startTime)/tickTime; 
     timer += endTime - startTime; 
     startTime = endTime; 

     if (delta >= 1) { 
      ticks++; 
      render(); 
      delta--; 
     } 
     if(timer >= 1000000000){ 
      ticks = 0; 
      timer = 0; 
     } 
    } 
} 

public int getTicks() { 
    return ticks; 
} 

public synchronized void startThread() { 
    // Start the thread 
    if (!running) {    // if not already 

     running, you can begin 
     running = true; 
     thread = new Thread(this); //Run THIS class on a 

     new thread 
     thread.start(); 
    } 
    else { // safety measure 
     return; 
    } 
} 

public void stopThread() { 
    if (!running) { 
     return; 
    } 
    running = false; 
    try { 
     thread.join(); //Method stops the thread 
    } catch (InterruptedException e) { 
     e.printStackTrace(); 
    } 

} 

}

Es funktioniert gut genug und ich so viel CPU. Im Durchschnitt verbraucht es ungefähr 50% CPU und verlangsamt den Rest meines Computers - Minecraft verwendet ungefähr dasselbe auf meinem Laptop! Ich weiß genau, dass das Problem in der Spielschleife im obigen Code liegt.

Also versuche ich nur zu verstehen, was das verursacht. Ich habe versucht, einen Profiler zu verwenden, aber ich konnte immer noch nicht herausfinden, was das Problem verursacht hat.

+2

Die Schleife wird ständig ausgeführt, um zu sehen, ob sie render() aufrufen muss. Dies wird als Polling bezeichnet und ist normalerweise eine schlechte Idee. Versuchen Sie einen Timer zu verwenden, um stattdessen render() aufzurufen. – James

+0

Auch würde ich Ihnen raten, in JavaFX zu schauen, da es Hardware-Beschleunigung verwendet, ist es viel mehr für die Entwicklung von Spielen geeignet. Es kommt auch mit Klassen wie [AnimationTimer] (https://docs.oracle.com/javase/8/javafx/api/javafx/animation/AnimationTimer.html) und [Zeitleiste] (https://docs.oracle.com /javase/8/javafx/api/javafx/animation/Timeline.html), die Gameloop-Management recht einfach machen. – Jhonny007

Antwort

2

Ok, um die einfachste Antwort zu geben: Ihre Schleife läuft so schnell, wie die CPU damit umgehen kann. Ich nehme an, Sie haben eine CPU mit zwei Kernen, also nutzt der Haupt-Loop-Thread jedes bisschen Rechenleistung, um die Schleife so schnell wie möglich durchlaufen zu können und blockiert damit einen Ihrer beiden Kerne (50%).

Eine Lösung besteht darin, zu berechnen, wie lange jede Iteration dauert und für eine bestimmte Zeit schläft, bis die nächste Iteration beginnen kann. Hier ist ein Pseudo-Code:

while(running) { 
    long startTime = getTime(); 

    update(); 
    render(); 

    long endTime = getTime(); 
    long elapsedTime = endTime - startTime; 
    Thread.sleep(TARGET_FRAME_TIME - elapsedTime); //sleep to yield unneeded CPU usage 
} 

Auch werden Sie nicht bekommen eine sehr gute Leistung aus Swing, wenn Sie nicht eine OpenGL-Bibliothek wie LWJGL oder JOGL verwenden. Ich weiß nicht, was dein Spiel sein wird, aber 60fps ist wahrscheinlich die Obergrenze für Swing.

Ich rate Ihnen, JavaFX für Spiele zu lernen, da es Hardwarebeschleunigung für das Rendern verwendet und 120fps für relativ komplexe Anwendungen verarbeiten kann. Es bietet auch Timeline und AnimationTimer Klassen für Game-Loop-Design.

+0

+1, aber Swing blockiert immer noch manchmal den Rendervorgang, mit ['newt'] (http://jogamp.org/jogl/doc/NEWT-Overview.html) passiert das nicht mehr – elect

+0

Danke für den Hinweis an alle Ich verstehe jetzt viel besser als vorher. Scheint wie eine 2D-Spielebibliothek ist dann der Weg nach vorne! – SlightlyConfused

+0

Wenn Sie mit der Standardbibliothek bleiben wollen, schauen Sie sich JavaFX an. Es ist perfekt für 2D-Spiele, wenn Sie nicht 3rd Party gehen wollen. (Ich kann ein kleiner Fanboy sein) – Jhonny007