grundsätzliche Fragen bzgl. Animationen in (Py)Games

Hier werden alle anderen GUI-Toolkits sowie Spezial-Toolkits wie Spiele-Engines behandelt.
Antworten
Benutzeravatar
Don Polettone
User
Beiträge: 115
Registriert: Dienstag 23. November 2010, 20:26
Wohnort: Schweiz

Hi zusammen,

Ich habe da ein / zwei grundsätzliche Fragen bzgl. Animation von Sprites und hoffe, jemand kennt sich da super aus und hat mir ein paar nützliche Tipps oder einen Denkanstoss.

Ich schildere mal die Ausgangslage:

Ich habe schon einige 2D Spiele gecodet (selten bis zum finalen Produkt aber immerhin...), und sehr oft besteht meine 2D Spielwelt aus Tiles und es bewegen sich Objekte darin, welche 4 Richtungen haben können (UP DOWN LEFT RIGHT). Manchmal sinds auch nur 2, wenn's strikt von der Seite dargestellt werden soll.

Mein Game Loop ist nicht ultradynamisch (möchte ich auch nicht); aber die FPS werden limitiert auf 50, sprich: Ein Frame dauert 20 ms. Ich lasse mir jeweils auf den Screen blitten, wieviel Zeit mein Game noch "übrig" hat für Berechnungen pro Frame, so kann ich eigentlich stabile Spiele programmieren, welche auf vielen Maschinen tadellos und gleich schnell laufen. So weit, so gut.

Nun zu meinem Anliegen:

Mein Game Loop datet up, es sind 20 ms vergangen. Nun werden meine Spielobjekte (Chars etc) upgedated und sie wissen, dass 20 ms vergangen sind im Spiel. Bzgl. Animation habe ich das nun bisher so gemacht, dass sich ein Char beim Instanzieren seine Bilder merkt als dict (self.images). Auch merkt er sich seine Richtung (self.direction). Der Char hat ein Attribut a_counter und a_trigger; zudem ein Attribut i_index. Nun wird jedes Frame a_counter um 20 (ms) erhöht und wenn a_trigger erreicht wird, wird i_index um 1 erhöht. (i_index ist die Indexzahl des aktuellen Bildes in self.images, das angezeigt werden soll). Das images dict habe ich so aufgebaut, dass es in states und directions unterteilt ist:

Code: Alles auswählen

self.images = {RIGHT : {STAND : (img, ),
                        WALK  : (img, img, img),
                        RUN   : (img, img, img, img),
                        JUMP  : (img, img)},
               UP    : {STAND : (img, ),
                        WALK  : (img, img, img),
                        RUN   : (img, img, img, img),
                        JUMP  : (img, img)},
               LEFT  : {STAND : (img, ),
                        WALK  : (img, img, img),
                        RUN   : (img, img, img, img),
                        JUMP  : (img, img)},
               DOWN  : {STAND : (img, ),
                        WALK  : (img, img, img),
                        RUN   : (img, img, img, img),
                        JUMP  : (img, img)}}
img = pygame.Surface Objekt (Grafiken hat)

Der Char hat ein img property, welches immer sein aktuelles Bild zurückgibt: self.images[self.direction][self.state][self.i_index]

Soweit hat das gut funktioniert eigentlich - der Char konnte seine direction und seinen state updaten und auch animieren, und self.img repräsentiert dabei das gerade aktuelle Bild.

Diese Methode hat aber auch Nachteile irgendwie hab ich mit der Zeit nun festgestellt:
- jede Animation ist gleich schnell, weil sie abhängig ist vom self.a_trigger Attribut.
- Auch muss für jede Animation ein neuer state her für den Char, das ist irgendwie unschön, weil der state eines chars ja nichts mit seiner Animation zu tun haben sollte - glaube ich zumindest

Ich habe mir nun deshalb überlegt eine Animation Klasse zu proggen, welche ich für alle kommenden Projekte nutzen kann. Ich denke da an ein Objekt, welchem man beim instanzieren eine Liste von Images mitgibt sowie einen Animation Speed, einen loop bool etc etc.
Wenn man dieses Objekt updated, ändert sich allenfalls dessen Bild - self.img.

Wenn ich nun einen Char progge, muss ich ihm kein img Zeugs mehr geben; er merkt sich nur seine Animation - eben so ein Objekt. Und dieses wird dann vom char upgedated jedes Frame. So habe ich den Animationsteil von der Char Klasse outgesourced und das Ganze wird wieder etwas hübscher und dynamischer, nicht? Zudem möchte ich noch eine (neue) SpriteSheet Klasse proggen, weil es mit der Zeit mühsam wird, die vielen Bilder als einzelne Dateien zu handeln. Aber das sollte eigentlich klappen. Das Zeugs mit der Animation und den Chars ist eher das, wobei ich froh um Hilfe wäre.

Ich möchte einfach mal die "Fühler strecken" nach Inputs, wie sich dieser Lösungsansatz bzgl. Animation für Euch da draussen anhört und allenfalls wie ihr das macht mit Animation in Spielen; ganz grundsätzlich.

Danke schon mal und Gruss,


Don Polettone
Ich code, also bin ich.
Benutzeravatar
Madmartigan
User
Beiträge: 200
Registriert: Donnerstag 18. Juli 2013, 07:59
Wohnort: Berlin

Was ist der Grund für die harte Begrenzung der Frames? Warum 50 FPS? Was passiert auf Geräten, die mehr als 20ms pro Frame benötigen?

Ich würde generell davon abraten, die "Leistung" manuell zu "drosseln", das macht keinen Sinn. Verwende doch stattdessen eine globale GameTime/DeltaTime o.Ä. die angibt, wie viel Zeit der letzte Frame benötigt hat. Alle arithmetischen Operationen werden dann mit diesem Wert normiert (http://lmgtfy.com/?q=time+based+movement). Das ermöglicht dir verlässliche (Sprite-) Animationsgeschwindigkeiten bzw. schafft es überhaupt erst einmal die Garantie, dass unterschiedlich leistungsfähige Hardware deine Animationen gleich schnell darstellt (nur eben unterschiedlich "smooth").

Verwendest du wirklich Einzelbilder für deine Sprite Animationen? Wenn ja, würde es sich schon aus organisatorischem Aufwand anbieten, diese in einem Bild respektive Atlas zusammen zu fassen. Das entsprechende Sprite holst du dir dann in jedem Frame einfach über die (Textur-) Koordinaten. Statt eines Dictionary mit vielen Image Objekten hälst du also nur ein Dictionary o.Ä. mit Koordinaten vorrätig. Darüber hinaus sind Texture-Binding und hochfrequentes Wechseln relativ teure Operationen, d.h. es ist auf jeden Fall ratsam, auf die vielen Einzeldateien pro Sprite Animation zu verzichten.
Benutzeravatar
Don Polettone
User
Beiträge: 115
Registriert: Dienstag 23. November 2010, 20:26
Wohnort: Schweiz

Hi Madmartigan,

danke fürs Ansehen :|

Nun, bzgl. fixed Time Step von 20 ms: Beide Methoden (fixed / fluid) haben deren Vor- und Nachteile. Ich code oft auch gerne mit (Py)Box2D, weshalb sich für mich im allgemeinen eher ein fixed time step anbietet (http://gamedev.stackexchange.com/questi ... -time-step). Zudem möchte ich nicht zwingend Games proggen, die auf "Uralt-Hardware" laufen, und das ist sichergestellt so. (Habe meine Games schon auf vielen Maschinen ausprobiert und ich progge meine Games absichtlich so, dass ich noch genug ms übrig habe für wirklich schlechte Maschinen). Wie gesagt, ich habe mir das überlegt, eine Entscheidung getroffen und bin soweit zufrieden damit. Ich komme an mein Ziel und sehe für mich mehr Vor- als Nachteile. Aber danke für den Hint. Sobald meine Games anspruchsvoller werden, werde ich umstellen müssen.

Bzgl. SpriteSheeet und Animation: Ich hatte mir gedacht, beim Start des Programms die benötigten SpriteSheets reinzuladen. Das SpriteSheet macht sich beim Instanzieren dann (einmalig) ein dict wo seine Sprites drin sind. Diese Sprites holt es sich aus seinem Hauptbild (eben dem SpriteSheet Bild). Die Keys fürs dict sind topleft Koordinaten und beim instanzieren des Sheets kann man die Zellengrösse angeben, damit das SpriteSheet jede (rechteckige) Spritegrösse haben kann. So werden die Bilder nur einmal geladen beim start des Programms. Wenn nun eine Animation erstellt wird, braucht diese nur ein spritesheet und topleft koordinaten und holt sich so ihre bilder (nicht nochmals laden, nur verlinken!). Soweit sind wir uns in etwa einig, oder? (Ich kann nicht genau nachvollziehen, was Du mit "hochfrequentes Wechseln" genau meinst)
Ich code, also bin ich.
Antworten