Ich versuche, das Grafikmodul meiner Engine zu schreiben. Das heißt, dieser Teil des Codes bietet nur eine Schnittstelle, über die Bilder, Schriftarten usw. geladen und auf dem Bildschirm gezeichnet werden können. Es ist auch ein Wrapper für die Bibliothek, die ich verwende (in diesem Fall SDL).
Hier sind die Schnittstellen für meine Image
, Font
und GraphicsRenderer
Klassen. Bitte sag mir, ob ich den richtigen Weg gehe.
Bild
class Image
{
public:
Image();
Image(const Image& other);
Image(const char* file);
~Image();
bool load(const char* file);
void free();
bool isLoaded() const;
Image& operator=(const Image& other);
private:
friend class GraphicsRenderer;
void* data_;
};
Schriftart
class Font
{
public:
Font();
Font(const Font& other);
Font(const char* file, int ptsize);
~Font();
void load(const char* file, int ptsize);
void free();
bool isLoaded() const;
Font& operator=(const Font& other);
private:
friend class GraphicsRenderer;
void* data_;
};
GrapphicsRenderer
class GraphicsRenderer
{
public:
static GraphicsRenderer* Instance();
void blitImage(const Image& img, int x, int y);
void blitText(const char* string, const Font& font, int x, int y);
void render();
protected:
GraphicsRenderer();
GraphicsRenderer(const GraphicsRenderer& other);
GraphicsRenderer& operator=(const GraphicsRenderer& other);
~GraphicsRenderer();
private:
void* screen_;
bool initialize();
void finalize();
};
Bearbeiten: Einige Änderungen am Code und weitere Details.
Nach einigen der Diskussionen hier habe ich beschlossen, meine Verwendung void*
durch so etwas zu ersetzen :
class Image
{
private:
struct ImageData;
std::shared_ptr<ImageData> data_;
};
(Natürlich werde ich das Gleiche für die Font
Klasse tun .)
Ich sollte auch erwähnen, dass dies nicht meine letzten, vollständigen Klassen sind. Ich zeige hier nur die Grundfunktionen (Laden und Rendern). Anstatt mir zu sagen, welche Funktionen ich Ihrer Meinung nach hinzufügen muss (rotierende Bilder, Schrägstellung, Skalierung usw.), konzentrieren Sie sich nur darauf, zu überprüfen, was ich bereits habe. Ich werde versuchen, meine Entscheidungen zu verteidigen, wenn ich kann, oder meinen Ansatz ändern, wenn ich nicht kann.
quelle
Antworten:
Das, was mich am meisten juckt, ist der
void *
"Missbrauch".Nun, Sie könnten die Aufnahme vermeiden (was ich übrigens genehmige), indem Sie sie an einem für Sie geeigneten Ort weiterleiten.
quelle
struct FontData
undstd::shared_ptr<FontData>
ein privates Mitglied anstelle des definieren werdevoid*
. Dies bedeutet, dass ich auch eine Vorwärtsdeklaration vermeiden kann, was bedeutet, dass die Bibliothek in der Header-Datei nicht erwähnt wird. :)Über Schnittstellen (allgemein)
Sie haben uns daher gebeten, Ihre Entwürfe für Schnittstellen zu überprüfen.
Sie haben uns keine Schnittstellen gegeben, Sie haben uns vollständige Klassenerklärungen gegeben. Wenn dies Schnittstellen wären, würde ich etwas erwarten wie:
Das ist in C ++ eine Schnittstelle. Ich kann es in einer Unterklasse überschreiben, die Funktionalität implementiert (tatsächlich muss ich!). Wenn Sie eine Schnittstelle schreiben, erzwingen Sie Richtlinien, und wie oben beschrieben tun Sie dies.
Die Hälfte der Beschwerden über die Verwendung von void * in den anderen Antworten wäre vermieden worden, wenn Sie nur die Schnittstellenfunktionen verfügbar gemacht und die Mitgliedsvariablen ausgeblendet hätten (wie sie sein sollten, in einer Schnittstellenklasse ).
Rawr.
Auf Schnittstellen (deine)
Bild: Kopieren
Sie haben einen Kopierkonstruktor und einen Gleichheitsoperator. Das Problem, das ich hier sehe, ist, dass es keine gute Möglichkeit gibt, den Benutzer daran zu hindern, alberne Fremdkopien von Bildern zu erstellen.
Für Sie ist die Verwendung von SDL_surfaces ein großes Problem . Ohne beleidigend zu sein, bin ich bereit zu wetten, dass Sie nicht darüber nachgedacht haben, was passiert, wenn Sie ein Bild freigeben, das ein Duplikat eines anderen Bildes ist. Ich bin weiterhin bereit zu wetten, dass Sie nicht geplant haben, die SDL_surface vollständig zu kopieren. In dem oben genannten Fall werden Sie wahrscheinlich ein Bild freigeben, und dann werden Ihre anderen Kopien davon explodieren und jeden töten, den Sie lieben .
Lösung: KEINE KOPIEN. Tu es nicht, erlaube es nicht. Verwenden Sie eine Factory- oder eine C-Ladefunktion, um neue Instanzen eines Images zu erstellen, und verwenden Sie diese, anstatt das Kopieren zuzulassen oder Zuweisungen gleichzusetzen. Alternativ können Sie vollständig herausfinden, wie Sie ein SDL_image tief kopieren (nicht sehr schwer, aber ärgerlich).
Bildbearbeitung
Wie ändere ich Ihre Bilder, nachdem ich sie geladen habe? Laut Ihrer Schnittstelle nicht. Sind Sie sicher, dass dies eine gute Idee ist? Wie finde ich die Bittiefe eines Bildes heraus? Seine Höhe? Breite? Farbraum?
Schriftart
Wie zeichne ich mit dieser Schriftart? Wie bekomme ich den Namen? Wie verhindere ich die Kopierprobleme, über die ich mich oben beschwert habe? Wie stelle ich die Farbe ein? Kerning? Was ist mit der Unicode-Unterstützung?
Renderer: Allgemein
Ich stelle also fest, dass Sie einige Funktionen von blit * () und eine Funktion von render () haben. Dies scheint zu implizieren, dass Benutzer in der Lage sein sollen, eine Reihe von Blitting-Vorgängen in die Warteschlange zu stellen und sie dann alle mit dem Aufruf render () auf einmal auf den Bildschirm zu spülen.
Das ist gut; Genau so geht auch die Motorentechnologie meiner Gruppe damit um. :) :)
Die Verwendung eines Singletons ist hier akzeptabel, vor allem, weil Sie dem Renderer anscheinend die vollständige Kontrolle über das Zeichnen geben möchten. Wenn es nur eine Instanz davon gibt (wie es wahrscheinlich sein sollte), wird dies nichts schaden. Nicht was wir tun, aber hey, es ist Geschmackssache.
Es gibt jedoch ein paar große Probleme, die ich hier sehe.
Renderer: Transformationen
Sie scheinen nur in 2D zu arbeiten. Das ist gut. ABER...
Wie gehen Sie mit Dingen wie dem Drehen eines Bildes beim Zeichnen um? Skalieren? Sie benötigen volle Unterstützung für sogenannte affine Transformationen . Auf diese Weise können Sie Bilder auf hübsche Art und Weise leicht drehen, skalieren, übersetzen, verzerren und auf andere Weise frobieren.
Dies muss (irgendwie) sowohl für Text als auch für Bilder unterstützt werden.
Renderer: Färben und Mischen
Ich möchte in der Lage sein, Farben auf meine Bilder zu mischen und die Farben für meinen Text festzulegen. Sie sollten dies aussetzen.
Ich möchte auch in der Lage sein, Dinge wie das Mischen von Bildern beim Blitzen zu tun, damit ich Dinge wie durchscheinende Geister oder Rauch oder Feuer tun kann.
So sparen Sie sich die Mühe
Verwenden Sie SFML . Es hat ein besseres Design für so ziemlich alles über SDL. Sie haben bereits getan, was Sie hier versuchen. Schauen Sie sich zumindest an, wie sie ihre Schnittstellen spezifiziert haben und wie sie ihre Klassenhierarchie entworfen haben.
Beachten Sie auch, dass sie sich mit dem Problem der Transformationen befassen, auf das ich bereits in ihrer Drawable-Klasse hingewiesen habe. Und färben. Und mischen.
Sie haben gute Tutorials und Dokumentationen, daher lohnt es sich möglicherweise, ein wenig damit herumzuspielen und ein Gefühl dafür zu bekommen, was Ihr Code leisten kann.
Viel Glück!
quelle
Ich bin ein bisschen besorgt über die "Lade" -Methode, die gegen das Prinzip der Einzelverantwortung verstößt (übrigens, für die kommunistische Ente, ich denke, deshalb verwendet er ein const char *, weil die SDL-Ladebildfunktion codiert ist nimm in C keinen std :: string). Es sollte aus mindestens zwei Gründen kein Job einer Image-Klasse sein, sich selbst zu laden:
quelle
std::string
zuchar*
, so dass nicht der Grund ist. Der Grund ist einfach, dassstd::string
dies keinen Vorteil bringen würde.Singleton = BAD, sofort entfernen. Schriftarten und Bilder sollten keine
free()
Funktion haben, das sollte die Aufgabe des Destruktors sein. DieGraphicsRenderer
sollten die nicht anbietenBlit
Funktionen, sollten Sie objektorientierte Klassen bieten , die eine Position für jedes Ergebnis liefern wird, und die tatsächliche Rendering automatisch vom GraphicsRenderer verwaltet. Verwenden Sie zur Kapselung die Vererbung, nicht PIMPL, und verwenden Sie auf keinen Fall eine Leere *. Verwenden Sie einen stark typisierten undurchsichtigen Zeiger.Hier sind einige Auszüge aus meinem eigenen Design, obwohl ich das Umschalten zur Kompilierungszeit und nicht die Vererbung zur Laufzeit verwendet habe.
Hier wird der Speicher für Sie verwaltet - alle Eigentumsrechte werden durch intelligentes Zeigen verwaltet, und die Schnittstelle ist vollständig objektorientiert.
quelle
free()
Funktion ist aus demselben Grund vorhanden, aus dem dieifstream::close()
Funktion vorhanden ist. | Blit: Ich versuche, die Daten von dem Mechanismus zu trennen, der die Daten verwendet. | Sprites: Dies ist nur das Grafikmodul. Sprites sind in den höheren Ebenen des Motors auf ähnliche Weise wie von Ihnen beschrieben zu handhaben. [Fortsetzung]data_
ist nur eineSDL_Surface*
Besetzung zuvoid*
. Wann immer ich es benutze, wirke ich es zurück zuSDL_Surface*
. | Blit [Forts.]: Eine andere Art, darüber nachzudenken, ist, dass sich das Bild nicht selbst zeichnet & mdash; Der Renderer zeichnet es. Das Bild existiert einfach .std::shared_ptr
. Vielen Dank auf jeden Fall nützlich sein.free()
Funktion erforderlich . Man könnte auch ein benutzerdefiniertes Zuordnungsschema verwenden, das den Destruktor nicht aufruft.Erste Dinge, die ich erkennen kann:
Font
undImage
scheinen eng verwandt zu sein. Vielleicht könnten Sie einen Teil der Funktionalität der Hierarchie auf a bringenRenderable
.const char*
überstd::string
?quelle
const char*
, das hatte ich nicht bemerkt.SDL.h
(void* data_
ist nur eineSDL_Surface*
Besetzungvoid*
). | Zeichenfolgen: Habe ich einen Grund, sie nicht zu verwenden? Ich mache keine Manipulationen, ich brauche nur eine Schnur. Ich kann esstd::string
außerhalb dieser Klassen verwenden, wenn ich dies wünsche. | Singletons: Ja, mehr als ein GR wird meinen Code brechen. GR initialisiert SDL. Wenn es ein Singleton ist, muss ich mir keine Sorgen machen, wenn SDL initialisiert oder beendet wird.SDL.h
ist so ziemlich Null. Irgendwie hätte ich keinen Renderer, der SDL initialisiert. Ich würde das auf die Hauptmaschine oder etwas anderes abstrahieren. Was ist, wenn Sie andere Teile von SDL außer Grafiken möchten?