# Bomberman: Bewegung der Spielfigur in windows viel schneller



## MaN_ (27. Feb 2007)

Hi,

Ich programmier momentan an nem Bomberman-clone. Jetzt habe ich gerade festgestellt, dass die Bewegung auf meinem Windows Laptop einiges schneller ist als auf meinem Desktop mit Linux (Kubuntu).

Hier der Thread der die gedrückten tasten aus dem boolean Array bCursorKey[] ausliest und der Figur übergiebt.


```
for (int i = 0; i < 4; i++)
{
	if (bCursorKey[i])
	{					
		switch (i)
		{
		case 0:
			man.moveMan(Man.LEFT);
			break;
		case 1:
			man.moveMan(Man.UP);
			break;
		case 2:
			man.moveMan(Man.RIGHT);
			break;
		case 3:
			man.moveMan(Man.DOWN);
			break;
		}
		
		try{Thread.sleep(50);} catch (InterruptedException e){}	
	}
}
```

Die moveMan Methode:

```
/** speichert die Richtung in eine LinkedBlockingQueue **/
public void moveMan(int dir)
{
	queue.add(dir);
}
```

und hier der Thread der dann die LinkedBlockingQueue ausliest und die Befehle an die Methode move übergibt, der dann die Figur an der neuen stelle zeichnet.


```
public void run()
{
	while(!bStopRequested)
	{
		try 
		{
			move(queue.take());
		}
		catch (InterruptedException e) 
		{
			e.printStackTrace();
		} 
	}
}
```

Ich denke mal das die 50ms (im ersten code-teil) ausreichen sollte das ganze zu zeichnen. bei 100ms ist es auf dem Desktop noch viel langsamer... Kann es sein das die Sleep-Methode so ungenau ist das sie auf dem Laptop einiges kürzer wartet?
Bei 150 Schritten ist der Laptop ca 30 Schritte vorraus. 

Am Prozessor oder Ram kann es nicht liegen. der Desktop hat nen 3800+ und 2GB Ram der Laptop nur nen Centrino 1,6 und 512MB Ram. 
Das einzige wäre die Grafikkarte. im Desktop steckt ne Geforce 4 TI 4400 und im Laptop ne Radeon 9700 Mobility. Also könnte die Zeichenoperation auf dem Laptop deswegen schneller passiert sein. Aber da der Unterschied bei 100ms noch viel höher ist, glaub ich eher das es am sleep liegt. 

Habt ihr mir ein Tipp das ganze gleichschnell hinzubekommen? Im Singleplayer ist das natürlich egal aber im Multiplayer 
ist das ja schon ein bisschen unfair... 

Ich hoffe ihr versteht mein Problem  

Gruß 
MaN


----------



## Tobias (27. Feb 2007)

Das von sleep() sogar eine Variante gibt, die Nanosekunden-genau sein soll, kann ich mir Ungenauigkeiten an dieser Stelle kaum vorstellen. Lass in mal was auf die Konsole schreiben, wenn eine InterruptedException fliegen sollte, vielleicht verhält sich ja an der Stelle etwas unterschiedlich...

mpG
Tobias


----------



## SlaterB (27. Feb 2007)

Im Multiplayer musst du so oder so die Zeit vereinheitlichen,
mit einem einfachen Mittel wird das viel genauer:

statt hardkodiert 50 ms zu warten (und vielleicht noch zusätzliche 20 ms weil ein anderer Thread was macht +5-15ms reine Baerbeitungszeit)
setzt du dir feste Intervalle,
nächster Schritt zu Systemzeit 5535333.300
übernächster Schritt zu Systemzeit 5535333.350
Schritt 3 zu Systemzeit 5535333.400

das sleep ist dann Zielzeit - aktuelle Zeit, 
so kannst du minimale externe Einflüsse sehr genau ausgleichen,

wenn du feststellst, dass mehrere Schritte übersprungen wurden, kannst du gesondert reagieren


----------



## Chris_1980 (27. Feb 2007)

Ich hab allerdings auch das Gefühl das Thread.sleep superungenau ist. 
Ich hab das anhand von System.nanoTime gemessen, wie lang er denn nun wirklich wartet... und bei gewollten 10ms hat er immer im Wechsel n paar durchläufe 7ms gewartet, dann wieder n paar durchläufe 15ms.
Allerdings niemals exakt 10.
Ist jetzt schwer zu sagen, welche Methode ungenau ist, Thread.sleep oder System.nanoTime, ider gar beides :? ...
wobei ich ja schon ganz schwer auf ersteres tippe.  :meld:


----------



## Illuvatar (27. Feb 2007)

Mir kam Thread.sleep unter Windows auch schon oft _äußerst_ ungenau vor - nie wirklich gemessen, aber Thread.sleep(5000) sind bei mir höchstens 4 Sekunden!

==> Mit nanoTime oder currentTimeMillis arbeiten


----------



## Guest (27. Feb 2007)

SlaterB hat gesagt.:
			
		

> Im Multiplayer musst du so oder so die Zeit vereinheitlichen,
> mit einem einfachen Mittel wird das viel genauer:
> 
> statt hardkodiert 50 ms zu warten (und vielleicht noch zusätzliche 20 ms weil ein anderer Thread was macht +5-15ms reine Baerbeitungszeit)
> ...




ich hab jetzt mal den ersten code-Schnippsel umgeschrieben und so ist es einiges genauer, doch jetzt ist der Desktop ein kleines bisschen schneller


```
for (int i = 0; i < 4; i++)
{
	if (bCursorKey[i])
	{	
		long time = System.currentTimeMillis();
		long nextTime = time + 50;
		
		switch (i)
		{
		case 0:
			man.moveMan(Man.LEFT);
			break;
		case 1:
			man.moveMan(Man.UP);
			break;
		case 2:
			man.moveMan(Man.RIGHT);
			break;
		case 3:
			man.moveMan(Man.DOWN);
			break;
                                    }
		
		while(System.currentTimeMillis()<nextTime);
		//try{Thread.sleep(50);} catch (InterruptedException e){}	
	}
}
```

wie soll ich das dann aber im Netzwerk sinvoll machen? alle 50ms übers netzwerk den impuls fürs weitergehen senden? der würde dann ja auch überall zeitversetzt ankommen.


----------



## Illuvatar (27. Feb 2007)

Dennoch ist das sinnvollste, solche Berechnungen auf dem Server zu machen. Aber ich würde hier keinen "Impuls zum Weitergehen" senden, sondern auf dem Server *alles* berechnen, und den Clients immer nur senden, wo alle Spielfiguren sind - die Clients sagen dem Server dafür, in welche Richtung sie gerade gehen wollen.
Ich weiß ja nicht, ob du dein Spiel "groß rausbringen" willst - aber solange du den Client irgendsowas berechnen lässt, können immer die Clients dekompiliert, verändert und wieder kompiliert werden - Cheaterparadies 
Und das ist genau der Punkt - das was Cheater ausnutzen können, kann - und wird - eben auch versehentlich geschehen.

Edit: Was ich noch sagen wollte: Sowas wie deine Zeile 25 ist... nicht so gut  Dadurch verbraucht das Programm 100% der CPU. Mach vielleicht lieber

```
long diff = nextTime - System.currentTimeMillis();
      while(diff >= 0){
            try{Thread.sleep(Math.max(diff / 2, 10);} catch (InterruptedException e){interrupt();}
      }
```


----------



## MaN_ (27. Feb 2007)

ok das mit den Berechnungen am Server durchführen lassen leuchtet mir ein. Das muss ich unbedingt noch einbauen.  

aber dein Code-Snipet versteh ich net ganz... Müsste man nicht in der while schleife diff einen Wert geben, denn sonst wird ja nur einmal diff initialisiert und bleibt dann in der whileschleife hängen. Und wieso übergibst du der Methode sleep Math.max(diff/2,10) und nicht nur diff/2?


----------



## Illuvatar (28. Feb 2007)

Öh ja, das erste stimmt, man müsste diff in der while-Schleife immer neu berechnen lassen.

Das mit dem Math.max mache ich, weil hier im Forum mal einer rausgefunden hat: Wenn man Thread.sleep mit zu kleinen Werten aufruft (10 ist schon hart an der Grenze), kann das die Windows-Systemuhr verstellen. Frag mich bitte nicht warum - mit dem max stell ich eben sicher, dass der Wert nicht zu klein wird.


----------



## Wildcard (28. Feb 2007)

Illuvatar hat gesagt.:
			
		

> Wenn man Thread.sleep mit zu kleinen Werten aufruft (10 ist schon hart an der Grenze), kann das die Windows-Systemuhr verstellen.


Das kann auch bei zu häufigen Aufrufen von System.currentTimeMillis passieren.
Ist AFAIK ein Bug in der Windows API.


----------



## SlaterB (28. Feb 2007)

>      long time = System.currentTimeMillis();
>      long nextTime = time + 50;

>      while(System.currentTimeMillis()<nextTime); 

neben der schon erwähnten 100%-CPU-Last ist das immer noch falsch

nextTime darf nicht von der vorherigen Zeit direkt abhängen,
sondern:

long time = System.currentTimeMillis();
long nextTime = getNextTargetTime(time);


Thread.sleep(nextTime-System.currentTimeMillis());


getNextTargetTime liefert einen festen Slot,
zu 454340566 -> 454340600 
zu 454340576 -> 454340600 
zu 454340596 -> 454340650 (weil 4 ms zu kurz wären)
zu 454340606 -> 454340650 

usw., das kann natürlich berechnet werden, aber nicht stur 50ms drauf,
gerade dann gleichst du ja externe Pausen (andere Windows-Threads) NICHT aus

-------

das hilft nun aber nur, wenn man gleiche Zeitintervalle haben will und ein Auslassen egal ist 
(etwa zur Bildschirmaktualisierung einer Bewegung, die man zu einem beliebigen Zeitpunkt berechnen kann, z.B. eine Uhr-Anzeige),


möchtest du dagegen gleich viele Schritte (z.B. Figurbewegungen), 
dann wäre es ja fatal, einen Slot zu überspringen,

dann würde ich beim Festellen von Abweichungen das Warteintervall anpassen,
-> anderes Programm hat drei Schritte Vorsprung bzw. nach normaler Rechnung sollte zu dieser Zeit im Singleplayer schon 3 Schritte mehr gemacht werden?, 
dann die nächsten 15 Intervalle nur 40ms statt 50 ms warten,

aber das klingt ganz schön abenteuerlich, wahrscheinlich sollte man bei solchen Problemen ganz anders anfangen 
ich rede auch nur theoretisch daher


----------



## Inanis (2. Mrz 2007)

Tag Man_,

versuch doch mal wesentlich mehr Grafik auf den Screen zu spucken, dann merkst ja auch, obs
an der Grafikkarte liegen koennte.

Falls dass das Problem ist, schlage ich dir vor nachzusehen, ob deine Grafikkarte unter Linux ordentlich
unterstuetzt wird. Ansonsten halt nen entsprechenden Driver installieren, was ja heutzutage kein Problem
mehr darstellt, auch wenn sich viele Hardware-Hersteller noch gegen Linux straeuben.
Mit nem frischen Java solltest du dann unter Linux ein recht performantes Spiel bekommen. Ansonsten helfen
eventuell Flags fuer opengl-support.

Bei mir laufen auf derselben Maschine auf der ich ein Linux und ein Windows XP laufen habe, die Javaspiele unter
Linux performanter als unter Windows. Da erkennt man halt wieder das Ressourcenmanagement.


----------



## Guest (2. Mrz 2007)

Hi,

Also meine Treiber sind richtig installiert, läuft ja sogar XGL mit beryl drauf. Ich denke mal es liegt an der Grafikkarte. Der Unterschied zwischen Radeon 9700 und Geforce 4 TI ist halt doch recht arg... Aber es funktioniert jetzt auch eingermaßen!


----------

