Anfänger-Frage: Pointer-list auf eine list?



  • Hallo liebes Forum,

    ich mache mich jetzt unbeliebt und stelle mal ganz naiv meine erste Frage - sorry, bin noch C++-Anfänger und hab noch keinen Mentor/"Mensch, dem ich dumme Fragen stellen kann" 🙂 Medieninformatik-Studium fängt bald an, und ich will mich schonmal vorab 'n bisschen schlauer machen. 😃

    Vorab:
    Ich tüftele gerade an einem Jump'n'Run, meine Frage ist aber denk ich doch zu allgemein für die Gaming Corner...

    Folgende Überlegung:
    Sämtliche Objekte in dem Game (Spieler, Gegner, "feste" Umgebung -> alles Einzel-Sprites) werden praktisch gleiche Eigenschaften und Fähigkeiten haben, folglich habe ich auch nur eine Klasse GameObject. Die packe ich in eine list<GameObject>, die pro Level natürlich einmal geleert und befüllt (per XML-Datei) wird - also 1. Liste leeren, 2. Level laden.

    Ich hab also den Vorteil (so denk ich mir das zumindest), dass ich einfach eine Schleife durchlaufen und alles für alle Objekte in einem Rutsch durchführen kann.

    Jetzt würde ich natürlich ganz gerne auch mal eine Unterauswahl (z.B. Gegner) durchlaufen lassen bzw. bestimmte Objekte auch bei ihrem Namen nennen. Und da war meine (vielleicht auch bescheuerte) Idee, weitere Listen mit Pointern auf die "AlleGameObjects"-Liste zu erstellen. (aus den Listen wird während des Spiels auch nix rausgelöscht - müssen also nur einmalig abgeglichen werden)

    Lange Rede, wenig Sinn - kann man das so machen? Oder ist das doch eine total schwachsinnige Idee?

    Wie gesagt, bin noch Anfänger - ich hoffe, man verzeiht mir, wenn ich totalen Blödsinn schwafele. 😉 🙄



  • Wenn du abgeleitete Klassen für Gegner, Spieler, Projektile, Landschaftsobjekte etc. hast, warum speicherst du sie nicht direkt in verschiedenen Containern?

    std::vector<Gegner> gegner;
    std::vector<Projektil> projektile;
    ...
    

    Alles in einen Container zu packen läuft bei Dingen wie Kollision, wo zwei Objekte involviert sind, auf mühsame Fallunterscheidungen hinaus – oder auf Dynamic Dispatch, was auch nicht gerade trivial ist. Oft ist es besser, man kennt die Typen zur Kompilierzeit.



  • Wenn du die Objekte nicht als Pointer in die Vektoren steckst sondern als Objekte, dann kann es dir (bei weiterem Einfügen von mehr Objekten) passieren das diese später nicht mehr über denselben Pointer erreichbar sind

    #include <vector> 
    int main() 
    {
      std::vector<int> meineInts;
      std::vector<*int> pointerAufMeineInts;
    
      meineInts.reserve(1000); // mach Platz für 1000
    
      for(int i=0;i<1000;++i)
        meineInts.push_back(i++); // objekte speichern
    
      for(int i=0; i< meineInts.size(); i+=4)
        pointerAufMeineInts.push_back(&(meineInts.at(i))); // jeden 4ten merken
    
      for(int i=1000; i<10000; ++i)
        meineInts.push_back(i++); // mehr objekte hinzufügen - verursacht vermutlich ein umkopieren aller Werte, also Pointer ungültig
    
      // jetzt hier irgendwas mit den gemerkten pointer machen kann schief gehen.
    }
    

    Eine saubere Trennung ala Nexus ist also erstrebenswert - auch wenn die Objekte streng genommen alles Gleich sind. Alternativ könntest du etwas mit std::multimap bauen (eine ID pro "Typ") - ist aber frickeliger. Statt Liste würde ich warscheinlich den Vector nehmen, und schau dir am besten noch iteratoren an 🙂 die sind super um durch sowas durchzugehen - netter als 'for (bla=0; bla<blub; ++bla)'



  • Meine (vielleicht auch nicht wirklich gute) Überlegung: es gibt wirklich nur eine Klasse, weil jedes Objekt auf dem Bildschirm alles kann - evtl. sogar vom Spieler die Steuerung übernommen werden. Und da die Physik von Box2D übernommen wird, kümmere ich mich (sozusagen) gar nicht um die Kollision.

    Weil die Unterschiede zwischen den Objekten wirklich marginal sind - z.B. Gegner wie Landschaftsobjekte können entlang eines Pfades wandern, können dem Spieler Schaden zufügen, können sich drehen, blah...

    Deswegen seh ich halt den Vorteil in einer großen list (Du empfiehlst den vector??) --- alles wird gleich behandelt.



  • Okay, ich dachte, du würdest mit Polymorphie arbeiten. Aber auch hier kannst du die einzelnen Dinge getrennt haben, ich würde die Vorteile nicht unterschätzen. Du könntest aber auch einen separaten Container mit Pointern auf alle Objekte aus unterschiedlichen Containern merken.

    std::vector ist meist die beste Wahl, allerdings können Iteratoren, Zeiger und Referenzen auf Objekte ungültig werden, was bei std::list nicht passiert.

    Die Alternative wäre, eine Funktion auf allen Elementen aufzurufen, die aber nur bei Gegnern etwas tut. Ist jedoch auch wieder mit Fallunterscheidungen verbunden.



  • Oder anders gesagt: was mir eigentlich missfällt - wenn ich mehrere Objekt-Listen/Vektoren/wasauchimmer habe, muss ich ja im Prinzip für jede Objektliste doch wieder die gleiche Schleife durchlaufen - also sagen wir 1x den Spieler zu allen Objekten berechnen und z.B. 2x die selbe Schleife für Gegner und "Tyles"/Landschaft...

    Dann kommen vielleicht noch Bonii (Lebensenergie, die ja im Endeffekt genauso wie Gegner und Tyles agiert, nur eben "positiven" Schaden zufügt) dazu, Events (können auch wieder alles, was die anderen können - bis auf eben den Event ausführen - und selbst das kann vielleicht ein Gegner auch, wenn man ihm zu nahe kommt z.B.).



  • Gnarz-Farz schrieb:

    Oder anders gesagt: was mir eigentlich missfällt - wenn ich mehrere Objekt-Listen/Vektoren/wasauchimmer habe, muss ich ja im Prinzip für jede Objektliste doch wieder die gleiche Schleife durchlaufen - also sagen wir 1x den Spieler zu allen Objekten berechnen und z.B. 2x die selbe Schleife für Gegner und "Tyles"/Landschaft...

    Wo liegt das Problem? Die Schleife kannst du in eine Funktion auslagern. Codeduplikation hast du dadurch keine.

    Es heisst übrigens "Tiles" 😉



  • Tiles - danke 🙂

    OK, fräg ich halt mal andersrum - Ihr sprecht von den Vorteilen der Unterscheidung, wo wären die in meinem Fall? (sorry, blöd gefragt)

    Ich seh da halt nicht so große Vorteile... Anfänger, sag ich ja 😉



  • Naja, ich hab selbst schon mal ein Jump'n'Run geschrieben mit ziemlich ähnlichem Aufbau. Dass ich getrennte Container (in meinem Fall für unterschiedliche abgeleitete Klassen) benutzte, hatte verschiedene Vorteile.

    Zum einen sind spezifische Aktionen wie "setze alle Gegner zurück" möglich (denn das kann die Gegner-Klasse nicht selbst, auch wenn sie virtuelle Methoden hat). Zum Anderen kann man viel leichter Aktionen zwischen zwei Objekten eines bestimmten Typs durchführen. Kollisionen liefen bei mir je nach beteiligten Objekten verschieden ab, z.B. konnten abgefeuerte Waffen nicht den Spieler selbst treffen. Wenn man das versucht generisch in einer Klasse zu lösen, ist oft doch eine komplexe Logik oder fortgeschrittene Techniken wie Double-Dispatch notwendig. Und generell hat man einfach mehr Typsicherheit und kann auf explizite Fallunterscheidungen verzichten, wenn die Typen bereits zur Kompilierzeit bekannt sind.

    P.S. Ob es nun tatsächlich verschiedene Typen (abgeleitete polymorphe Klassen) oder nur ein Typ ist, der gleichzeitig unterschiedliche Dinge repräsentiert, macht diesbezüglich keinen grossen Unterschied. Allerdings ist Polymorphie oft die bessere Wahl, sobald die Unterschiede grösser werden (wieder wegen der Fallunterscheidungen).


Log in to reply