AnimatedSprite Klasse Design-/Umsetzungproblem
-
Hallo zusammen,
ich arbeite gerade an einer Klasse namens AnimatedSprite, deren Header im moment so aussieht: (Ich verwende SFML)
class AnimatedSprite : public sf::Sprite { public: AnimatedSprite(float updateEvery); virtual ~AnimatedSprite(void); void updateImage(float delta); void addImage(sf::Image* img); void setLoop(int low, int high); private: std::vector<sf::Image*> images; float updateRate; //update every X seconds int imgIndex; int lowLoopBound; int highLoopBound; //exclusive float restTime; void nextImg(); //skips to next image in loop };Also die Idee ist folgende:
Ich habe einen Vektor, der alle images die in animationen für dieses Sprite verwendet werden speichert.
Mit addImage fügt man ein image hinzu, mit setloop kann man (durch indizes) auswählen zwischen welchen images durchgelaufen werden.Für ein Anwendungsbeispiel brauche ich für einige sachen animationen:
- nach links gehen
- nach rechts gehen
- richtung von links nach rechts ändern
- richtung von rechts nach links ändern
- stehen in richtung nach rechts
- stehen in richtung nach links
- und evtl. mehrIch finde das etwas mühsam, mit setloop, dann muss man sich genau die indizes merken und darauf basierend setzen...
Deswegen suche ich nach einer besseren Möglichkeit.
Ich habe mir schon überlegt, eine Unterklasse zu machen, die eine unterloop hält und evtl eine aktion, die aufgerufen wird, wenn diese unterloop zuende ist.
Beispiel:
Ein Loop Element, dass die animation für "von links nach rechts wechseln" hält, würde dann wenn die animation zuende ist irgendwie dafür sorgen, dass auf "nach rechts gehen" gewechselt wird oder so.
Naja, so ganz fertiggedacht krieg ich die idee irgendwie noch nicht^^Ich bin dankbar für hilfreiche Vorschläge!
-
Vererbung ist verlockend, aber nicht unbedingt sinnvoll. Viele Funktionen der Basisklasse haben keine sinnvolle Bedeutung mehr, z.B.
SetImage()oderSetSubRect(). Verwende stattdessen Komposition.Eine Möglichkeit ist eine Klasse, die mehrere
sf::Sprites als Member hält. Diese verweisen auf unterschiedlichesf::Images oder Teile davon. Bei einemUpdate()-Aufruf sollte dasAnimatedSpriteautomatisch schauen, ob das aktuelle Frame der Animation bereits passiert ist und wenn nötig zum nächsten Element im Container wechseln. Als Loop würde ich nur einbool-Flag verwenden, das nach Passieren wieder von vorne anfängt.
-
Mit der Vererbung hast du Recht, ich brauche diese Methoden von außen wirklich nicht, und die würden den internen zustand kaputt machen wenn man sie dennoch aufruft. Danke für den Vorschlag.
Ist es dann sinnvoller ein Sprite als Attribut zu nehmen, oder privat zu erben?Warum denn mehrere Sprites als member? (sprites halten z.B. auch position usw), ich dachte eher daran mehrere images zu speichern und nur ein spirte (oder keins, falls man privat erbt).
Das mit dem bool flag hab ich nicht verstanden, wie du das anwenden willst.
Dann noch nen bischen was anderes:
Ich möchte möglichst zwei verschiedene Arten von bildmaterial unterstützen:
Einmal das man die Loops aus einzelnen bildern zusammenbastelt und einmal, dass man nur ein bild hat und davon verschiedene subRects genommen werden, habt ihr dazu ne gute idee?
Ich hab mir folgendes gedacht: Ich könnte ja eine abstrakte basisklasse / interface machen für diese loopobjekte und dann zwei verschiedene implementierungen, eine mit einzelbildern und eine mit subrects.
Was haltet ihr davon?Also zur Hilfsklasse habe ich dann folgenden Entwurf:
class ImageLoop { public: bool update(float delta); //gibt true zurück und resetted sich selbst, wenn das ende erreicht ist ImageLoop& getNextLoop(); void reset(); //setzt die animation wieder auf anfang private: std::vector<sf::Image*> images; ImageLoop* nextLoop; //kann entweder eine nachfolger loop sein, oder auf sich selbst zeigen void nextImage(); }Benutzung dann ungefähr so:
ImageLoop* newLoop = 0; switch(direction) { case LEFT: newLoop = loopLeft; break; case RIGHT: newLoop = loopRight; break; //andere fälle noch } //wenn sich die richtung geändert hat wird eine neue animation genommen und entsprechend diese neue animation auf ausgangszustand zurückgesetzt if(newLoop && newLoop != loop) { loop = newLoop; loop->reset(); } //wenn das ende erreicht ist, wird der nachfolger erreicht if(loop->update(delta)) { loop = &(loop->getNextLoop()); }
-
Gast1337 schrieb:
Ist es dann sinnvoller ein Sprite als Attribut zu nehmen, oder privat zu erben?
Attribut. Private Vererbung brauchst du ziemlich selten (z.B. für Zugriff auf
protected-Member oder Überschreibung einer virtuellen Funktion). Verwende immer Komposition, wenn du damit erreichst was du willst.Gast1337 schrieb:
Warum denn mehrere Sprites als member? (sprites halten z.B. auch position usw), ich dachte eher daran mehrere images zu speichern und nur ein spirte (oder keins, falls man privat erbt).
Du hast Recht, ein Sprite wäre klüger. Ich würde einen Container mit
std::pair<sf::Image*, sf::IntRect>oder was Ähnlichem speichern, um die einzelnen Bilder/Bildausschnitte zu referenzieren. Das sollte auch deine andere Frage beantworten, Polymorphie ist wahrscheinlich Overkill.Gast1337 schrieb:
Das mit dem bool flag hab ich nicht verstanden, wie du das anwenden willst.
Ein
AnimatedSprite::SetLoop(bool loop)sorgt mittruedafür, dass die Animation immer wieder abgespielt wird; hingegen istfalsefür einmalige Animationen geeignet.
-
Wie das mit SpriteSheets mit SFML genau geht, kann ich dir nicht sagen. Aber mit subrects haste natürlich den richtigen Riecher. Um SpriteSheets zu erstellen nehme ich das hier: http://spritesheetpacker.codeplex.com/
Sehr einfach zu parsen das Format.
Und dann halt einfach das Bild wechseln.