Du bist nicht angemeldet.

Stilllegung des Forums
Das Forum wurde am 05.06.2023 nach über 20 Jahren stillgelegt (weitere Informationen und ein kleiner Rückblick).
Registrierungen, Anmeldungen und Postings sind nicht mehr möglich. Öffentliche Inhalte sind weiterhin zugänglich.
Das Team von spieleprogrammierer.de bedankt sich bei der Community für die vielen schönen Jahre.
Wenn du eine deutschsprachige Spieleentwickler-Community suchst, schau doch mal im Discord und auf ZFX vorbei!

Werbeanzeige

TrommlBomml

Community-Fossil

Beiträge: 2 117

Wohnort: Berlin

Beruf: Software-Entwickler

  • Private Nachricht senden

11

31.01.2014, 13:48

Das sollte sowieso jede Anwendung bzw. jedes Spiel machen, die mehr als zwei drei Bilder in der Main rendert. David hat aber auch recht, es lohnt sich meist nicht, was sehr komplexes zu überlegen. Wenn beim ersten Zugriff die Ressourcen geladen und danach einfach geholt werden können, dann hast du schon 95% der Fälle abgedeckt. Ich habe das auch nie wirklich gebraucht, aber immer dummerweise implementiert, obwohl ich es gar nicht gebraucht habe :)

12

31.01.2014, 13:52

Weniger ist manchmal mehr :) Das ist eine Sache die mir in letzter Zeit mal wieder sehr verdeutlicht wurde :D

Danke für Eure Hilfe :thumbup:

LG Glocke

Sacaldur

Community-Fossil

Beiträge: 2 301

Wohnort: Berlin

Beruf: FIAE

  • Private Nachricht senden

13

31.01.2014, 15:02

Es wurde ja bereits richtigerweise angemerkt, dass man sowas nur dann implementieren sollte, wenn man es auch wirklich braucht bzw. es auch Sinn macht (abhängig vom Spiel).

Solltest du feststellen, dass es in deinem Fall sinnvoll ist und du es umsetzen wollen:
Ich würde die Ressourcenverwaltung (Grafiken, Sounds, Scriftarten, ...) unabhängig von der Internationalisierung halten, da es gänzlich unterschiedliche Dinge sind und die Verwaltung der einzelnen Ressourcen auch getrennt halten. (Getrennt in dem Sinne, dass am Ende nicht unterschiedliche Ressourcen von dem gleichen Manager(-Objekt) verwaltet werden.) Will man einen Sound laden, dann will man auch enen Sound laden und keine Grafik oder Schirftart.
Zusätzlich könnte es sinnvoll sein, den Manager dahingehend zu erweitern, dass von jeder Ressource (bspw. "Grastextur") mehrere Varianten vorhanden sein können (Grafiken: 512x512, 1024x1024; Schriftarten: 12pt, 14pt, 20pt; ...), wo abhängig vom Kontext die richtige gewählt wird. Ob diese Auswahl vom Ressourcenmanager durchgeführt werden kann ist aber abhängig vom Spiel. (Wenn bspw. beim Laden des Levels bereits bestimmt werden kann, welche Ressourcen nötig sind, wäre es möglich, allerdings dürfte das höchstens bei 2D Spielen der Fall sein.)

Ein "Wörterbuch" für die Internationalisierung (bzw. dessen Einträge für die Nationalisierung) braucht entweder etwas zur Identifizierung des Textes und etwas zur Identifizierung der verwendeten Sprache, damit einzelne Texte abgerufen werden können, oder nur etwas zur Identifizierung des Textes und ein Nachladen der übersetzten Texte, wenn die Sprache bspw. in den Optionen gewechselt wird. Will man zudem den Fall abfangen, dass eine bestimmte sprache unvollständig ist (sollte möglichst nicht der Fall sein, kann unter bestimmten Umständen aber passieren), muss man bspw. auf die "Standardsprache" zurückgreifen. Es ist egal, wie man es umsetzt, man muss die "Wörtbucheinträge" anders als die anderen Ressourcen handhaben.


Aber auch bei den Übersetzungstexten gilt:
Solange du es nicht jetzt gerade brauchst, kannst du es auch später noch implementieren, wenn du es auch wirklich brauchen wirst.
Spieleentwickler in Berlin? (Thema in diesem Forum)
---
Es ist ja keine Schande etwas falsch zu machen, als Programmierer tu ich das täglich, [...].

14

31.01.2014, 16:36

Ich würde die Ressourcenverwaltung (Grafiken, Sounds, Scriftarten, ...) unabhängig von der Internationalisierung halten, da es gänzlich unterschiedliche Dinge sind und die Verwaltung der einzelnen Ressourcen auch getrennt halten. (Getrennt in dem Sinne, dass am Ende nicht unterschiedliche Ressourcen von dem gleichen Manager(-Objekt) verwaltet werden.) Will man einen Sound laden, dann will man auch enen Sound laden und keine Grafik oder Schirftart.


Die Trennung von SoundManager, MusicManager (die Unterscheidung Sound und Music ist SDL-bedingt), ImageManager und FontManager habe ich eh bereits implementiert - die sind also paarweise disjunkt was die Ressourcen angeht. Später will ich dann noch weitere Manager implementieren: Objekte sollen auf Objekt-Templates basieren (beschreiben Aussehen, Animationen, Sounds etc.). Von daher hatte ich einen ObjectManager im Kopf, der die Config parst, die Template-Daten im RAM behält und - wenn ich ein Objekt eines bestimmten Templates anfordere - mir Objekte (die dieses Template verwenden sollen) generiert.

Zusätzlich könnte es sinnvoll sein, den Manager dahingehend zu erweitern, dass von jeder Ressource (bspw. "Grastextur") mehrere Varianten vorhanden sein können (Grafiken: 512x512, 1024x1024; Schriftarten: 12pt, 14pt, 20pt; ...), wo abhängig vom Kontext die richtige gewählt wird. Ob diese Auswahl vom Ressourcenmanager durchgeführt werden kann ist aber abhängig vom Spiel. (Wenn bspw. beim Laden des Levels bereits bestimmt werden kann, welche Ressourcen nötig sind, wäre es möglich, allerdings dürfte das höchstens bei 2D Spielen der Fall sein.)


Den Fall mit den verschiedenen Schriftarten hatte ich bereits gehabt: Lade ich mit SDL_ttf eine Font, muss ich die Schriftgröße mit angeben. Daher dient mir (zumindest war das meine bisherige Idee) nicht ein std::string als Key sondern ein Tupel aus Dateiname und Schriftgröße.

Ein "Wörterbuch" für die Internationalisierung (bzw. dessen Einträge für die Nationalisierung) braucht entweder etwas zur Identifizierung des Textes und etwas zur Identifizierung der verwendeten Sprache, damit einzelne Texte abgerufen werden können, oder nur etwas zur Identifizierung des Textes und ein Nachladen der übersetzten Texte, wenn die Sprache bspw. in den Optionen gewechselt wird. Will man zudem den Fall abfangen, dass eine bestimmte sprache unvollständig ist (sollte möglichst nicht der Fall sein, kann unter bestimmten Umständen aber passieren), muss man bspw. auf die "Standardsprache" zurückgreifen. Es ist egal, wie man es umsetzt, man muss die "Wörtbucheinträge" anders als die anderen Ressourcen handhaben.


Das habe ich jetzt (noch) nicht ganz verstanden, wie du das meinst. Kannst du das nochmal genauer ausführen? :)
Ich hatte überlegt beim Ändern der Sprache den Language-Cache zu löschen und für die neue Sprache wieder aufzubauen, so dass myLanguage.translate(key) dann die Formulierung für die neue Sprache liefert. Von daher müsste meine bisherige Konstruktion reichen, oder?

Damit wir über die gleiche Konstruktion reden, hier mal der Code der Klasse (Kommentare habe ich mal rausgeworfen, sollte für das geübte C++-Auge trotzdem recht verständlich sein):

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
template <typename Ident, typename Type>
class ResourceManager {
    private:
        std::map<Ident, Type*> cache;
        std::map<Ident, std::uint32_t> counter;
        std::mutex mutex;
    protected:
        virtual Type* fetch(Ident const & identifier) = 0;
        virtual void free(Type* data) = 0;
    public:
        Type* load(Ident const & identifier) {
            Type* data = NULL;
            this->mutex.lock();
            auto node = this->cache.find(identifier);
            if (node != this->cache.end()) {
                if (node->second != NULL) {
                    data = node->second;
                } else {
                    data = this->fetch(identifier);
                    node->second = data;
                }
                this->counter[identifier] += 1;
            } else {
                data = this->fetch(identifier);
                this->cache[identifier] = data;
                this->counter[identifier] = 1;
            }
            this->mutex.unlock();
            return data;
            }
        void unload(Ident const & identifier) {
            this->mutex.lock();
            this->counter[identifier] -= 1;
            if (this->counter[identifier] == 0) {
                auto node = this->cache.find(identifier);
                if (node != this->cache.end()) {
                    if (node->second != NULL) {
                        this->free(node->second);
                    }
                        this->cache.erase(node);
                    }
                }
            this->mutex.unlock();
        }
        void clear() {
            this->mutex.lock();
            for (auto node = this->cache.begin(); node != this->cache.end(); node++) {
                if (node->second != NULL) {
                    this->free(node->second);
                }
            }
            this->mutex.unlock();
        }
};


Aber auch bei den Übersetzungstexten gilt:
Solange du es nicht jetzt gerade brauchst, kannst du es auch später noch implementieren, wenn du es auch wirklich brauchen wirst.


Oh ja ^^ Das ist der Punkt schlecht hin warum ich zur Zeit überlege die Texte zunächst nur auf Englisch auszugeben und auf Übersetzung zu Verzichten :D

LG Glocke

dot

Supermoderator

Beiträge: 9 757

Wohnort: Graz

  • Private Nachricht senden

15

31.01.2014, 16:56

Wieso fetch() und free() als virtuelle Methoden implementieren, wenn das Ding eh schon ein template ist? Sollte man in der map nicht besser smartpointer speichern? Der Code ist nicht exception-safe. Wenn der Destruktor deines ResourceManager aufgerufen wird, ohne dass vorher explizit clear() aufgerufen wurde, dann leaked alles. Wenn fetch() oder free() oder sonst was eine exception wirf, bleibt das Mutex gelocked. http://en.cppreference.com/w/cpp/thread/lock_guard ;)

Dieser Beitrag wurde bereits 4 mal editiert, zuletzt von »dot« (31.01.2014, 17:23)


Sacaldur

Community-Fossil

Beiträge: 2 301

Wohnort: Berlin

Beruf: FIAE

  • Private Nachricht senden

16

31.01.2014, 17:03

Grundsätzlich habe ich 2 Aspekte in fraglichem Absatz angesprochen:
  1. Unterscheidung der Sprachen
  2. Standardsprache

zu 1.:
Entweder hält man alle Wörterbucheinträge im Speicher oder man lädt ggf. eine Sprache nach. Es gibt ein paar Fälle, in denen man wohl auf das 1. zurückgreifen muss (immer dann, wenn die unterschiedlichen Sprachen gleichzeitig gebraucht werden), grundsätzlich (und auch in deinem Fall) dürfte letzteres ausreichend sein. Wenn man alle Einträge im Speicher hält, braucht man zur Identifizierung noch die Sprache, die abgefragt werden soll, in letzterem Fall lädt man bei der Änderung die Sprache und verwirft die vorher geladene. Letzteres hast du also bereits mit eigenen Worten beschrieben:
Ich hatte überlegt beim Ändern der Sprache den Language-Cache zu löschen und für die neue Sprache wieder aufzubauen, so dass myLanguage.translate(key) dann die Formulierung für die neue Sprache liefert.

zu 2.:
Wenn beim Laden festgestellt wird, dass bestimmte "Übersetzungen" nicht da sind, müsste ggf. auf eine andere Sprache (die "Standardsprache", bspw. englisch) zurückgegriffen werden. Hier muss man irgendwie erkennen, welche Übersetzungen erforderlich sind, allerdings ist das grundsätzlich unabhängig von dem oberen Punkt (wenn man alles im Speicher hat, ist es ohne ein Nachladen zur Laufzeit möglich, man könnte die "Standardsprache" permanent im Speicher halten etc.).

Diese beiden Aspekte sollten einfach nur den Unterschied zu den anderen Ressourcen aufzeigen, da du ohnehin zwischen den verschiednen Arten an Ressourcen unterscheidest, dürften sich für dich aber keine Änderungen ergeben. ;)

[...], sollte für das geübte C++-Auge trotzdem recht verständlich sein
Ist in diesem Fall aber nicht gegeben. (Ich denke, ich konnte aber dennoch verstehen, was dein Code machen soll.)
Spieleentwickler in Berlin? (Thema in diesem Forum)
---
Es ist ja keine Schande etwas falsch zu machen, als Programmierer tu ich das täglich, [...].

17

31.01.2014, 19:09

Wieso fetch() und free() als virtuelle Methoden implementieren, wenn das Ding eh schon ein template ist? Sollte man in der map nicht besser smartpointer speichern? Der Code ist nicht exception-safe. Wenn der Destruktor deines ResourceManager aufgerufen wird, ohne dass vorher explizit clear() aufgerufen wurde, dann leaked alles. Wenn fetch() oder free() oder sonst was eine exception wirf, bleibt das Mutex gelocked. http://en.cppreference.com/w/cpp/thread/lock_guard ;)


fetch() und free() hängen vom entsprechenden Typ ab der gecached werden soll. So wird eine SDL_Surface anders aus einer Datei geladen als eine Schriftart usw. Daher muss vom ResourceManager der konkrete Manager abgeleitet und beide Methoden implementiert werden (zumindest war das die Idee dahinter).

Was die Exeptions angeht muss ich nochmal drüber nachdenken. Danke :) Das mit dem Destruktor: Ja, das habe ich bisher außer acht gelassen, weil ich in meiner Image-Klasse eine statische Instanz des Managers ablege (so dass ich das Problem nicht habe). Prinzipiell sollte ich das aber fixen! :)

Diese beiden Aspekte sollten einfach nur den Unterschied zu den anderen Ressourcen aufzeigen, da du ohnehin zwischen den verschiednen Arten an Ressourcen unterscheidest, dürften sich für dich aber keine Änderungen ergeben. ;)


Oki :)

Ist in diesem Fall aber nicht gegeben. (Ich denke, ich konnte aber dennoch verstehen, was dein Code machen soll.)


Auch gut :)

LG Glocke

Werbeanzeige