Programmablaufzeit konstant halten

BRoll

Bekanntes Mitglied
Hallo Leute, ich wüsste gerne wie oder ob man das überhaupt lösen kann:

Immer wenn ich was programmiere, (besonders bei Spielen) laufen
die auf verschiedenen PCs unterschiedlich schnell,
die Zeitunterschiede sind zudem gewaltig (bei manchen bis zu 5 mal so schnell/langsam).

Ich denke mal das Problem liegt hier:
Java:
public void run ()
			{
				//Run-Methode
				while(true)
				{
					try
					{
						//Pausenzeit
						Thread.sleep (10);
					}
					catch (InterruptedException ex)
					{
					}
					//Neuzeichnen
					repaint();
				}
			}

Anscheinend ist Thread.sleep() für das selbe Programm nicht auf jedem pc gleich lang.
Deshalb hab ich versucht mit einer Zeitmessung das selber zu machen:

Java:
public void run ()
		{
			//Run-Methode
			while(true)
			{
			    double time= System.nanoTime()/10000000;  
			    double time2=0;
			    int i=0;
				//MultiplayerMethode
			    do
			    {
			    	//Programmgeschwindigkeit	
			    	time2=System.nanoTime()/10000000;			    	
			    	i++;
			    }
			    while(time==time2);
			    
			    multiplayer();
				//Neuzeichnen
				repaint();
			}
		}

Aber das hat auch nicht geklappt.

Jetzt weiß ich wirklich nicht wie ich das besser machen könnte,
ein Freund von mir hat gemeint dass man das garnicht umsetzen kann.
Bloß wie machen dass dann andere Spiele die (ungeähr) überall gleich schnell laufen?

Kann mir jemand helfen?
???:L
 
G

Gast2

Gast
Die Sache ist eigentlich ganz einfach. Du definierst dass ein Durchlauf ~x ms dauern soll. Wenn ein Durchlauf kürzer ist legst du den Thread schlafen um auf diese x ms zu kommen.
Das ganze nennt sich gameloop und wird bspw. in diesem Tutorial ganz gut beschrieben:
http://www.java-forum.org/spiele-multimedia-programmierung/54795-quaxli-2d-spiele-tutorial.html

Anscheinend ist Thread.sleep() für das selbe Programm nicht auf jedem pc gleich lang
Doch, ein Thread.sleep(x) dauert auf jedem PC (ungefähr) gleich lang. Nur ein Durchlauf deiner gameloop dauert auf jedem PC unterschiedlich lange ;)
Außerdem ist die genauigkeit der meisten systeme um die 15-20ms, ein Thread.sleep(10) ist also eher ungünstig.
 
Zuletzt bearbeitet von einem Moderator:

BRoll

Bekanntes Mitglied
Ok vielen Dank, werde es demnächst mal probieren.

Hätte ich vielleicht auch durch ne Suche finden können,
aber ich wusste nicht wie/wonach ich suchen soll... ;)
 

BRoll

Bekanntes Mitglied
Hmm ich habs mir jetzt mal angeschaut versteh aber nicht ganz wie ich es umsetzen soll:

Java:
const int FRAMES_PER_SECOND = 25;
    const int SKIP_TICKS = 1000 / FRAMES_PER_SECOND;

    DWORD next_game_tick = GetTickCount();
    // GetTickCount() returns the current number of milliseconds
    // that have elapsed since the system was started

    int sleep_time = 0;

    bool game_is_running = true;

    while( game_is_running ) {
        update_game();
        display_game();

        next_game_tick += SKIP_TICKS;
        sleep_time = next_game_tick - GetTickCount();
        if( sleep_time >= 0 ) {
            Sleep( sleep_time );
        }
        else {
            // Shit, we are running behind!
        }
    }

anscheinend muss man ja GetTickCount() selber schreiben, aber wie genau?
Einfach die aktuellen millisekunden minus die Startmillisekunden?

Und was ist DWORD ???

Sieht logisch aus, aber ich schaffs nicht umzusetzen xD
 

AquaBall

Top Contributor
anscheinend muss man ja GetTickCount() selber schreiben, aber wie genau?
Einfach die aktuellen millisekunden minus die Startmillisekunden?

Korrekt, im Prinzip ja.
Wenn du es noch durch eine Konstante dividierst, dann hast du deine "Zeitscheiben".
Wieviel ms dur für eine Zeitscheibe spendierst oder benötigst gibt vor, wie flüssig das Spiel läuft, und welche Ansprüche du an Hardware stellst.

Und was ist DWORD ???
DWORD ist doubleWord , long int, 32Bit, ist aber letzlich nicht entscheidend, du musst nur einen Typ nehmen der die millesec managen kann: int reicht.
 

Noctarius

Top Contributor
Man kann auch einfach am Anfang der Methode die Zeit festhalten, dann alles updaten / zeichnen / ... und dann die Zeit wieder nehmen. Ist die Differenz Ende-Start < Millis eines Gameticks musst du halt warten.

Java:
public static final GAME_TICK = 1000 / 25; // 25FPS

public void gameloop() {
  for (++) {
    long startTime = System.nanoTime();
    update();
    draw();

    long diff = System.nanoTime() - startTime;
    if (diff < GAME_TICK) {
      try {
        Thread.sleep(GAME_TICK - diff);
      } catch (InterruptedException e) {
        break;
      }
    } else {
      Thread.yield(),
    }
  }

  cleanup();
}
 

tuttle64

Bekanntes Mitglied
Die Sache ist eigentlich ganz einfach. Du definierst dass ein Durchlauf ~x ms dauern soll. Wenn ein Durchlauf kürzer ist legst du den Thread schlafen um auf diese x ms zu kommen.


So trivial ist das nicht, denn nach dem Schlafen wechselt der Thread zum Status Runnable und ob er dann sofort vom Scheduler ausgewählt wird, ist nicht garantiert. Insofern handelt es sich bei der in der Methode sleep() verwendete Zeit und eine Mindestzeit. Bei Spielen muss das kein Problem sein, denn i.d.R. soll ein Spiel nicht zu schnell laufen d.h. beim Verlangsamen ist eine Mindestzeit ok.
 
Zuletzt bearbeitet:

Jdecay

Neues Mitglied
Bin selber ein angehender Informatiker und habe dieses Problem über eine TimerTask umgesetzt.
Also ein Thread, der immer wieder die run() Methode meiner "Spiel" Klasse aufruft und einen "Tick" rechnet. Ob dies eine gängige Lösung ist, kann ich nicht sagen, bei mir funktioniert es :)

Java:
public class MyTimerTask extends TimerTask {
	
	/** Das Game das geTimed werden soll */
	private Game game;

	/**
	 * der Konstruktor der Klasse MyTimerTask
	 * @param theGame die Game Instanz
	 */
	public MyTimerTask(Game theGame) {
		this.game = theGame;
	}

	@Override
	public void run() {
		if(this.game.running){
		this.game.run();
		}else
			this.game.stop();
	}
}

Die eigene Instanz TimerTask wird dann einem Timer per aufruf von schedule() zugewiesen
Java:
java.util.Timer newTimer = new java.util.Timer(true);
		MyTimerTask timerTask = new MyTimerTask(erstesLevel);
		newTimer.schedule(timerTask, 1000,30);
 
Zuletzt bearbeitet:

Noctarius

Top Contributor
Bin selber ein angehender Informatiker und habe dieses Problem über eine TimerTask umgesetzt.
Also ein Thread, der immer wieder die run() Methode meiner "Spiel" Klasse aufruft und einen "Tick" rechnet. Ob dies eine gängige Lösung ist, kann ich nicht sagen, bei mir funktioniert es :)

Java:
public class MyTimerTask extends TimerTask {
	
	/** Das Game das geTimed werden soll */
	private Game game;

	/**
	 * der Konstruktor der Klasse MyTimerTask
	 * @param theGame die Game Instanz
	 */
	public MyTimerTask(Game theGame) {
		this.game = theGame;
	}

	@Override
	public void run() {
		if(this.game.running){
		this.game.run();
		}else
			this.game.stop();
	}
}

Die eigene Instanz TimerTask wird dann einem Timer per aufruf von schedule() zugewiesen
Java:
java.util.Timer newTimer = new java.util.Timer(true);
		MyTimerTask timerTask = new MyTimerTask(erstesLevel);
		newTimer.schedule(timerTask, 1000,30);
Und wenn dein Task länger als einen Tick läuft? Dann hast du 2 Zeichenprozesse parallel.
 

Noctarius

Top Contributor
Huch den meinte ich auch aber irgendwie war mir nicht bewusst, dass er diese Methode hat. Naja ich arbeite aber auch schon ewig nur noch mit ExecutorServices, Server-Development eben :D
 

Ähnliche Java Themen


Oben