Das stimmt, das klappt so nicht. Die Aufgabe, alle uGaAs anzulegen, hat nicht die uGaA-Klasse. Eine Spielwelt besteht aus Spieler, Monstern, Hindernissen. Wer also sollte die uGaAs anlegen? Richtig: die Spielwelt. In C++ ist es sehr wichtig, sich bei langlebigen Objekten klarzumachen, wem sie gehören. In diesem Fall ganz einfach: der aktuelle Spielablauf hat eine Spielwelt, die Spielwelt hat Spieler, Monster, Hindernisse.
Und das Stichwort "langlebig" besagt auch, was noch an Deiner Überlegung falsch ist. Wie lange leben denn die Hindernisse in Deiner Spielwelt? Meistens so lange wie die Spielwelt selbst. Wenn Du am Anfang eine Spielwelt erstellst, also von Platte lädst oder live mit Hindernissen und dem Spieler bevölkerst, dann werden die BasisObjekte erzeugt. Und am Ende, wenn der Spielablauf fertig ist, wird die Spielwelt gelöscht. Und die Spielwelt räumt dann auch alle Objekte ab. Vielleicht kommen zwischendurch noch neue Objekte hinzu, oder manche Objekte werden vorzeitig gelöscht, aber das ist nebensächlich. Die längste mögliche Lebensdauer ist hier von Interesse. Demzufolge mein Vorschlag:
|
Quellcode
|
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
|
class Spielwelt
{
/// Konstruktor
Spielwelt()
{
// hier kannst Du für den Anfang erstmal die Hindernisse und den Spieler erzeugen
}
/// Destruktor
~Spielwelt()
{
// alle Objekte abräumen
for( BasisObjekt* obj : mObjekte )
delete obj;
}
/// Fügt ein neues Objekt hinzu
void AddObjekt( BasisObjekt* obj ) { mObjekte.push_back( obj); }
/// Lässt die Spielwelt arbeiten. Aktuell: alle Objekte kommen einmal dran
void Arbeiten()
{
for( BasisObjekt* obj : mObjekte )
obj->Arbeiten();
}
/// Zeichnet die Spielwelt
void Zeichnen( ZeichenProzess& prozess ) const
{
for( BasisObjekt* obj : mObjekte )
obj->Zeichnen( prozess);
}
protected:
// alle Objekte in der Spielwelt
std::vector<BasisObjekt*> mObjekte;
};
|
In Deiner Lösung oben legst Du die Objekte lokal an, sie werden automatisch abgeräumt, wenn der aktuelle Gültigkeitsbereich (also die Funktion Init()) zu Ende ist. Das sind sehr kurzlebige Objekte. Wir brauchen aber langlebige Objekte. Die Spielwelt und die Objekte darin müssen ja viele Frames hintereinander aktiv sein und gezeichnet werden. Also legen wir sie per new an:
|
Quellcode
|
1
2
3
4
5
|
void BevoelkereDieWelt( Spielwelt& welt )
{
welt.AddObjekt( new SpielerObjekt( position, ...));
welt.AddObjekt( new UgaaObjekt( position, typ, ...));
}
|
Das ist ein Beispiel, also bitte nicht über-interpretieren. In jeder Zeile wird ein neues Objekt des benannten Typs erzeugt, es wird mit den gegebenen Parametern konstruiert (also der Konstruktor aufgerufen) und dann wird der Zeiger des Objekts an die Welt übergeben. An dieser Stelle geht das Objekt in den Besitz der Spielwelt über. Das steht dort nirgends, das kann C++ nicht leisten. Es ist eine Vereinbarung, die Du mit Dir selbst triffst: sobald ich ein Objekt an Spielwelt::AddObjekt() übergeben habe, gehört es nicht mehr mir, sondern der Spielwelt. Und die kümmert sich am Ende auch um die saubere Aufräumung.
Diese Lösung hat übrigens auch den Vorteil, dass der Spieler in seiner Arbeiten()-Funktion die Welt fragen kann, ob denn da ein Hindernis in seiner Wunsch-Bewegung ist. Jedes einzelne UGAA kann nur von sich selbst prüfen, ob eine Kollision mit einem gebenenen Rechteck erfolgt. Nur die Spielwelt kennt alle Objekte und kann demzufolge allgemeine Aussagen darüber treffen, ob eine Bewegung möglich ist.
Bleib dran! Du wirst mit der Zeit - also im Laufe der nächsten Jahre, ein paar Wochen reichen da nicht - ein Gefühl dafür bekommen, wie man die jeweilige Aufgabe in Klassen, Funktionen, Strukturen usw. aufteilt. Man neigt am Anfang eh dazu, nur wenige Klassen zu basteln und die dafür mit Funktionalität vollzustopfen. Ich kenne aber keine Methode, Dich davon abzuhalten... ich glaube, Du wirst es einfach ausprobieren müssen und nach und nach lernen, welche Strategien besser funktionieren als andere.