in einem einfachen Spiel lade ich Bilder folgendermaßen (das ganze Spiel
ist in einer jar-Datei):
private Image foo =
getToolkit().getImage(getClass().getResource("foo.png"));
Das Spiel implementiert Runnable. Die Run-Methode wird aller 40 ms
durchlaufen und ruft ein repaint() auf. In der paint-Methode zeichne ich
Bilder z.B. mit
g2.drawImage(foo, xPos, yPos, this);
Das Ganze läuft auf einem JPanel mit DoubleBuffer ab. Dieses JPanel
liegt in einem JFrame, welches zusätzlich Statusanzeigen zum Spiel enthält.
Das Spiel funktioniert sehr gut, jedoch ist die Performance nicht sehr
beeindruckend (abhängig von Plattform und Java-Version). Es wäre
natürlich schön, wenn das Spiel auch auf langsamen Rechnern flüssig
laufen würde, zumal es wirklich nicht sehr umfangreich ist. Könnt Ihr
mir Tipps geben, wie man die Performance steigern kann?
Jens
> Das Spiel funktioniert sehr gut, jedoch ist die Performance nicht sehr
> beeindruckend (abhängig von Plattform und Java-Version). Es wäre
> natürlich schön, wenn das Spiel auch auf langsamen Rechnern flüssig
> laufen würde, zumal es wirklich nicht sehr umfangreich ist. Könnt Ihr
> mir Tipps geben, wie man die Performance steigern kann?
Um den Eindruck des flüssigeren Laufens zu gewährleisten, würde ich
nicht eine feste Bildwiederholungsrate (deine 40ms) definieren, sondern
die Animationen alle von der Zeit abhängig machen und dann eben so viele
Durchläufe durch die Hauptschleife starten, wie das System hergibt.
Die 40ms kannst du systembedingt eh nicht genau genug einhalten.
Außerdem kann je nach Spiel eventuell so viel Objektmüll erzeugt werden,
dass die Pausen durch die garbage collection spürbar wird. Diesen
Effekt kannst du mit der Kommandozeilenoption "-Xincgc" reduzieren oder
sogar beseitigen.
An dem Verfahren, wie du Bilder lädst und malst ist nichts auszusetzen.
bye
--
Stefan Matthias Aust // "Ist es normal, nur weil alle es tun?" -F4
> Das Spiel implementiert Runnable. Die Run-Methode wird aller 40 ms
> durchlaufen und ruft ein repaint() auf. In der paint-Methode zeichne ich
> Bilder z.B. mit
>
> g2.drawImage(foo, xPos, yPos, this);
Rufe in der run()-Methode nicht repaint() auf, sondern
paint(this.getGraphics()), oder überschreibe die repaint()-Methode
entsprechend, daß nur paint() aufgerufen wird; die Graphics-Referenz kannst
Du von dem Objekt auf dem Du zeichnest mit getGraphics() holen. Dann sollte
alles wesentlich besser laufen; ich hatte bis gestern das Gleiche Problem.
Wie realisiere ich das?
Die run-Methode sieht folgendermaßen aus:
while (remainingTurns > 0)
{
// ... viele Anweisungen
// aktuellen Durchlauf hochzählen
threadCurrentTurn ++;
// neuzeichnen
repaint();
try
{
Thread.sleep(threadSleepingTime);
}
catch (InterruptedException ex)
{
System.err.println("interrupted sleep");
}
}
In einer Zählvariable werden die Durchläufe durch die while-Schleife
gezählt und abhängig davon (per Zufall) neue Bilder erzeugt, die dann
von oben nach unten durch das JPanel wandern.
Damit das anschaulich wird, gebe ich doch mal den Link zum Spieldownload:
http://www.inf.tu-dresden.de/~swt03-35/game/waeschfangen.jar
Start einfach mit java -jar waeschefangen.jar oder Klick.
Jens
>> Um den Eindruck des flüssigeren Laufens zu gewährleisten, würde ich
>> nicht eine feste Bildwiederholungsrate (deine 40ms) definieren,
>> sondern die Animationen alle von der Zeit abhängig machen und dann
>> eben so viele Durchläufe durch die Hauptschleife starten, wie das
>> System hergibt.
>
> Wie realisiere ich das?
Statt per sleep die Geschwindigkeit einzustellen, stellst du z.B. für
deine Wäschestücke eine Bewegungsgleichung der Form h(t) = v * t + h0
(h=Höhe, v=Geschwindigkeit, t=Zeit, h0=Starthöhe) auf. Für t setzt du
dann System.currentTimeMillis ein, abzüglich des Startzeitpunkts. Du
kannst natürlich auch gerne h(t)=a*t^2+v*t verwenden, um etwas
Gravitation mit einzubringen.
Das berechnest du jeweils in der paint-Methode, da der Aufruf des
repaints in der while-Schleife die paint-Methode nicht vorhersagbar
verzögert aufruft, was dann wieder ungleichmäßige Animationen geben
würde. Vielleicht brauchst du dann die while-Schleife gar nicht mehr und
kannst das repaint ans Ende des paint setzen? Musst du mal ausprobieren.
--
Frank Buß, f...@frank-buss.de
http://www.frank-buss.de, http://www.it4-systems.de
C:\java\projects\newsgroup>java -jar waeschefangen.jar
Exception in thread "main" java.util.zip.ZipException: Das System kann die
angegebene Datei nicht finden
at java.util.zip.ZipFile.open(Native Method)
at java.util.zip.ZipFile.<init>(Unknown Source)
at java.util.jar.JarFile.<init>(Unknown Source)
C:\java\projects\newsgroup>java -version
java version "1.3.1_04"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.3.1_04-b02)
Java HotSpot(TM) Client VM (build 1.3.1_04-b02, mixed mode)
A.G.
> C:\java\projects\newsgroup>java -version
> java version "1.3.1_04"
> Java(TM) 2 Runtime Environment, Standard Edition (build 1.3.1_04-b02)
> Java HotSpot(TM) Client VM (build 1.3.1_04-b02, mixed mode)
C:\tmp>java -version
java version "1.4.1_02"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.4.1_02-b06)
Java HotSpot(TM) Client VM (build 1.4.1_02-b06, mixed mode)
Klick -> läuft.
Klick-Starter sind irgendwie 'n bissel krank. Das erste, was
ich dem OS sofort abgewöhne, wenn Java installiert wurde.
Im übrigen ist das kein Argument dafür, dass es per Kdo-Zeile
nicht geht.
Klick & Klack, Drag & Drops etc... - die heile Klicki-Bunti-Welt,
in der GUI's halt mal fix zusammen geklickt werden und Anwendungen
nicht mit Inhalt sondern mit hüpfenden Männchen gänzlich ausgefüllt
sind.
A.G.
Da ist wohl ein "e" zuviel im Befehl, also
java -jar waeschfangen.jar
Jens
> Frank Buss wrote:
>
>> ...
>>
>> Klick -> läuft.
>
>
>
> Klick-Starter sind irgendwie 'n bissel krank. Das erste, was
> ich dem OS sofort abgewöhne, wenn Java installiert wurde.
> Im übrigen ist das kein Argument dafür, dass es per Kdo-Zeile
> nicht geht.
Vielleicht solltest du es angeschaltet lassen - dann läuft das Programm
auch bei dir......
[KlickiBuntiBashi]
Hey, heute ist der Vorabend des Wochenendes.
Niemand kann nur auf Deine Vorlieben Rücksicht nehmen.
Respekt ist eine Tugend, die gerade freitag nachmittag
selbstverständlich sein sollte. Hat Dir jemand
das wöchentliche Release versaut?
Smoooooooooth :)
Das führt bei mir zu starkem Flimmern.
> oder überschreibe die repaint()-Methode
> entsprechend, daß nur paint() aufgerufen wird; die Graphics-Referenz kannst
> Du von dem Objekt auf dem Du zeichnest mit getGraphics() holen. Dann sollte
> alles wesentlich besser laufen; ich hatte bis gestern das Gleiche Problem.
Bei mir ergibt das keine Verbesserung. Ich hatte bereits die
update-Methode so umgeschrieben, dass diese nur paint() aufruft.
Jens
> Bei mir ergibt das keine Verbesserung. Ich hatte bereits die
> update-Methode so umgeschrieben, dass diese nur paint() aufruft.
Du kannst versuchen, in der paint()-Methode Dein eigenes Double-Buffering zu
implementieren. Dann könnte es klappen.
Beispiel:
public void paint(Graphics graphics) {
bild = (BufferedImage) this.createImage(getWidth() , getHeight());
Graphics2D g = bild.createGraphics();
// Hier mit Hilfe von g zeichnen...
graphics.drawImage(bild , 0 , 0);
}
>> Rufe in der run()-Methode nicht repaint() auf, sondern
>> paint(this.getGraphics()),
>
> Das führt bei mir zu starkem Flimmern.
Das ist auch nicht zu empfehlen, da dann nicht aus dem UI-Thread heraus
gezeichnet wird.
Alternativ kannst du dir deine eigene Zeichenschleife mit BufferStrategy
und mehreren Offscreen-Images bauen, die dann per Page-Flip umgeschaltet
werden, wie hier zu sehen:
http://www.frank-buss.de/demo/
> Bei mir ergibt das keine Verbesserung. Ich hatte bereits die
> update-Methode so umgeschrieben, dass diese nur paint() aufruft.
Ich habe eben mal deine SpielPanel-Klasse decompiliert, da du leider
keinen Quelltext beigelegt hast und ein möglicher Grund, neben dem schon
von SMA erwähnten, könnte sein, daß du mehrere Aufrufe auf dem
Graphics2D-Objekt machst. Lege besser ein Offscreen-Image an, zeichne
alles da drauf und dann nur noch dieses Image ins Graphics2D-Objekt.
Das hatte ich auch mal gemacht, aber JPanel macht den DoubleBuffer
bereits selber. Das lässt sich über den Konstruktor einstellen.
Jens
> Das hatte ich auch mal gemacht, aber JPanel macht den DoubleBuffer
> bereits selber. Das lässt sich über den Konstruktor einstellen.
Nein, macht es bei deiner Implementierung nicht, da du fälschlicherweise
die paint-Methode statt die paintComponent-Methode überschrieben hast.
Standardmäßig ist double buffering sowieso immer eingeschaltet (bei Swing)
und mit der paintComponent-Methode kannst du dir dann den manuellen
DoubleBuffer sparen.
Mehr Hintergrundinformationen findest du hier:
http://java.sun.com/products/jfc/tsc/articles/painting/index.html#paint_process
Ganz sicher nicht. Ich sehe schon - die Kdo-zeile
ist nicht das Ding einiger Java-Entwickler.
A.G.
That's was it. Eine etwas undlückliche Bezeichnung. ;-))
Was so ein kleines 'e' alles ausmacht. (Brille putzen..)
A.G.
Warum ist das günstiger? Wenn ich es richtig verstanden habe (was
wahrscheinlich nicht der Fall ist), dann zeichne ich doch bereits auf
einem Buffer, der erst dann angezeigt wird, wenn er fertig ist.
Meinst Du so etwas an den Anfang der paintComponent-Methode zu setzen?
BufferedImage bild = (BufferedImage) this.createImage(getWidth() ,
getHeight());
Graphics2D g2 = bild.createGraphics();
Im weiteren soll dann g2 zum Zeichnen benutzt werden, oder?
Am Ende dann das Bild auf den Parameter g2real zeichnen?
g2real.drawImage(bild , 0 , 0, this);
So habe ich es probiert und sehe keinen Unterschied. Läuft das Spiel
eigentlich bei Dir flüssig? Welche Java-Version, Betriebssystem und
Rechner hast Du?
Jens
Ich habe jetzt einfach die paint-Methode durch paintComponent ersetzt.
Einen Unterschied sehe ich leider nicht. Ich vermute fast, dass das
Problem doch spezifisch für meinen Rechner ist (warum auch immer). Siehe
auch den Thread "Performanceproblem mit J2SDK 1.4.1". Danke Dir trotzdem
für die vielen Hilfestellungen.
Jens
Das habe ich jetzt ebenfalls probiert. Es funktioniert gut und ist die
bessere Variante. Leider läuft das Spiel trotzdem nicht flüssiger. Ein
repaint-Aufruf direkt in paint scheint mir gefährlich, aber das ging
auch, wenn ich vorher ein kurzes sleep einbaue.
Jens
> Warum ist das günstiger? Wenn ich es richtig verstanden habe (was
> wahrscheinlich nicht der Fall ist), dann zeichne ich doch bereits auf
> einem Buffer, der erst dann angezeigt wird, wenn er fertig ist.
Ja, das ist dann dasselbe und du brauchst keinen manuellen Doublebuffer.
> So habe ich es probiert und sehe keinen Unterschied. Läuft das Spiel
> eigentlich bei Dir flüssig? Welche Java-Version, Betriebssystem und
> Rechner hast Du?
Läuft bei mir flüssig mit Suns Java 1.4.1_02 auf Windows XP mit einem 1.5
GHz P4. Allerdings sieht die Bewegung der Teile etwas wie bei Stroboskop-
Licht-Beleuchtung aus, was wahrscheinlich daran liegt, daß du nicht mit dem
Vertical-Retrace synchronisiert. Ich glaube per Page-Flip wird das
automatische mit dem Retrace synchronisiert, was aber nur im Fullscreen-
Modus geht:
http://javaalmanac.com/egs/java.awt/screen_Flip.html
Mittlerweile habe ich das gesamte Spiel so umgeschrieben, dass alle
Ereignisse unabhängig von der Framerate sind. Das Thread.sleep brauche
ich jetzt theoretisch nicht mehr. Ich setze es dennoch ein (1ms), da
Frameraten im vierstelligen Bereich nicht so sinnvoll sind. Jetzt läuft
es unter Java 1.4.1 bei mir besser wie vorher. Danke nochmal für die
Hinweise.
Jens
>
>
Na dann solltest du mal Brille putzen. Es lag an
der überaus glücklichen Bezeichnung des Archives,
die da lautete: waeschfangen.jar statt wie von mir
eingegeben waeschefangen.jar.
Guckst du - lesen bildet und wo ein 'e' zu viel,
da nix gehen wollen.
A.G.
btw: alle meine progs starten auf der kdo-zeile
> Guckst du - lesen bildet und wo ein 'e' zu viel,
> da nix gehen wollen.
Auf die Idee wäre ich gar nicht gekommen, da ich auch viel von der
Kommandozeile arbeite, aber die Tab-Completion eingeschaltet habe und
somit eigentlich nie Dateinamen selbst ausschreiben brauche, mal
abgesehen davon, daß ein Doppelklick in diesem Fall schneller ist.
HKEY_CURRENT_USER\Software\Microsoft\Command Processor\CompletionChar, REG_DWORD auf 9:
http://www.microsoft.com/technet/treeview/default.asp?url=/technet/prodtechnol/windowsserver2003/proddocs/entserver/cmd.asp
Derjenige, der es hochgeladen hat, hat beim umbennen ein "e" vergessen.
Ist mir auch nicht gleich aufgefallen. Streitet Euch deswegen nicht. ;-)
Jens