Modellklasse mit c++ und OpenGL - Blender Exporter Script und Model-Viewer



  • OpenGL hat keine Möglichkeit Meshes und ähnliches in einer Datei zu speichern. Deshalb habe ich mir eine Möglichkeit überlegt wie man so was speichern könnte.
    Ich habe mir also überlegt aus meinem Modell eine C++ Klasse zu machen, anstatt alles aus einer Textdatei zu laden und dann mit c++ zu verarbeiten. So muss man also immer neu compilieren wenn man was an seinem Modell ändert. Auf der anderen Seite dürfte diese Methode aber Leistungseffizienter sein, habe ich mir gedacht. Soweit die Theorie.
    Dann habe ich begonnen so eine Klasse zu schreiben, als Modell habe ich eine einfache Plane genommen.
    Ich habe die vier Punkte also in einem Array gespeichert und mir dazu eigene Transformationsroutinen geschrieben, statt die Matrix zu manipulieren.
    Insgesamt umfasst die Klasse Translations-, Skalierungs-, Rotations- eine Draw-, sowie eine Resetfunktion-Funktion.
    Weitere Funktionsmöglichkeiten währen denke ich zB. Rotation von einzelnen Verts für Gelenke oder eine Rotation um eine Beliebige Achse.

    Nun stellen sich mir zwei Fragen:
    1. Ist es überhaupt sinnvoll Modelle in Form von C++ Klassen zu speichern?
    2. ist es schneller meine Rotations- translations und Skalierungsfunktion zu benutzen oder die Matrix mittels Push und Pop und glTranslate, glRotate und glScale zu manipulieren?

    sry für diesen Mamutcodebrocken, aber wo ist denn hier der Button für einen Anhang O.o

    class plane
    {
    public:
    
    float position_x[4];
    float position_y[4];
    float position_z[4];
    float position_u[4];
    float position_v[4];
    int counter;
    int number_of_verts;
    float position_to_translate_x;
    float position_to_translate_y;
    float position_to_translate_z;
    float width;
    float height;
    float length;
    float degree_x;
    float center_x;
    float degree_y;
    float center_y;
    float degree_z;
    float center_z;
    
    	plane()
    	{
    	position_x[0] = -1; position_y[0] = 0; position_z[0] = -1; position_u[0] = 0; position_v[0] = 0; //hinten links
            position_x[1] = -1;  position_y[1] = 0; position_z[1] = 1; position_u[1] = 0; position_v[1] = 1; //hinten rechts
            position_x[2] = 1;  position_y[2] = 0; position_z[2] = 1;  position_u[2] = 1; position_v[2] = 1; //vorne rechts
            position_x[3] = 1; position_y[3] = 0; position_z[3] = -1;  position_u[3] = 1; position_v[3] = 0; //vorne links
            number_of_verts = 4;
    	}
    
            void reset_position()
            {
            position_x[0] = -1; position_y[0] = 0; position_z[0] = -1; position_u[0] = 0; position_v[0] = 0; //hinten links
            position_x[1] = 1;  position_y[1] = 0; position_z[1] = -1; position_u[1] = 1; position_v[1] = 0; //hinten rechts
            position_x[2] = 1;  position_y[2] = 0; position_z[2] = 1;  position_u[2] = 1; position_v[2] = 1; //vorne rechts
            position_x[3] = -1; position_y[3] = 0; position_z[3] = 1;  position_u[3] = 0; position_v[3] = 1; //vorne links
            }
    
            void translate_x(float translate_x)
            {      
                    //position in x richtung bestimmen
                    position_to_translate_x = translate_x;
    		for(counter = 0; counter < number_of_verts; counter++)
    		{
    		position_x[counter] = position_to_translate_x + position_x[counter];
    		}
            }
    
            void translate_y(float translate_y)
            {
                    //position in y richtung bestimmen
                    position_to_translate_y = translate_y;
    		for(counter = 0; counter < number_of_verts; counter++)
    		{
    		position_y[counter] = position_to_translate_y + position_y[counter];
    		}
            }
    
            void translate_z(float translate_z)
            {
                    //position in z richtung bestimmen
                    position_to_translate_z = translate_z;
    		for(counter = 0; counter < number_of_verts; counter++)
    		{
    		position_z[counter] = position_to_translate_z + position_z[counter];
    		}
            }
    
            void scale_x(float w)
            {
    	float translate;
    	center_x =0;
    		//Mittelpunkt bestimmen(x)
    		for(int counter = 0; counter < number_of_verts; counter++)
    		{
    		center_x = center_x + position_x[counter]; 
    		}
    	center_x = center_x / number_of_verts;
    		//Wenn der Mittelpunkt nicht 0 ist, hinbewegen
    		if(center_x != 0)
    
    		{
                    translate = center_x * (-1);
                    translate_x(translate);                
    		}
    	        //breite berechnen
                    width = w;
    		for(int counter = 0; counter < number_of_verts; counter++)
    		{
    		position_x[counter] = width * position_x[counter]; 
    		}
           		//wieder zurück bewegen
    		if(center_x != 0)
    		{
    		translate_x(center_x);
    		}
    	}
    
            void scale_y(float h)
            {
    		float translate;
    		center_y = 0;
    		//Mittelpunkt bestimmen(y)
    		for(int counter = 0; counter < number_of_verts; counter++)
    		{
    		center_y = center_y + position_y[counter]; 
    		}
    	center_y = center_y / number_of_verts;
    		//Wenn der Mittelpunkt nicht 0 ist, hinbewegen
    		if(center_y != 0)
    		{
                    translate = center_y * (-1);
                    translate_y(translate);                
    		}
    		//hoehe berechnen
                    height = h;
    		for(int counter = 0; counter < number_of_verts; counter++)
    		{
    		position_y[counter] = height * position_y[counter];
    		}
    		//wieder zurück bewegen
    		if(center_y != 0)
    		{
    		translate_y(center_y);
    		}
    	}
    
            void scale_z(float l)
            {
    		float translate;
    		center_z = 0;
    		//Mittelpunkt bestimmen(y)
    		for(int counter = 0; counter < number_of_verts; counter++)
    		{
    		center_z = center_z + position_z[counter]; 
    		}
    	center_z = center_z / number_of_verts;
    		//Wenn der Mittelpunkt nicht 0 ist, hinbewegen
    		if(center_z != 0)
    		{
                    translate = center_z * (-1);
                    translate_z(translate);                
    		}
    		//laenge berechnen
                    length = l;
    		for(int counter = 0; counter < number_of_verts; counter++)
    		{
    		position_z[counter] = length * position_z[counter]; 
    		}
    		//wieder zurück bewegen
    		if(center_z != 0)
    		{
    		translate_z(center_z);
    		}
    	}
    
            void rotate_y(float D_Y)
            {       
    		float translate;
    		center_x = 0;
    		center_z = 0;
    		//Mittelpunkt bestimmen(x)
    		for(int counter = 0; counter < number_of_verts; counter++)
    		{
    		center_x = center_x + position_x[counter];
    		}
    	center_x = center_x / number_of_verts;
    		//Wenn der Mittelpunkt nicht 0 ist, hinbewegen
    		if(center_x != 0)
    		{
                    translate = center_x * (-1);
                    translate_x(translate);                
    		}
    		//Mittelpunkt bestimmen(y)
    		for(int counter = 0; counter < number_of_verts; counter++)
    		{
    		center_z = center_z + position_z[counter]; 
    		}
    	center_z = center_z / number_of_verts;
    		//Wenn der Mittelpunkt nicht 0 ist, hinbewegen
    		if(center_z != 0)
    		{
                    translate = center_z * (-1);
                    translate_z(translate);                
    		}
            degree_y = D_Y;
    	float sinus = sin_d(degree_y);
    	float cosinus = cos_d(degree_y);
    	float pos_x_alt;
            	for(int counter = 0; counter < number_of_verts; counter++)
    		{
                    pos_x_alt = position_x[counter];
                    position_x[counter] = position_z[counter] * sinus + position_x[counter] * cosinus;
    		position_z[counter] = position_z[counter] * cosinus - pos_x_alt * sinus;
    		}
    		if(center_x != 0)
    		{
    		translate_x(center_x);
    		}
    		if(center_z != 0)
    		{
    		translate_z(center_z);
    		}	
            }
    
            void rotate_x(float D_X)
            {
    	float translate;
    		center_y = 0;
    		center_z = 0;
    		//Mittelpunkt bestimmen(y)
    		for(int counter = 0; counter < number_of_verts; counter++)
    		{
    		center_y = center_y + position_y[counter];
    		}
    	center_y = center_y / number_of_verts;
    		//Wenn der Mittelpunkt nicht 0 ist, hinbewegen
    		if(center_y != 0)
    		{
                    translate = center_y * (-1);
                    translate_y(translate);                
    		}
    		//Mittelpunkt  bestimmen(z)
    		for(int counter = 0; counter < number_of_verts; counter++)
    		{
    		center_z = center_z + position_z[counter]; 
    		}
    	center_z = center_z / number_of_verts;
    		//Wenn der Mittelpunkt nicht 0 ist, hinbewegen
    		if(center_z != 0)
    		{
                    translate = center_z * (-1);
                    translate_z(translate);                
    		}
    	degree_x = D_X;
    	float sinus = sin_d(degree_x);
    	float cosinus = cos_d(degree_x);
    	float pos_y_alt;
    		for(int counter = 0; counter < number_of_verts; counter++)
    		{
    		pos_y_alt = position_y[counter];
                    position_y[counter] = position_y[counter] * cosinus - position_z[counter] * sinus;
    		position_z[counter] = pos_y_alt * sinus + position_z[counter] * cosinus;
    		}
    		if(center_y != 0)
    		{
    		translate_y(center_y);
    		}
    		if(center_z != 0)
    		{
    		translate_z(center_z);
    		}
    	}
    
    	void rotate_z(float D_Z)
            {
    	float translate;
    	center_x = 0;
    	center_y = 0;
    		//Mittelpunkt  bestimmen(x)
    		for(int counter = 0; counter < number_of_verts; counter++)
    		{
    		center_x = center_x + position_x[counter];
    		}
    	center_x = center_x / number_of_verts;
    		//Wenn der Mittelpunkt nicht 0 ist, hinbewegen
    		if(center_x != 0)
    		{
                    translate = center_x * (-1);
                    translate_x(translate);                
    		}
    		//Mittelpunkt bestimmen(y)
    		for(int counter = 0; counter < number_of_verts; counter++)
    		{
    		center_y = center_y + position_y[counter];
    		}
    	center_y = center_y / number_of_verts;
    		//Wenn der Mittelpunkt nicht 0 ist, hinbewegen
    		if(center_y != 0)
    		{
                    translate = center_y * (-1);
                    translate_y(translate);                
    		}
    	degree_z = D_Z;
    	float sinus = sin_d(degree_z);
    	float cosinus = cos_d(degree_z);
    	float pos_x_alt;
    		for(int counter = 0; counter < number_of_verts; counter++)
    		{
    		pos_x_alt = position_x[counter];
                    position_x[counter] = position_x[counter] * cosinus - position_y[counter] * sinus;
    		position_y[counter] = pos_x_alt * sinus + position_y[counter] * cosinus;
    		}
    		if(center_y != 0)
    		{
    		translate_y(center_y);
    		}
    		if(center_x != 0)
    		{
    		translate_x(center_x);
    		}
    	}
    
            void draw()
            {
    
            glBegin(GL_QUADS); 
            glTexCoord2f(position_u[0], position_v[0]); glVertex3f(position_x[0], position_y[0], position_z[0]);   
            glTexCoord2f(position_u[1], position_v[1]); glVertex3f(position_x[1], position_y[1], position_z[1]);  
            glTexCoord2f(position_u[2], position_v[2]); glVertex3f(position_x[2], position_y[2], position_z[2]);  
            glTexCoord2f(position_u[3], position_v[3]); glVertex3f(position_x[3], position_y[3], position_z[3]);
            glEnd();
            }
    };
    


  • Isodrink schrieb:

    OpenGL hat keine Möglichkeit Meshes und ähnliches in einer Datei zu speichern.

    Das hat OpenGL zwar nicht, aber OpenGL hat funktionen, denen du komplette Meshes als Datenstruktur übergeben kannst. Siehe: glInterleavedArrays , glDrawArray , etc. Die sind auf jeden Fall schneller, weil du der Grafikkarte die Mesh-Daten in einem Rutsch übergeben kannst, statt für jeden Punkt eine Funktion aufzurufen. Ob du das Mesh vorher transformieren willst, musst du testen.

    Ich halte es nicht für sinnvoll, die Modell-Daten in Klassen zu kodieren.



  • ProgChild schrieb:

    Isodrink schrieb:

    OpenGL hat keine Möglichkeit Meshes und ähnliches in einer Datei zu speichern.

    Das hat OpenGL zwar nicht, aber OpenGL hat funktionen, denen du komplette Meshes als Datenstruktur übergeben kannst. Siehe: glInterleavedArrays , glDrawArray , etc. Die sind auf jeden Fall schneller, weil du der Grafikkarte die Mesh-Daten in einem Rutsch übergeben kannst, statt für jeden Punkt eine Funktion aufzurufen. Ob du das Mesh vorher transformieren willst, musst du testen.

    Das hieße ich müsste meine Draw-funktion verändern...

    ProgChild schrieb:

    Ich halte es nicht für sinnvoll, die Modell-Daten in Klassen zu kodieren.

    Aber warum? Diese Methode ist doch schneller als ein Modell aus einer Datei zu laden oder so etwas...

    Was ist schneller: meine Selbst geschriebenen Funktionen oder die von OpenGL gestellten(glTranslate, glRotate und glScale) mit push und pop?



  • Isodrink schrieb:

    Aber warum? Diese Methode ist doch schneller als ein Modell aus einer Datei zu laden oder so etwas...

    Nein sie ist auch nicht schneller.

    Deine Modell Daten, werden ja dann halt vom Betriebssystem mit in den Arbeitsspeicher geladen, statt von dir. Aber wenn du Funktionen wie glVertex3 benutzt ist das langsam, weil die Funktionen einen Overhead mitbringen.

    Was also genau so schnell ist: Bring die Daten in ein Format, so dass du sie direkt der Funktion glInterleavedArrays oder Ähnlichem übergeben kannst und schreibe Diese Daten einfach roh, wie sie sind in eine Datei. Die Daten lädst du ganz einfach in ein char-Array und übergibst es dann der OpenGL funktion. Das wäre schnell.

    Und jetzt noch warum es nicht gut ist, die Daten fest zu kodieren: Solange du alleine Arbeitest geht das ganze noch. Aber stell dir mal vor, du bittest einen Freund, für dich ein 3D-Modell zu entwickeln und mit dem Spiel zu testen. Dann musst du ihm den Compiler geben, den Quelltext, einrichten, dass er compilieren kann, usw. Du kannst aber auch einfach die Daten aus einer Datei laden und ihm die Executable geben.

    Du bist einfach viel flexiebler. Außerdem kannst du, wenn du willst, diese Modell Daten immernoch als Resourcen in deine Executable mit einbinden.

    Isodrink schrieb:

    Was ist schneller: meine Selbst geschriebenen Funktionen oder die von OpenGL gestellten(glTranslate, glRotate und glScale) mit push und pop?

    Das kannst du nur wissen, wenn du es testest. Ich denke, das Ausrechnen der Matrizen, macht bei OpenGL auch die CPU, das anwenden der Matrix auf das Mesh macht, denke ich, die Grafikkarte. Das vermute ich. Daraus folgt, dass wenn du besser, als die Treiberprogrammierer bist, du schnellere Matrixoprationen programmieren kannst. Ob du das schaffst ist eine andere Frage.

    Ich persönlich fand meine eigenen Matrixklassen flexiebler.



  • Ok, danke für deinen umfangreichen Post, mir ist einiges klar geworden 🙂

    Wenn ich so eine einfachen Würfel speichern wollte klappt das in Meinem Kopf schon ganz gut, aber ein Paar Fragen habe ich noch:
    Was ist aber, wenn ich auf das Mesh vor dem Zeichnen noch Funktionen anwenden muss? Also wenn ich Farbe, Größe ändern, oder sogar mit einem Skelet arbeiten will. Dann brauche ich Funktionen, die das Mesh verändern, bevor es zum Zeichnen übergeben wird!

    Würde man dann eine Scriptsprache verwenden? So kann man dann alles auch nach dem Compilieren noch ändern.
    Auf was ich auf keinen Fall verzichten möchte ist der objektorientierte Ansatz!

    Ich persönlich fand meine eigenen Matrixklassen flexiebler.

    Was meist du damit?



  • Isodrink schrieb:

    Wenn ich so eine einfachen Würfel speichern wollte klappt das in Meinem Kopf schon ganz gut, aber ein Paar Fragen habe ich noch:
    Was ist aber, wenn ich auf das Mesh vor dem Zeichnen noch Funktionen anwenden muss? Also wenn ich Farbe, Größe ändern, oder sogar mit einem Skelet arbeiten will. Dann brauche ich Funktionen, die das Mesh verändern, bevor es zum Zeichnen übergeben wird!

    Dann sind wir ja nicht mehr bei einfach. Du brauchst für das ganze ja eine Datenstruktur. Es wäre ja vollkommen Wahnsinn, das fest kodieren zu wollen. Du hättest also dein Modell und dein Skelett. Dann würdest du die Vertices in deiner Datenstruktur ja dem Skelett anpassen, also die Vertices bewegen.

    Du hättest also ein Modell bei dem du eine Liste von Vertices hast, die von den Faces referenziert werden. Dann manipulierst du die Vertices und übergibst das ganze an OpenGL. Soweit ich weiß, kann OpenGL auch mit solchen indizierten Datenstrukturen umgehen. Du müsstest mal nach der entsprechenden Funktion suchen.

    Isodrink schrieb:

    Würde man dann eine Scriptsprache verwenden? So kann man dann alles auch nach dem Compilieren noch ändern.
    Auf was ich auf keinen Fall verzichten möchte ist der objektorientierte Ansatz!

    Ja, aber du wärst wieder bei dem selben Problem: Erkläre deinem Freund, der noch nie Programmiert hat, mal deine Skriptsprache zu benutzen. Ich kann die nur raten, die Daten vom Programm sauber zu trennen.

    Isodrink schrieb:

    Was meist du damit?

    Beispiel: Quaternionen. Ich kann einfach die Rotation eines Objektes als Quaternion abspeichern.

    Außerdem kann man auf einfache Weise Zwischenergebnisse speichern.



  • Naja, mein Fahrplan sah eigentlich so aus, dass ich mir ein geeignetes Format für die Daten ausdenke, und dann ein Programm schreibe, dass solche Daten aus einer Datei, die ich mit Blender abspeichere auslese und in mein Format wandle. Dann macht es auch keine Probleme mehr das ganze fest zu codieren oder? 😕
    Und das Risiko meinem Freund erklären zu müssen, wie man Primitive mit Blender macht gehe ich ein 🙂 (Wenn du meine Freunde kennen würdest *g*)



  • Isodrink schrieb:

    Naja, mein Fahrplan sah eigentlich so aus, dass ich mir ein geeignetes Format für die Daten ausdenke, und dann ein Programm schreibe, dass solche Daten aus einer Datei, die ich mit Blender abspeichere auslese und in mein Format wandle. Dann macht es auch keine Probleme mehr das ganze fest zu codieren oder? 😕

    Was versprichst du dir davon, außer dass du Flexibilität aufgibst? Deine Daten müssten doch eh in eine Datenstruktur rein. Wenn du willst, kannst du deine Datei ja so gestalten, dass die Daten in einem Rutsch einfach nur in die Struktur rein kopiert werden müssen.



  • Es ist im Prinzip egal, da die Daten hinterher sowieso durch den Arbeitsspeicher gejagt werden. Wird beides keine Auswirkungen auf die Performance haben...
    Zumindest nicht bei kleinreren Framework's...



  • Gut nehmen wir an, ich schreibe die Positionen von Verts, Texturen, Farben und Knochen in eine txt Datei oder so.
    Wie bekomme ich daraus dann ein Objekt?



  • Isodrink schrieb:

    Gut nehmen wir an, ich schreibe die Positionen von Verts, Texturen, Farben und Knochen in eine txt Datei oder so.
    Wie bekomme ich daraus dann ein Objekt?

    Ich hab zwar nicht alles gelesen aber:
    Schreib dir doch einfach einen Loader.
    Dem übergibst du den Pfad der Datei und gibt nen Pointer auf das von dir gewünschte Objekt zurück.
    (Einfach alles auslesen und dann damit ein neues Objekt erstellen...)


  • Mod

    bei nehe gibt es einige tutorials dazu, mach dir das leben einfach und lies sie durch 😉



  • Gut nehmen wir an, ich schreibe die Positionen von Verts, Texturen, Farben und Knochen in eine txt Datei oder so.
    Wie bekomme ich daraus dann ein Objekt?

    Ganz einfach, du legst dir eine genaue Struktur an, beispielsweise:

    2 3 2 0 1
    

    2, 3 und 2 wären beispielsweise die x, y, z-Koordinaten die du ausliest. 0 und 1 sind zum Beispiel die Texturkoordinaten. Kannste so machen, lieste einfach ein und verarbeitest jede Zeile. Das war's im Prinzip schon, müsste bei nehe Tutorial 6 oder 8 sein.



  • Das ich einen Loader schreiben kann und alle Daten in das Programm einlesen kann ist mir schon klar 😉
    Nur wie erzeuge ich dann daraus eine Klasse, von der ich dann Istanzen erzeugen kann? Meiner Meinug nachgeht das nur mit einer Scriptsprache, aber ich lasse mich natürlich gerne korrigieren 🙂



  • Wozu willst du von dieser Klasse dann noch Instanzen erzeugen?
    Wenn das Model einmal im Speicher liegt, reicht das doch.



  • Naja, wenn ich zb. für ein Tower-Defence Spiel oder so, Einheiten mach, brauche ich nicht nur eine, sondern mehrere. Jede dieser Instanzen hat dann die gleichen Eigenschaften, jede aber andere Werte, wie zB. Lebenspunkte.
    Mir ist allerdings schon eine Idee gekommen, wie ich das machen könnte......mal sehen 🙂



  • Aber was haben denn bitte Eigenschaften wie Lebenspunkte etc. mit dem 3D-Modell an sich als Model zu tun? Das bleibt doch immer gleich, also musst du dieses ja nur einmal laden.



  • Das ich einen Loader schreiben kann und alle Daten in das Programm einlesen kann ist mir schon klar 😉
    Nur wie erzeuge ich dann daraus eine Klasse, von der ich dann Istanzen erzeugen kann? Meiner Meinug nachgeht das nur mit einer Scriptsprache, aber ich lasse mich natürlich gerne korrigieren 🙂

    Ich glaube du hast die Idee dahinter noch nicht so ganz verstanden.

    Isodrink schrieb:

    Naja, wenn ich zb. für ein Tower-Defence Spiel oder so, Einheiten mach, brauche ich nicht nur eine, sondern mehrere. Jede dieser Instanzen hat dann die gleichen Eigenschaften, jede aber andere Werte, wie zB. Lebenspunkte.
    Mir ist allerdings schon eine Idee gekommen, wie ich das machen könnte......mal sehen 🙂

    In der Datei stehen NUR die Daten für Vertices und Texturkoordinaten.
    Also z.B.:

    10 345 12 0 0
    

    Mehr nicht.
    Und du baust dir jetzt einfach einen Loader der die Daten Zeile für Zeile ausliest und in irgendeiner Struktur abspeichert.
    Somit hast du dein 3D Model geladen und kannst dann alles damit machen, was du willst.
    Wenn du jetzt z.B. bei einem Towerdefensegame die Gegner darstellen willst, lädst du einmal am Anfang das Model und speicherst es in (z.b.) einer EnemyManager Klasse die alle Gegner in einer Liste verwaltet. Wenn du jetzt alles rendern willst musst du nur das Model nach den Daten der einzelnen Gegner ausrichten und renderst es einfach.
    Das Model selbst hat rein gar nichts mit Lebenspunkten, Koordinaten, Waffen oder sonstwas zu tun.
    Zum Speichern der Vertices kannst du ja einfach einen Vector/Liste/Whatever verwenden.

    Das Model für jede Einheit zu laden wäre doch vollkommen sinnlos und obendrein speicherfressend.
    Die Gegner selbst haben nur eine Rotation und Position - zusätzlich zu den anderen Daten die das Spielinterne Verhalten betreffen



  • Ok, wenn ich das richtig verstanden habe, kann ich eine Klasse Enemy oder so schreiben, die dann eine Funktion loadData enthält,die die Datei ausliest und die ich nur einmal bei der Initialisierung aufrufe?



  • Na ja, du solltest das ganze trennen.
    Ne Klasse Enemy ist schonmal richtig.
    Dann hast du noch ne Klasse Model, oder so. Die beinhaltet einfach nur das 3D-Model und die entsprechenden Funktionen zum Verwalten, und die kann auch ein Model laden. Wenn du meinetwegen 5 3D-Modelle hast, erstellst du halt 5 Instanzen dieser Klasse und lädst in jeder das jeweilige Model.
    Jetzt brauchst du dem Gegner nur noch die ID des Models zuweisen.
    Das Ganze lässt sich zwar noch erweitern und verbessern, aber vom Grundprinzip ist es schonmal das, was wir meinen 😉


Log in to reply