Ist Java AWT für das Rendern von 2D-Spielen geeignet?

8

[Reposting dieser Frage aus dem Stackoverflow, da darauf hingewiesen wurde, dass sie hier besser passt.]

Ich portiere derzeit meine 2D-Game-Engine nach Java. Ich habe mir einige der Spielbibliotheken angesehen, auf die hier im Stackoverflow verwiesen wird. Diejenigen, die ich mir ansah, waren jedoch ziemlich simpel und gaben nicht einmal an, ob sie Dinge wie Alpha-Transparenz unterstützten. Deshalb entschied ich mich, meinen C ++ - Renderer zu portieren, für den ich die Logik bereits geschrieben hatte.

Dieser Renderer ist ein reiner Software-Renderer, der Kacheln verwendet, um unnötiges erneutes Rendern zu vermeiden. Ich habe die Bildlaufleistung optimiert, indem ich einen "Offscreen-Puffer" erstellt habe, der etwas größer als mein Ausgabebereich ist, und diesen Offscreen-Puffer in jedem Frame auf meine Ausgabe aufgeteilt habe. Auf diese Weise konnte ich vermeiden, dass Kacheln unnötig neu gezeichnet wurden, nur weil ich ein Pixel auf der Karte gescrollt habe.

Ich habe Javas AWT verwendet, um es zu implementieren, wobei ich ein großes BufferedImage für den Offscreen-Puffer verwendet habe. Die CPU-Auslastung ist in Ordnung (ungefähr doppelt so hoch wie in C ++), aber es gibt ein merkwürdiges Problem beim kontinuierlichen Scrollen, bei dem der Renderer etwa jede Sekunde etwa 0,2 Sekunden hinterherhinkt.

Da mein eigener Code in diesen Zeiträumen nichts enthält und die Spitzen verschwinden, wenn ich meinen Offscreen-Puffer nicht in die Hauptansicht ziehe, kann ich nur den Schluss ziehen, dass Java selbst eine interne Optimierung durchführt. Ich bin mir jedoch nicht sicher, was es bewirkt, und ich weiß auch nicht, welche meiner eigenen Optimierungen ich entfernen müsste, um die Spitzen zu entfernen. Es kann auch sein, dass Java AWT nicht für kontinuierliches Scrollen mit hohen FPS erstellt wurde und dies für diesen Zweck völlig unbrauchbar ist.

Gibt es eine Möglichkeit für mich, diese Spitzen loszuwerden?

cib
quelle
4
Könnte es der Müllsammler sein, der dich schlägt?
Bummzack
@ Bummzack: Möglicherweise. Im Profiler sieht es so aus: i.imgur.com/EMxkA.png Ich bin mir jedoch nicht sicher, wie ich diesen Effekt reduzieren würde, insbesondere wenn er durch meine Aufrufe von graphics.drawImage verursacht wird
cib

Antworten:

4

Obwohl ich nicht sicher sein kann, ohne auf Ihren Code zu schauen, klingt es so, als ob Ihr Problem der Garbage Collector ist. In Java finden ab und zu große und kleine Speicherbereinigungen statt. Der Minderjährige verbraucht einen Teil Ihrer CPU, nervt Sie aber nicht zu sehr. Die wichtigsten Sammlungen können ein echtes Problem für Echtzeit-Apps wie Spiele sein, da sie während der Ausführung tatsächlich alles anhalten.

Es gibt zwei Möglichkeiten, dies zu lösen. Zuerst können Sie die JVM optimieren, um sicherzustellen, dass weniger wichtige Sammlungen stattfinden. Zweitens (und empfohlen) können Sie sicherstellen, dass Sie nicht zu viel Müll hinterlassen. Überprüfen Sie einfach, wo in Ihrer App viele Objekte erstellt werden (in meinen Spielen sind dies normalerweise die vector3-Klassen), und stellen Sie sicher, dass Sie sie so oft wie möglich wiederverwenden (insbesondere in inneren Schleifen usw.).

Pjotterke
quelle
2

Ja.

Bei 2D-Sprite-basierten Spielen kann AWT verwendet werden, um das Rendern mit großer Wirkung zu handhaben. Abhängig von der verfügbaren Hardware kann die Hardware sogar beschleunigt werden.

Ohne Code oder detaillierte Profilausschnitte ist das Problem schwer zu sagen. Das Beste, was ich tun kann, ist, einige grundlegende Tipps für die Arbeit mit Java und AWT beim Erstellen von Spielen anzubieten.

Arbeiten mit dem Garbage Collector

Der GC in Java ist etwas, das wir beim Erstellen unserer Spiele wirklich berücksichtigen müssen. Es wird regelmäßig ausgeführt und sucht nach Objekten, auf die keine Verweise vorhanden sind, und entfernt sie aus dem Speicher. Dieser Entfernungsprozess ist langsam und wahrscheinlich die Ursache für die aufgetretenen Probleme.

Mein Vorschlag ist, zu vermeiden, Objektreferenzen zu erstellen, die während der gesamten Laufzeit der Ausführung (oder zumindest so weit wie möglich) nicht beibehalten werden. Das ideale Ziel ist es, sicherzustellen, dass der GC bei jeder Ausführung nichts zu tun hat.

In der Praxis kann es vorkommen , dass Sie viele statische Variablen verwenden , die Sie im Laufe des Spiels wiederverwenden. Hier ist ein sehr ausgeklügeltes Beispiel dafür, wie ich damit umgehe:

public final class Resources {
    public static Map<int, String> strings;
    public static Map<int, Texture> textures;
    public static Map<int, GameObject> objects;
    public static Map<int, SoundEffect> sounds;
}

Während des Ladebildschirms können Sie Ihre MapInstanzen mithilfe des newSchlüsselworts vergrößern oder verkleinern . Aber während des Spiels sollten Sie dies so weit wie möglich vermeiden. Wenn während des Spiels etwas zerstört wird, setzen Sie ein Flag auf das Objekt, damit Sie wissen, dass es derzeit nicht aktiv ist. Wenn Sie ein neues Objekt erzeugen müssen, gehen Sie es durch, Mapbis Sie eines finden, das nicht aktiv ist, und legen Sie seine Eigenschaften fest und markieren Sie es als aktiv.

Dies sollten Sie berücksichtigen, wenn Sie Java für leistungsempfindliche Anwendungen verwenden, unabhängig davon, ob Sie AWT, JavaFX oder OpenGL für das Rendern verwenden.

Segeltuch

Insbesondere für AWT würde ich die Verwendung der Canvas- Klasse empfehlen , um alles aus verschiedenen Gründen zu rendern:

  • Sie haben eine bessere Kontrolle darüber, wann die Dinge gerendert werden. Dies bedeutet, dass Sie Ihre eigene Spielschleife schreiben und Dinge wie Interpolation, Extrapolation, Ratenbegrenzung usw. ausführen können.
  • Es scheint besser zu funktionieren. Ich konnte mehr Dinge gleichzeitig mit einer akzeptablen Bildrate auf den Bildschirm bringen, anstatt zu versuchen, eine Reihe von Beschriftungs- und Bildobjekten zu animieren.
  • Es ist einfacher, in Editoren einzubetten. In der Lage zu sein, einen Frame mit normalen GUI-Steuerelementen einzurichten und die Renderlogik Ihres Spiels auf einen Canvas zu richten, bedeutete, dass ich den Rendercode des Spiels in den Editor-Tools wiederverwenden konnte.
  • Sie erhalten einfachen Zugriff auf die Java2D-API (auch bekannt als Graphics2D- Klasse).
Chiffre
quelle