Java GameLoop wird stark langsamer und freezt den Canvas

Lacritz

Lieutenant
Registriert
Okt. 2013
Beiträge
920
Heyho,

ist heute mein zweites Thema hier im Forum , hoffe das ist erlaubt da es sich um 2 stark abweichende Themen handelt:

Hier erstmal der Code (einmal der ganzen Game Klasse und darunter dann nocheinmal nur der GameLoop Methode)

Code:
package team14;

import javafx.event.EventHandler;
import javafx.fxml.Initializable;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyEvent;
import javafx.scene.input.MouseEvent;

import java.net.URL;
import java.util.ResourceBundle;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;


public class Game implements Initializable {
    private ExecutorService executor = Executors.newSingleThreadExecutor();
    private Canvas canvas;
    private Map map;
    private int width;
    private int height;
    private boolean running;

    private int mouseX;
    private int mouseY;

    private int characterX = 0;
    private int characterY = 0;

    /*
    Getter implemented
     */

    public int getCharacterX() {
        return characterX;
    }

    public int getCharacterY() {
        return characterY;
    }



    public Game(GraphicsContext graphicsContext, Map map) {
        this.map = map;
        canvas = graphicsContext.getCanvas();
        width = (int) canvas.getWidth();
        height = (int) canvas.getHeight();
        canvas.setFocusTraversable(true);

    }


    @Override
    public void initialize(URL location, ResourceBundle resources) {
    }

    void startGameLoop() {
        executor.execute(() -> {
            long before = System.currentTimeMillis(), now, sleep;
            running = true;

            //noinspection InfiniteLoopStatement

            int i = 0;

            while (running == true) {

                /*
                Components which gonna be Looped
                 */

                Controll();
                paint();

            //    System.out.println(i);
                i++;
                /*
                End of drawing and Controlling -> next FPS
                 */

                now = System.currentTimeMillis();
                sleep = Math.max(0, 40 - (now - before)); // 25 fps
                try {
                    Thread.sleep(sleep);
                } catch (InterruptedException ignored) {
                }

                before = System.currentTimeMillis();
            }
        });
    }

    public void paint() {
        map.Render();
    }

    /*
    Getting the Controll of our Game
     */

    public void Controll() {

        /*
        Getting X and Y Coordinate of our mouse
         */

        canvas.setOnMouseClicked(new EventHandler<MouseEvent>() {
            @Override
            public void handle(MouseEvent event) {
                mouseX = (int) event.getX();
                mouseY = (int) event.getY();
                map.MiningBlock(mouseX, mouseY);
            }

        });

        /*
        Getting our Key's Pressed

        WASD implemented as CharacterControl

         */

        canvas.setOnKeyPressed(new EventHandler<KeyEvent>() {
                                   @Override
                                   public void handle(KeyEvent event) {
                                       if (event.getCode().equals(KeyCode.W)) {
                                           characterY = characterY - 10;
                                       } else if (event.getCode().equals(KeyCode.A)) {
                                           characterX = characterX - 10;
                                       } else if (event.getCode().equals(KeyCode.S)) {
                                           characterY = characterY + 10;
                                       } else if (event.getCode().equals(KeyCode.D)) {
                                           characterY = characterX + 10;
                                       }
                                       if (event.getCode().equals(KeyCode.ESCAPE)) {
                                           System.exit(0);
                                       }
                                   }
                               }

        );


    }




}

Hier der Gameloop:
Code:
   void startGameLoop() {
        executor.execute(() -> {
            long before = System.currentTimeMillis(), now, sleep;
            running = true;

            //noinspection InfiniteLoopStatement

            int i = 0;

            while (running == true) {

                /*
                Components which gonna be Looped
                 */

                Controll();
                paint();

            //    System.out.println(i);
                i++;
                /*
                End of drawing and Controlling -> next FPS
                 */

                now = System.currentTimeMillis();
                sleep = Math.max(0, 40 - (now - before)); // 25 fps
                try {
                    Thread.sleep(sleep);
                } catch (InterruptedException ignored) {
                }

                before = System.currentTimeMillis();
            }
        });
    }

Ich bin derzeitig am rätseln wieso er Freezt - da es prinzipiell schlagartig langsamer wird -> vorher läuft es mit ca. 20-25 Frames per second -> aufeinmal läufts dann aber mit 2-3 Frames und der Canvas lässt sich nicht mehr anklicken - sprich auch keine Aktionen werden ausgeführt mit der Maus, das ganze lässt sich aber merkwürdiger Weise mit dem ESC Button schließen (ist im KeyListener)

Hoffe ihr könnt mir mit Rat beistehen!

Mit freundlichen Grüßen Lacritz
 
Zuletzt bearbeitet:
Werf doch am besten einfach mal einen Profiler drauf, dann solltest du schonmal sehen, in welchen Teilen der Anwendung die meiste Zeit verbraten wird und ob du ggf irgendwo zuviel Speicher verwendest.

Warum setzt du eigentlich immer wieder die Handler auf dein Canvas neu?
 
hab ich gerade auch gemerkt , der ist mittlerweile in den Constructor verschwunden

Wenn ich wüsste was ein Profiler ist, würde ich das glatt tun -> leider ist ein informatik Studium auch nicht das was es einmal war... nichtmal der Debugger kam in der Uni dran
 
Endlich mal Java-Code, der auch Lambdas verwendet. Coole Sache. Sorry, musste einfach mal meine Begeisterung ausdrücken. Im Büro bin ich gezwungen, noch Java6 zu programmieren :D

Ich denke, das Problem liegt hier:

sleep = Math.max(0, 40 - (now - before));

now ist bei Dir immer kleiner als before, dementsprechend ist der Ausdruck now-before immer negativ. Damit wird der Thread.sleep kontinuierlich immer größer...

Was hälst von der Idee/Analyse?
 
Warum ist now kleiner als before? now wird direkt davor gesetzt und ist entsprechend eigentlich immer höher als das im letzten durchlauf gesetzte before.
 
characterY = characterX + 10;
das scheint schonmal nicht zu stimmen, auch wenns dein problem nicht löst
vielleicht solltest du dir auch mal das switch statement anschauen und nicht eine elend lange if else if Konstruktion wählen
 
Also am Loop an sich kann es nicht liegen wie ich gerade gemerkt habe, lasse ich paint() weg - wird er in unregelmäßigen abständen zwar für 10 frames etwas langsamer , aber das dürfte sich nicht bemerkbar machen - nach 3000 frames lief er immer noch wie eine 1 (vorher mit der paint()) war bei etwa 300-320 immer Schluss danach lief er mit 3-4frames pro Sekunde weiter aber eben nicht mit 25 und vor allem lies sich der canvas nicht "abbauen"
Ergänzung ()

das sind lediglich die Koordinaten des Characters auf der X/Y achse, daran dürfte es nicht liegen da es derzeitig tote Variablen sind
 
Könntest du mir einen Link , dass ich auch das richtige finde / benutze - da wie bereits gesagt ich nichtmal weiß was ein Profiler ist (lese mich derzeitig kurz ein)
 
Ich benutze derzeitig IntelliJ - werd aber schnell mal schauen
 
Vermutung: hat was mit der Art zu tun wie paint() arbeitet.
http://www.oracle.com/technetwork/java/painting-140037.html

repaint() update() usw... ? Du rufst auch paint() auf, was man nicht machen sollte.

Programs may trigger a future call to paint() by invoking repaint(), but shouldn't call paint() directly.

On components with complex output, repaint() should be invoked with arguments which define only the rectangle that needs updating, rather than the no-arg version, which causes the entire component to be repainted.

Sleepen ist vielleicht auch nicht die sinnvollste Lösung, da einem schlafenden Prozess durchaus die CPU Zuweisung vom Betriebssystem genommen werden kann.

Ein Timer vielleicht, der im Intervall tickt ? Gibt ja eine Timer Klasse, bietet sich für FPS praktisch an.
Oder ganz simpel, per lockout Variable (aktives warten = schleife iterieren), das kostet Rechenzeit, aber das Programm wird sicher nicht vom Betriebssystem ausgeknockt :D
 
Zuletzt bearbeitet:
Ich arbeite ja mit JavaFx - stimmt derzeitig übermalt er alles und cleart die Rec. die angeklickt wurden! - versuche gleich mal ob es helfen würde nur das zu updaten was verändert wurde
 
Zurück
Oben