# Analyse Spielkonzept + Performanceproblem



## Revenant (16. Dez 2006)

Hi,

wie schonmal hier erwähnt programmiere ich gerade ein SpaceInvader-Spiel. Dabei bin ich fest davon überzeugt, dass Java anderen Hochsprachen wie C++ hinsichtlich der Performance kaum nachsteht, wenn man ein gutes Design wählt. Mein Konzept steht nun inzwischen und kann auch schon getestet werden. Deswegen möchte ich bevor ich weitermache gerne eure Meinung zu meinem Konzept wissen. Ich bin noch nicht so lange dabei hier und jetzt kann man ausserdem auch noch alles ändern.







Das Projekt habe ich als .zip hochgeladen (siehe unten). Wer will darf gerne reinschauen und mal den Code nach Schwachstellen auseinander nehmen (naja eigentlich bitte ich darum  )

Dann habe ich noch ein kleines Problem. Wenn ein Feind vom Spieler zerstört wird, möchte ich die Explosion aus einer Hintergrundexplosion und einer Vordergrundexplosion (animiertes .gif) machen. Die Hintergrundexplosion stellt hierbei ein roter Kreis dar, den ich direkt in Java zeichne. Dieser Kreis wird am Anfang recht schnell größer, am Schluss jedoch immer langsamer und verblasst allmählich. Solange ich den Radius der Explosion klein halte funktioniert das auch, sobald der jedoch recht groß ist ruckelt das Spiel und wird erbärmlich langsam. Wenn ich die Transparenz der Hintergrund-Explosion deaktiviere gibt es keine Probleme mehr. Das will ich jedoch nicht. Ich vermute, dass das Problem bei g.setColor(blabla) liegt. Scheint einfach zu viel Zeit zu kosten. Aber wie kann ich das sonst lösen? Die Transparenz wird ja direkt beim Zeichnen in Abhängigkeit des Radius berechnet.

Die Klasse die diese Explosion darstellt heißt ExplosionBackground und wird von der Sub-Logikklasse Level1 verwaltet.



```
public class ExplosionBackground
{
	int xpos = 0;
    int ypos = 0;
    double radius = 1;
    double radiusStart = 0;
    double x = 0;
    double speed = 1.0;

    /**constructor
     * 
     * @param x - the x coordinate of the explosion
     * @param y - the y coordinate of the explosion
     * @param r - the radius of the explosion
     * @param speed - the speed of the explosion. Its value is proposed to be 0.0 < speed < 1.0 
     */
    ExplosionBackground(int x, int y, int r, double speed)
    {
        xpos = x;
        ypos = y;
        this.x = -Math.sqrt(r);
        radiusStart = r;
        this.speed = speed;
    }

    public void draw(Graphics g)
    {
        // x value of is increasing constantly
    	x+=speed; 
    	// basicalle the radius is calculated like the y value of the equation y = -x² + b
    	// this means the explosion will first grow fast and then slower and slower
    	radius = -(x*x)+radiusStart; 

        // cancel drawing if explosion is at max radius
    	if ((int)radius >= (int)radiusStart) return;
        if (x >= 0) return;

        // set transparent red color, whose transparency is calculated dynamically using the current radius
        g.setColor(new Color(1.0f,0f,0f,1.0f-(float)(radius/radiusStart)));
        g.fillOval(xpos-(int)(radius/2),ypos-(int)(radius/2),(int)radius,(int)radius);
    }

    /**get status of the explosion
     * 
     * @return true if explosion is still drawn false otherwise
     */
    public boolean getAlive()
    {
        if (x < 0) return true; // x < 0 ???
        return false;
    }
}
```

www.badongo.com/file/1877394
Der Entry Point ist die Klasse Ships3


mfg 
Revenant


----------



## Wildcard (17. Dez 2006)

Revenant hat gesagt.:
			
		

> Dabei bin ich fest davon überzeugt, dass Java anderen Hochsprachen wie C++ hinsichtlich der Performance kaum nachsteht, wenn man ein gutes Design wählt.


Gebe ich dir mit Abstrichen recht.
Für State-of-the-Art 3D Shooter ist Java einfach nicht das richtige Werkzeug, da man dazu IMO näher an der Hardware liegen muss. Für normale Anwendungen, kleinere Spiele,... stimme ich dir aber zu.

Zu deinem Process-Flow:
Ist in Punkto Performance nur bedingt aussagekräftig, da es hier tatsächlich auf die Implementierung ankommt.
Wichtig ist zu beachten, dass die standard-Mechanismen von AWT und Swing für Spiele nutzlos sind (ok, Space-Invaders kommt's noch nicht so drauf an...).
Der repaint Mechanismus muss als erstes dran glauben, stattdessen ist hier Active-Rendering von Nöten.
Als zweites muss man sich von Componentes verabschieden, bei Spielen werden eigene 'leichtere' Objekte gebraucht.
Der dritte wichtige Punkt ist das verwenden einer BufferStragety, und oder VolatileImages.

Zu deinem konkreten Problem:
Das müsste ich mir näher ansehen, fehlt mir aber im Moment etwas die Zeit, vielleicht schreibe ich die Tage was dazu.


----------



## LoN_Nemesis (19. Dez 2006)

Für ein 2D SpaceInvaders würde ich mir nicht die Arbeit machen irgendein kompliziertes Rendering Verfahren zu implementieren. Da reicht es eigentlich vollkommen aus Double-Buffering zu betreiben.

Hast du denn Double-Buffering? Wahrscheinlich ja, sonst würde es ja flackern. Zu deinem Explosionsproblem: Du erstellst zwar jedesmal in der draw Methode ein neues Color Objekt, aber ich glaube nicht, dass das so auf die Performance drückt. Die Frage ist viel eher: Wie oft zeichnest du denn neu?

Ich hab es bei meinem Spiel so gemacht, dass ich der Grafik einen eigenen Thread spendiert habe. Dieser ruft alle 40 ms die repaint() Methode auf. Die Spiele Logik (also KI, Schadensberechnungen, Bewegungen, etc) hab ich in einem anderen Thread. Dadurch ist die CPU Belastung sehr gering, da ich nicht dauernd neu male. Mein Spiel ist allerdings rundenbasierend, bei einem schnellen Action Spiel muss man evtl öfter neu malen. Aber so alle 20-25ms sollte auch da locker reichen.


----------



## Revenant (20. Dez 2006)

Geschwindigkeitsprobleme hab ich ansonsten nicht. Mein Thread zeichnet alle 20ms, bzw. er ruft die repaint() Methode auf die dann zeichnet.

Ich bin bisschen weiter gekommen was das Problem angeht, aber irgendwie scheint der Weg in eine Sackgasse zu führen. Wie du schon richtig angezweifelt hast, liegt das Performance Problem nicht beim erstellen einer neuen Farbe. Ich habe nämlich einen Versuch gestartet, bei dem die Farben in einem Array (Klassenvariable) gespeichert werden. Das Array wird logischerweise für alle Explosionen dann nur 1 mal berechnet, und zwar beim erstellen der ersten Explosion im Konstruktor.

Somit kann es nur noch das Zeichnen mit Transparenz sein... ich hab mal gegoogelt und anscheinend liegt es daran, dass Java keine hardwarebeschleunigung unterstütz. Einen Ausweg hab ich aber auch nicht gefunden... sieht wohl so aus, als ob ich vorerst auf Transparenz verzichten muss


----------



## Apo (20. Dez 2006)

naja ich finde die slick libary sehr nett für 2D-Spiele

ist zwar noch in der Entwicklung bzw Verbesserung, aber dank openGL Unterstützung sollte Transparenz usw kein Problem sein. Auch durch die nette Benennung der Klassen bzw deren Implementation muss man nicht viel neu erlernen.
Also ich finds gut


----------



## EgonOlsen (20. Dez 2006)

Revenant hat gesagt.:
			
		

> ....Mein Thread zeichnet alle 20ms, bzw. er ruft die repaint() Methode auf die dann zeichnet.


Das ist ein beliebter Denkfehler. repaint() zeichnet gar nichts neu. Es setzt die Component nur mit auf die Liste der neu zu zeichnenden Elemente. Gezeichnet werden die im Eventthread. D.h. ich kann 1000*repaint() aufrufen und solange der Eventthread meine Malanforderung nicht abholt, resultiert daraus ein einziger Repaint, wenn er es denn mal tut.
D.h. wenn dein Thread alle 20ms repaint() sagt, dann liegt die Vermutung nahe, dass das Malen auch zeitnah und in gleichen Abständen passiert. Garantiert ist das aber nicht. Das kann zu leichtem bis stärkerem Gezuppel führen, wenn der Bildaufbau eben nicht schön gleichmäßig ist.


----------



## Revenant (20. Dez 2006)

jo und genau weil ich mir nicht sicher war hab ichs so formuliert in der Hoffnung es korrigiert mich jemand . Jetzt ist das auch klar... gut, dann liegt das Geruckele aber immer noch daran, dass die Zeichen-Operation zu viel Zeit benötigt, in der dann wohl nicht gezeichnet (repaint() aufgerufen) wird. 

EDIT: Java3D unterstützt anscheinend diese "Hardwarebeschleunigung" (was auch immer genau es ist)... das würde bedeuten dass ich mein 2D Spiel mit einer 3D-API schreiben müsste. Irgendwie bisschen umständlich oO


----------



## EgonOlsen (20. Dez 2006)

Also wenn, dann würde ich eher ein direktes Binding wie LWJGL oder Jogl nehmen, und nicht Java3D, um 2D-Grafik damit zu machen. Aber das sollte nicht nötig sein. Ich habe dein Game mal mit der OpenGL-Pipeline gestartet (-Dsun.java2d.opengl=True), aber dann sehen Menü und das Schiff grenzwertig aus. Ob es damit schneller läuft, kann ich nicht sagen, weil es auch mit der normalen 2D-Pipeline bei mir vernünftig lief.


----------



## Wildcard (21. Dez 2006)

VolatileImages sind Hardwarebeschleunigt.
Falls du dich für einen Vollbildmodus entscheidest kannst du ach Hardwarebeschleunigtes Page Flipping verwenden.


----------



## Revenant (21. Dez 2006)

Zu der Pipeline... leider bin ich noch Anfänger in Java und ne Pipeline oder sowas sagt mir gar nichts. Trotzdem hab ich mal versucht das Game mit deinem Parameter zu starten, EgonOlsen. Auf einem Computer hat man danach gar nichts mehr gesehen (Bild nur schwarz,weiß gestreift) und auf einem anderen war alles gleich (also auch das geruckele).

Was bedeutet das VolatileImages sind Hardwarebeschleunigt. Ich zeichne doch kein Image sondern direkt über Java einen Kreis. 

Bedeuted das dann, dass ich wenn ich transparente Bilder benutze (z.B. die Feinde,Spieler usw.) lieber VolatileImages verwenden sollte?


----------



## Wildcard (21. Dez 2006)

Generell ist händisches aktives Offscreen Rendering mit einem VolatileImage schneller und für Spiele deutlich besser geeignet als die normalen Swing Mechanismen.
Du Zeichnest einfach in ein Offscreen VolatileImage im Grafikkartenspeicher und zeichnest dieses Image dann auf den Screen oder biegst den Graka Pointer entsprechend um (Page-Flipping).


----------



## LoN_Nemesis (21. Dez 2006)

Revenant hat gesagt.:
			
		

> Was bedeutet das VolatileImages sind Hardwarebeschleunigt. Ich zeichne doch kein Image sondern direkt über Java einen Kreis.



Das bedeutet, dass die Bilddaten im Speicher der Grafikkarte abgelegt werden statt im Hauptspeicher, wenn du VolatileImages benutzt. Du zeichnest in Java über ein Graphic Objekt, was im Grunde nichts anderes als eine "Schnittstelle" zu einem Bild ist.



			
				Revenant hat gesagt.:
			
		

> Bedeuted das dann, dass ich wenn ich transparente Bilder benutze (z.B. die Feinde,Spieler usw.) lieber VolatileImages verwenden sollte?



So ganz genau kenne ich mich damit auch nicht aus, aber ich glaube hauptsächlich geht es darum, dass das Bild auf welches man zeichnet (bei Double-Buffering z.B. der backBuffer) hardwarebeschleunigt ist, nicht jedes einzelne Bild.

http://weblogs.java.net/blog/chet/archive/2003/09/volatileimage_q.html 
hier wird es relativ gut erklärt wie ich finde


----------



## Guest (17. Feb 2007)

Wildcard hat gesagt.:
			
		

> Revenant hat gesagt.:
> 
> 
> 
> ...


----------

