Hochzählende Sekunden/Minuten-Anzeige

Hi,

Ich sitze gerade an einem Snake-Klon und würde dort gerne eine Sekunden/Minutenanzeige (Beispiel: “1:23” meint, 1 Minute und 23 Sekunden) einbauen. Allerdings weiß ich nicht so recht, wie ich dies umsetzen soll.
Zunächst dachte ich einfach, dass ich bei jedem Spielschleifendurchlauf die Systemzeit messe und vergleiche und falls die Differenz größer/gleich 1 Sekunde ist, wird die Anzeige um 1 Sekunde hochgezählt. Aber das ist ja keineswegs genau. Besonders dann nicht, wenn die Spielschleife für 1 Durchlauf z.B. 5 Sekunden braucht. Dann würde der Sekundenzähler nur alle 5 Sekunden um 1 nach oben gezählt werden. Und wielang die Spielschleife für 1 Durchlauf braucht ist ja auf jedem System wieder anders.

Meine 2. Idee ist nun das ganze in einen seperaten Thread auszulagern. Aber selbst dann wäre es doch eigentlich nicht wirklich genau oder? Ganz zu schweigen davon, wenn ich mir auch die Millisekunden korrekt ausgeben lassen will.

anstatt die aktuelle zeit mit der letzten irgendwie zu vergleichen und darauf hin irgendwas zu inkrementieren kannst du auch den startzeitpunkt als festen wert nehmen und bei jedem loop die aktuelle differenz in eine zeitangabe umrechnen lassen
so ist es völlig egal wie lange der loop braucht da du immer nur den ständig größer werdenen abstand zwischen startzeitpunkt und jetzt hast was man wiederum in eine normale zeit-anzeige umrechnen kann

[QUOTE=Unregistered]anstatt die aktuelle zeit mit der letzten irgendwie zu vergleichen und darauf hin irgendwas zu inkrementieren kannst du auch den startzeitpunkt als festen wert nehmen und bei jedem loop die aktuelle differenz in eine zeitangabe umrechnen lassen
so ist es völlig egal wie lange der loop braucht da du immer nur den ständig größer werdenen abstand zwischen startzeitpunkt und jetzt hast was man wiederum in eine normale zeit-anzeige umrechnen kann[/QUOTE]

Das muss aber trozdem in einen seperaten Thread ausgelagert werden oder? Weil wenn ich das in der Spielschleife mache und meine Spielschleife z.B. 5 Sekunden pro Durchgang braucht, dann kann ich mir da nicht jede Sekunde die Zeit berechnen lassen.

Eine Spielschleife (Kollisionsprüfung, Postionsbestimmung…) dauert doch keine 5 Sekunden - üblicherweise eher wenige Millisekunden? Daher kann man doch (bzw. macht man üblicherweise auch so) im selben Thread in dem das Spielfeld „berechnet“ wird auch die Zeit bestimmen.

nein, eben nicht !
denn das repaint() darf eh erst am ende des gameloops aufgerufen werden, und wenn der loop nun mal 5 sek braucht dann springt der zeit-counter auch nur alle 5 sek mit um (wobei zugegeben : 5 sec für ein einfaches snake ? da stimmt was nicht)

Ich habe es nun wie folgt umgesetzt (Ist es so korrekt?):

Hier wird jetzt erstmal als Test nur die Differenz jede Sekunde ausgegeben.

long startZeit = System.currentTimeMillis();
long aktuelleZeit = 0;

while(snakeSpiel.gestartet==true) {

//....

aktuelleZeit = System.currentTimeMillis();
			if(aktuelleZeit - startZeit >= 899) {
				System.out.println(aktuelleZeit-startZeit);
				startZeit += aktuelleZeit - startZeit + 100;
			}
			
			GUI.f1.repaint();
			
			
			
			try {
				Thread.sleep(100);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}


}

DIe 899 habe ich genommen, weil ich einmal ja sowieso den Thread 100ms schlafen lege und zum anderen die Spielschleife sicherlich mind. 1ms zum durchlaufen braucht.

Da ist mir nicht klar was Du damit bezweckst. Wieso veränderst Du die startzeit? Merke Dir doch einfach nur wie vorgeschlagen die Startzeit, damit kannst Du jederzeit die Differenz zur aktuellen Zeit berechnen und somit die vergangene Zeit in min:sec angeben.

Ich hab mal ein Beispiel gebastelt aus Spaß an der Freude, wie man es machen könnte. Außerdem erzeugt es hübsche Bilder.


import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;

import javax.swing.JFrame;
import javax.swing.JPanel;

public class ShowGameTime implements Runnable {

    public static void main(String[] args) {
        new ShowGameTime();
    }

    private long startTime;
    private boolean running;
    private MyPanel painted;
    private Random random;
    private JFrame frame;
    private long timeTillEnd;

    public ShowGameTime() {
        startTime = System.currentTimeMillis();
        running = true;
        random = new Random();
        timeTillEnd = 30 * 1000;

        createGui();

        Thread t = new Thread(this);
        t.start();
    }

    public class Circle {
        private int x;
        private int y;
        private int radius;
        Color color;
        public Circle(int x, int y, int radius, Color color) {
            this.x = x;
            this.y = y;
            this.radius = radius;
            this.color = color;
        }
        public int getX() {
            return x;
        }
        public int getY() {
            return y;
        }
        public int getRadius() {
            return radius;
        }
        public Color getColor() {
            return color;
        }
    }

    @SuppressWarnings("serial")
    public class MyPanel extends JPanel {
        private String time = "0:00";
        private List<Circle> circles = new ArrayList<>();
        public void setTime(String time) {
            this.time = time;
        }
        public void createCircle() {
            int width = getWidth();
            int height = getHeight();
            int length = (width > height ? height : width);
            int radius = 25 + random.nextInt(length / 4);
            int x = random.nextInt(width - radius);
            int y = random.nextInt(height - radius);
            int red = random.nextInt(255);
            int green = random.nextInt(255);
            int blue = random.nextInt(255);
            Color color = new Color(red, green, blue);
            Circle circle = new Circle(x, y, radius, color);
            circles.add(circle);
        }
        @Override
        public void paintComponent(Graphics graphics) {
            graphics.setColor(Color.BLACK);
            graphics.fillRect(0, 0, getWidth(), getHeight());

            for (Circle circle : circles) {
                graphics.setColor(circle.getColor());
                graphics.fillOval(circle.getX(), circle.getY(),
                        circle.getRadius(), circle.getRadius());
            }

            graphics.setColor(Color.RED);
            graphics.drawString("Time: " + time, 10, 20);
        }
    }

    private void createGui() {
        frame = new JFrame("Laufzeitanwendung");
        frame.setLayout(new BorderLayout());
        painted = new MyPanel();
        painted.setPreferredSize(new Dimension(800,600));
        frame.add(painted, BorderLayout.CENTER);
        frame.pack();
        frame.setVisible(true);
    }

    @Override
    public void run() {
        while (running) {
            long millisRunned = System.currentTimeMillis() - startTime;
            if (timeTillEnd <= millisRunned) {
                running = false;
            }
            final String time = createTime(millisRunned);
            EventQueue.invokeLater(new Runnable() {
                @Override
                public void run() {
                    painted.createCircle();
                    painted.setTime(time);
                    painted.repaint();
                }
            });
            sleep();
        }
    }

    private String createTime(long millis) {
        int timeInSeconds = ((int) millis) / 1000;
        int seconds = timeInSeconds % 60;
        int timeInMinutes = timeInSeconds / 60;
        int minutes = timeInMinutes % 60;
        int hours = timeInMinutes / 60;
        String time;
        if (hours > 0) {
            time = String.format("%d:%02d:%02d", hours, minutes, seconds);
        }
        else {
            time = String.format("%d:%02d", minutes, seconds);
        }
        return time;
    }

    private void sleep() {
        try {
            Thread.sleep(100);
        }
        catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

}

[QUOTE=Jack159]Das muss aber trozdem in einen seperaten Thread ausgelagert werden oder? Weil wenn ich das in der Spielschleife mache und meine Spielschleife z.B. 5 Sekunden pro Durchgang braucht, dann kann ich mir da nicht jede Sekunde die Zeit berechnen lassen.[/QUOTE]Grundsätzlich gehören genau die Dinge in einen separaten Thread, die nicht innerhalb einer Frametime aktualisiert werden können. Zeitmessungen gehören also nicht dazu, Dinge, die aber 5s bzw. mehr als 41,667ms (FrameTime bei 24fps - minimum für fliessende Bewegung) zur Berechnung brauchen schon.

Also sowas hab ich immer mit dem Timer gelöst :stuck_out_tongue: Da ist es auch ganz egal wo es läuft, er zählt auf jeden Fall nur einmal pro Sekunde hoch

import java.util.Timer;
import java.util.TimerTask;

public class TimerForum {

    private final Timer timer = new Timer();
    private TimerTask timerTask = null;

    //Wenn true, starten und erzeugen wenn er noch nicht läuft
    //Wenn false, canceln, wenn er läuft
    private void setTaskRunning(boolean running) {
        if (running) {
            if (timerTask == null) {
                System.out.println("Scheduling task");
                //Timer erstellen
                timerTask = new TimerForum.ExampleTimerTask();
                //Timer starten, Parameter: Der Task, dann ein Delay (hier 0) und dann, wann der Timer zählen soll
                //Hier nach 1 Sekunde
                timer.schedule(timerTask, 0, 1000);
            } else {
                System.out.println("Task is already scheduled");
            }
        } else {
            if (timerTask != null) {
                System.out.println("Cancelling task");
                timerTask.cancel();
                timerTask = null;
            } else {
                System.out.println("Task is not scheduled");
            }
        }
    }

    private static class ExampleTimerTask extends TimerTask {

        private int counter = 0;

        @Override
        public void run() {
            System.out.println("Task was run " + counter + " times");
            counter++;
        }
    }
    
    public static void main(String[] args) {
        //Startet den Timer
        new TimerForum().setTaskRunning(true);
    }
}