[Pygame] Isometrische Engine

Hier werden alle anderen GUI-Toolkits sowie Spezial-Toolkits wie Spiele-Engines behandelt.
glocke
User
Beiträge: 66
Registriert: Mittwoch 23. Februar 2011, 21:18

EyDu hat geschrieben:Vielleicht weil du nicht genau weißt was die tick-Methode macht ;-) Schau dir doch mal die Dokumentation dazu an
Die tick Methode verwende ich auf eine von object abgeleitete Klasse. In welcher Dokumentation sollte ich da denn nachschauen?
Soweit ich das sehe macht die Methode nix anderes als die Eingabe zu prüfen und entsprechend die Offsets der Kacheln zu verschieben.
EyDu hat geschrieben:... und überlege dir, warum du länger in der Methode bist, wenn nichts berechnet wird und warum du kaum in der Methode bist, wenn viel gearbeitet wird.
Ich bin aber weniger in der Methode wenn nichts berechnet wird, und länger wenn viel gearbeitet wird. Oder verdrehe ich hier was O_o

LG

/EDIT: wenn ich bei der Bewegung der Kamera keine Vector-Objekte sondern 2 Integer Zahlen verwende (und die Tiles durch 2 geschachtelte Dicts realisiere). Läuft es mehr als nur flüssiger O_o (Code gleich @GitHub)

Hat noch jemand Ideen die Performance zu verbessern?

Performance (100x100 Kacheln)
Netbook: 8-10 FPS
PC Freundin: 18-27 FPS
PC Kumpel: >50 FPS
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Ich würde ja vermuten, dass es sich um pygame.time.Clock.tick handelt.
Das Leben ist wie ein Tennisball.
glocke
User
Beiträge: 66
Registriert: Mittwoch 23. Februar 2011, 21:18

Ich meinte aber controller.tick() ein paar Zeilen drüber. Der Clock-Tick hat mit der Performance in dem Sinne hier nix am Hut, als dass er nachteilig wirken würde (bei 50 fps etc.)

/EDIT: In der Doku finde ich nix verdächtiges.
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

glocke hat geschrieben:Ich meinte aber controller.tick() ein paar Zeilen drüber. Der Clock-Tick hat mit der Performance in dem Sinne hier nix am Hut, als dass er nachteilig wirken würde (bei 50 fps etc.)
Der Clock-Tick hat gerade lange Laufzeiten bei hohen Frameraten, bzw. bei der Zielframerate, da die tick-Methode lediglich Zeit verbrennt um die Framerate auf das gewünschte Niveau zu senken. Wenn dein Programm nichts zu tun hat, dann wird die tick-Methode irgendwo auf die 100% Zeit zulaufen.

Vielleicht schaffe ich es morgen einen Blick in deinen Code zu werfen, heute abend ruft das Bett. Ich würde aber auch stark vermuten, dass zu viele unnötige Kacheln gezeichnet werden, welche nicht im Sichtbereich liegen. Um das zu testen, brauchst du noch nicht einmal einen Quadtree zu verwenden. Da du den Bildschirmausschnitt kennst, sollten sich leicht die zu sehenden Kacheln bestimmen lassen.

Falls du es noch nicht tust, solltest du vielleicht auch numpy in Betracht ziehen. Damit lassen sich viele Rechnungen vereinfachen und und beschleunigen.
Das Leben ist wie ein Tennisball.
glocke
User
Beiträge: 66
Registriert: Mittwoch 23. Februar 2011, 21:18

Ja es werden nicht sichtbare Kacheln gezeichnet. Da alle Kacheln in der SpriteGroup bottomlayer (Engine.py) liegen und ich mit zusätzlichem Rein- und Rausnehmen nicht noch mehr Rechenaufwand erzeugen will.

Btw habe ich jetzt eine Spielfigur (mit der sich die Kamera zusammen bewegt) und eine stehende Spielfigur (die sich mit den Kacheln bewegt) hinzugefügt. Bis auf einige Probleme mit diagonalen Bewegungen (dann ist irgendwann die WorldPosition "kaputt") läufts ganz gut. Das Problem schau ich mir morgen an; vielleicht können wir dann gemeinsam noch etwas an der Performance drehen.

Den Code werde ich morgen mal "aufräumen" und bisschen besser kommentieren ... ^^

Liebe Grüße und gute Nacht.
deets

Das mit den nicht-sichtbaren Kacheln solltest du tunlichst sein lassen. Dafuer ja mein Tipp mit BSPs/Quadtrees. Und du solltest dir die Arbeit mit dem Profiler angewoehnen - nur so erfaehrst du wirklich, was passiert.
webspider
User
Beiträge: 485
Registriert: Sonntag 19. Juni 2011, 13:41

Stichwort diesbezüglich wäre Culling, d.h. das Prüfen ob etwas gezeichnet werden muss anhand vereinfachter Bedingungen. Es ist eine verhältnismäßig schlichte Optimierung mit viel Performancegewinn im Gegensatz zu den Sachen, die man als Neuling sonst geneigt ist ohne jegliche Tests zu tun um das Programm auf Trab zu bringen.
glocke
User
Beiträge: 66
Registriert: Mittwoch 23. Februar 2011, 21:18

Also sollten alle Kacheln die beim Bewegen vom Bildschirm verschwinden aus der SpriteGroup entfernt und alle Kacheln die dabei erscheinen hinzugefügt werden?
Benutzeravatar
Hyperion
Moderator
Beiträge: 7478
Registriert: Freitag 4. August 2006, 14:56
Wohnort: Hamburg
Kontaktdaten:

Wo ich da grad etwas von `SpriteGroup` gelesen habe... arbeitest Du ausschließlich mit Sprites? Ich habe das für die Tiles einer Engine noch nie gesehen - aber vielleicht ist das ja auch eine gute Idee.

Zumindest was das Hinzufügen zu / Wegnehmen von einer Gruppe anbelangt, kann Dich die Doku beruhigen:
The groups are designed for high efficiency in removing and adding Sprites to them.
encoding_kapiert = all(verstehen(lesen(info)) for info in (Leonidas Folien, Blog, Folien & Text inkl. Python3, utf-8 everywhere))
assert encoding_kapiert
webspider
User
Beiträge: 485
Registriert: Sonntag 19. Juni 2011, 13:41

Eigentlich sollte Culling ja im Renderer implementiert werden statt mit den Objekten zu hantieren. Wie gesagt wird vor dem Zeichnen geprüft ob das aktuell drankommende Objekt sichtbar wäre, wenn ja wirds gezeichnet, wenn nein, dann nicht. Profilen solltest du dennoch.
deets

glocke hat geschrieben:Also sollten alle Kacheln die beim Bewegen vom Bildschirm verschwinden aus der SpriteGroup entfernt und alle Kacheln die dabei erscheinen hinzugefügt werden?
Wozu brauchst du diese Group ueberhaupt? Ich wuerde die loswerden. Alleine schon, weil sie keine Ordnung beim zeichnen einhaelt - womit dir bei Kacheln die eine Hoehe haben das ganze Ding um die Ohren fliegt - du musst ja von hinten nach vorne rendern.

Sammel einfach ausgehend von der Kamerposition alle notwendigen Kacheln ein, und mal die. Punkt.
deets

Und noch ein Hinweis: habe mir gerade mal das hier angeschaut:

https://github.com/christiangloeckner/P ... /isomap.py

Und da hast du natuerlich einen gigantischen konzeptionellen Fehler drin. Um deine Bildschirmtransformation zu machen, veraenderst du die Koordinaten der Tiles!! Das ist natuerlich dramatisch, denn dadurch erzeugst du Komplexitaet proportional zur Groesse deines Levels! Das ist quadratisch, und damit algorithmisch unguenstig - und unnoetig!

Die Koordinaten haben natuerlich fest zu bleiben. Stattdessen waehlst du ausgehend von der Kameraposition alle sichtbaren Tiles - damit wird das Problem in der Groesse reduziert auf die Anzahl der *darstellbaren* Tiles. Und die blittest du dann an ihre entsprechende Position.
glocke
User
Beiträge: 66
Registriert: Mittwoch 23. Februar 2011, 21:18

Hi, an der ganzen Sache bastel ich schon ne Weile ... hab gemerkt dass es absolut mistig ist, und bin gerade dabei das Modul neu zu schreiben. Allerdings habe ich bei der Umrechnung der Koordinaten einige Probleme.

Code: Alles auswählen

def map2screen(self, (x, y)):
        """Convert map coordinate to screen coordinate"""
        tw = self.tilesize
        th = tw / 2
        w  = self.mapsize.x * tw
        # convert
        x_ = w/2 + tw/2 * (x-y)
        y_ = th/2 * (1+x+y)
        return (x_, y_)
Die Methode klappt einwandfrei. Ich habe mir eine kleine Karte skizziert (0,0 dabei oben in der mitte) und eine Formel hergeleitete wie ich die Tile-Positionen auf dem Bildschirm berechne. Nur scheitere ich daran den Rückschluss von ScreenPosition auf MapPosition hinzubekommen. *rumgoogle* Das Umstellen der Formel ...

Code: Alles auswählen

def screen2map(self, (x, y)):
        """Convert screen coordinate to map coordinate"""
        tw = self.tilesize
        th = tw / 2
        w  = self.mapsize.x * tw
        # convert
        x_ = (tw * 2 * y - tw * th + 2 * th * x - th * w) / (2 * th * tw)
        y_ = (tw * th - 2 * y * th + 2 * th * x - th * w) / (2 * th * tw)
        return (x_, y_)
(mit wxMaxima, hoffentlich habe ich keine Tippfehler) ... wirkt bisschen derb und bringt auch falsche Ergebnisse. Beide Funktionen arbeiten nun unabhängig von der Kameraperspektive.

Bei map2screen ziehe ich danach noch die Kameraposition ab.

LG
EyDu
User
Beiträge: 4881
Registriert: Donnerstag 20. Juli 2006, 23:06
Wohnort: Berlin

Wenn du die Transformation umkehren willst, musst du lediglich

Code: Alles auswählen

x_ = w/2 + tw/2 * (x-y)
nach x und

Code: Alles auswählen

y_ = th/2 * (1+x+y)
nach y auflösen und entsprechend einsetzen. Ich sehe gar nicht, was man da groß mit Google versuchen möchte, das lässt sich doch schon im Kopf lösen. Zur Not tun es auch drei Zeilen auf Papier.

Da du offensichtlich Python 2.x verwendest, solltest du auch mit den Divisionen vorsichtig sein. ``x/2`` ist eine ganzzahlige Division, wenn x ein Integer ist. Das könnte bei dir das eine oder andere Problemchen verursachen.

Sebastian
Das Leben ist wie ein Tennisball.
glocke
User
Beiträge: 66
Registriert: Mittwoch 23. Februar 2011, 21:18

Umgestellt habe ich es (wie geschrieben mit wxMaxima, ich mach beim Umstellen sonst zu viele kleine Schusselfehler). Ich mag nur gerade nicht akzeptieren dass es damit gewesen sein soll O_o xD

Wegen der Divisions-Sache hast du Recht, hab die 2 im Divisor durch 2.0 ersetzt um explizit nen Float zu erzwingen. Damit müsste die Division imho im Float ausgeführt werden.
Wäre es ratsam

Code: Alles auswählen

from __future__ import division
zu verwenden? Ich würde ja Python 3 verwenden aber darauf möchte ich wegen http://www.pygame.org/wiki/FrequentlyAs ... ython%203? verzichten.

Den aktuellen Code hab ich mal hochgesynct damit alle das gleiche als Basis haben.

/EDIT: hatte 3 Tippfehler, die erklärten auch die Abweichungen xD Kamera auf ein Tile zentriert setzen (via Konstruktor) funktioniert auch. Dann kümmere ich mich jetzt wieder um das Selektieren der sichtbaren und unsichtbaren Kacheln.

/EDIT2: Erledigt (erstmal). Aber wahrscheinlich wäre es sinnvoller beim Bewegen der Kamera alle sichtbaren Tiles zu erfassen (und in einer Liste entsprechend der Renderreihenfolge einzuordnen) anstatt das beim Rendern jedes mal zu tun.... änder ich später.

/EDIT3: Hab den Repo-Namen gekürzt https://github.com/christiangloeckner/pare
Antworten