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

Daedra22

Treue Seele

  • »Daedra22« ist der Autor dieses Themas

Beiträge: 115

Wohnort: Osten

Beruf: Student Informationstechnik

  • Private Nachricht senden

1

18.06.2013, 10:14

C++ Quelltext mit Klassen, hpp- und cpp-Dateien strukturieren

Hallo liebe Spieleprogrammierer-Freunde,

Ich habe in letzter Zeit versucht den Quellcode von meinem Spiel viel besser zu strukturieren. (Inhalt ist nicht weiter von belang)

Nun habe ich die Einteilung der verschiedenen "Teile" des Games in Framework, Sound, Spieler, Ball, usw. eingeteilt, in Klassen zusammengefasst und in hpp- und cpp- Dateien eingespeichert, was bisher ganz gut von dannen ging.

Das Problem welches ich habe, ist nun, dass ich nicht ganz weiterkomme, da ich nicht verstehe ob ich jede Klasse einzeln mit einer Instanz erzeugen soll, sprich in meiner Main Funktion erst einmal immer schreiben soll.

(CGame Game, CSpieler Spieler, CBall Ball usw.)

In dem Buch von Heiko Kalista ist es etwas verwirrend, da er noch den Pfeilopferator (->) verwendet und noch mit Vererbung und Singleton und und und ... arbeitet und es für mich etwas zu verwirrend ist.
Außerdem hat er massenhaft Klassen erstellt aber nur eine einzige Instanz von CGame.
Ich habe nun versucht, dies auch alles auf eine Klasse eintreffen zu lassen, sprich ich habe die Funktionen aus Ball und Spieler in CGame hineingeschrieben:




--------------------------

HLSL-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
class CBall
{
public:
void Ballfunktion();
};

void CBall::Ballfunktion();

class CSpieler
{
public:
void Spielerfunktion();
};
void CSpieler::Spielerfunktion();

class CGame
{
public:
void SpielerBallFunktion();
};

void CGame::SpielerBallFunktion()
{ 
CSpieler::Spielerfunktion();
 CGame::Ballfunktion();
}


//main Funktion
CGame Game; // nur eine Instanz 
// Die finale Funktion
Game.SpielerBallFunktion(); 


----------------------------------------------

Dies ist der Quelltext mal extrem vereinfacht, über den Sinn streiten wir uns mal nicht ^^
Diese Variante wäre für mich die sinnvollste, da ich nur eine Instanz erzeugen muss und alles schön strukturiert habe, leider funktioniert es nicht.
Wie geht es nun richtig, wahrscheinlich ist die Frage zu trivial :D
Heiko hat es bei sich etwas, wie gesagt, zu komplex gemacht, mit Singleton und vererbten Klassen und dem Pfeilopferator, dass ich den Wald vor lauter Bäumen nicht sehen kann.

Danke für eure Zeit und eure Mühen, ich weiß die Hilfe im voraus zu schätzen :)


PS: Kann mir bitte jemand erklären, warum der PC immer versucht den Code wenn ich ihn in cpp Form- in diesem Forum. reinstelle total sinnlos zusammenzufassen und meine Leerzeichen und Zeilensprünge ignoriert, damit ich es für die Zukunft selbstständig formatieren kann, habs mal mit hsl gemacht, dies geht anscheinend
Who are you? - I am a game designer.
No you are not! - I am a game designer.
What kind of a designer ? - I am a game designer.
You mean you play games ? - I am a game designer.

Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von »Daedra22« (18.06.2013, 10:21)


Schorsch

Supermoderator

Beiträge: 5 145

Wohnort: Wickede

Beruf: Softwareentwickler

  • Private Nachricht senden

2

18.06.2013, 10:54

Soweit ich das richtig im Kopf habe ist das wenn du Code aus Visual Studio hier einfügst, dass die Formatierung verloren geht.
Was dein eigentliches Problem angeht. Klassen brauchst du eben so viele wie du eben brauchst. Das werden mit der Zeit vermutlich eher mehr als weniger. Der Pfeiloperator ist für Zeiger gedacht. Mal ein Beispiel:

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
class TestKlasse
{
public:
    void MachWas() {} // Etwas sinnlos da hier ja nichts passiert aber das ist unwichtig
};

int Main()
{
    TestKlasse normaleInstanz; // Eine "normale" Instanz
    TestKlasse *dynamischeInstanz; // Ein Zeiger auf eine Instanz

    // Zugriff auf Member der normalen Instanz
    normaleInstanz.MachWas();


    // Zugriff auf die Instanz auf die der Zeiger zeigt durch den * Operator. Danach kann normal über den . Operator auf die Member zugegriffen werden
    (*dynamischeInstanz).MachWas();

    // Eine vereinfachte Schreibweise ist der -> Operator.
    dynamischeInstanz->MachWas();

    return 0;
}


Wo du nun deine Instanzen erzeugst musst du schon selbst wissen. Heiko erzeugt die Instanzen in CGame, da er diese Klasse vermutlich als Hauptklasse für sein Spiel benutzt. Das heißt alles was im Spiel passiert läuft in dieser Klasse ab. Ein Beispiel. Ein Spiel hat einen Spieler und eine Map. Also würde deine Spielklasse eine Instanz vom Typ Spieler und eine Instanz vom Typ Map brauchen. Ein Spieler hat eine Position, also würde der Spieler eine Variable für die Position benötigen. Guck dir das Kapitel zu OO vielleicht noch mal an. Deine Klasse CGame sollte also weniger Funktionen wie Ballfunktion etc haben, da du sonst von außen aus der Mainfunktion wieder darauf zugreifen musst. Normal hast du irgendwie eine Hauptschleife und Updatecode bzw Rendercode der aufgerufen wird. In Update wird dein Code für die Logik des Spiels ausgeführt und in Render wird der Code für die Anzeige ausgeführt. Du kannst deiner CGame Klasse also eine Update Methode verpassen und diese beinhaltet dann den Updatecode deines Spiels. Dort werden wiederum Funktionen deines Spielers, der Map etc aufgerufen. Du verteilst im Prinzip den Code auf mehrere Stellen.
„Es ist doch so. Zwei und zwei macht irgendwas, und vier und vier macht irgendwas. Leider nicht dasselbe, dann wär's leicht.
Das ist aber auch schon höhere Mathematik.“

Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von »Schorsch« (18.06.2013, 14:18) aus folgendem Grund: Fehler im Code korrigiert. Danke Evrey.


Daedra22

Treue Seele

  • »Daedra22« ist der Autor dieses Themas

Beiträge: 115

Wohnort: Osten

Beruf: Student Informationstechnik

  • Private Nachricht senden

3

18.06.2013, 11:21

danke für die schnelle antwort, ich les mir erstmal nochmal das Kapitel über die Objektorientierte Programmierung durch und schau mir noch paar tutorials an, anscheinend ist es doch etwas komplexer ^^
Who are you? - I am a game designer.
No you are not! - I am a game designer.
What kind of a designer ? - I am a game designer.
You mean you play games ? - I am a game designer.

Evrey

Treue Seele

Beiträge: 245

Beruf: Weltherrscher

  • Private Nachricht senden

4

18.06.2013, 11:42

C-/C++-Quelltext

1
TestKlasse dynamischeInstanz; // Ein Zeiger auf eine Instanz
=>

C-/C++-Quelltext

1
TestKlasse* dynamischeInstanz; // Ein Zeiger auf eine Instanz
Böser Flüchtigkeitsfehler! D:

C-/C++-Quelltext

1
2
3
4
int main(int _argc, char** _argv) noexcept {
  asm volatile("lock cmpxchg8b %eax");
  return 0;
} // ::main
(Dieses kleine Biest vermochte einst x86-Prozessoren lahm zu legen.)

=> Und er blogt unter Hackish.Codes D:

Schorsch

Supermoderator

Beiträge: 5 145

Wohnort: Wickede

Beruf: Softwareentwickler

  • Private Nachricht senden

5

18.06.2013, 14:35

OOP ist eigentlich gar nicht so schwer. Ich glaube das Problem ist dass viele Bücher erst auf imperative Programmierung und später dann auf OOP eingehen. Dadurch gewöhnst du dir erst die eine Denkensweise an und musst diese dann unterdrücken und dir gleichzeitig eine neue Denkensweise aneignen. Dabei ist OOP für den Anfang eigentlich recht logisch, da man im Alltag vergleichbar Kategorisiert. Stell dir vor du hast die Klassen Hund und Katze. Beides sind Tiere, also erstellst du eine Klasse Tier und leitest davon ab. Jetzt könnte man weiter gehen. Du möchtest jetzt vielleicht ein weiteres Tier hinzufügen. Möglicherweise einen Löwen. Diesen leitest du auch von Tier ab. Die Katze und der Hund haben aber möglicherweise beide Eigenschaften die nur Haustiere haben, also könntest du eine Klasse Haustier machen, welche von Tier ableitet. Hund und Katze lässt du nun von Haustier ableiten. Löwe leitet sich weiterhin von Tier ab. Wichtig ist, die Klasse Tier definiert nur Eigenschaften die alle Tiere besitzen (wenigstens die Tiere die für dein Programm wichtig sind). Haustier erweitert diese Eigenschaften um die Eigenschaften die Haustiere haben. Ein Tier könnte also die Eigenschaft "Beine" haben, welche ein Array vom Typ Bein darstellen könnte. Ein Haustier könnte die Funktion "Streicheln" bekommen. So wäre klar, dass jedes Tier Beine hat, jedes Haustier kann gestreichelt werden, also haben Löwe, Hund und Katze alle Beine, einen Hund und eine Katze kann man jedoch streicheln und einen Löwen nicht. Der Hund bekommt dann vielleicht eine Funktion "Bellen", die Katze "Schnurren" und der Löwe "Brüllen". Das könnte man wieder zusammenfassen, indem man dem Tier eine Funktion "MachLaut" gibt. Diese Funktion macht man virtuell. Der Hund könnte in dieser Funktion dann den Code zum Bellen haben, die Katze den Code zum Schnurren und der Löwe den Code zum Brüllen. Jetzt könnte man im Prinzip alle Tiere gleich behandeln und einen Laut machen lassen, wobei die verschiedenen Tiere verschiedene Laute haben.
Das ist natürlich jetzt alles sehr sehr Bildlich und einfach und wenn du dann wieder vor deinem Spiel sitzt und es um CGame, CPlayer, CMap und was weiß ich nicht geht sieht das ganze wieder anders aus, aber die Denkweise bleibt die gleiche. Du musst einfach lernen dass du das obere auf das untere überträgst. Irgendwann kannst du diese Klassen im Prinzip auch so bildlich sehen. Zum Beispiel überlegst du was ein Spiel so alles haben muss. In diesem Fall wohl einen Spieler und eine Map. Vielleicht noch Gegner und Projektile/Geschosse. Spieler, Projektile und Gegner haben alle vermutlich eine Position, also kannst du hier theoretisch schon wieder zusammen fassen und eine Oberklasse bilden. So ist es grundlegend. Wenn du dich damit schwer tust, dann schreib doch einfach mal ein Konsolenprogramm. Zum Beispiel eins mit Tieren. Versuche mal Klassen für verschiedene Tiere zu machen und denen Eigenschaften und Funktionen zuzuweisen. Überleg dir wie du es sinnvoll strukturieren möchtest. Weiterhin ist es auch ein schönes Beispiel wenn du Klassen für Fahrzeuge schreibst. Da gibt es genug von. Du kannst Autos, Fahrräder, Boote, Flugzeuge, Skateboards und so weiter erstellen. Gucken wie du die Klug zusammenfasst. Wirklich Fahrzeuge sind das ja nicht alles. Dann kannst du irgendwann bestimmte Versionen davon erstellen. Zum Beispiel machst du einen Audi R8, einen BMW Z3, einen Fiat500 eine Boing757 und so weiter. Dort kannst du dich auch schön Bildlich durch die Klassenstruktur hangeln. Dann gibt es vielleicht Funktionen wie StarteMotor oder FahrVorwärts und so weiter. Bei so Beispielen reicht es wenn die Funktionen Textausgaben erzeugen. Zum Beispiel StarteMotor könnte "Motor wurde gestartet" ausgeben. Es geht ja eher um die Strukturierung. Das hilft dir vielleicht ein wenig weiter. Und bei konkreten Fragen zur Strukturierung steht dir der Forenchat oder das Forum selbst zur Verfügung.
„Es ist doch so. Zwei und zwei macht irgendwas, und vier und vier macht irgendwas. Leider nicht dasselbe, dann wär's leicht.
Das ist aber auch schon höhere Mathematik.“

Daedra22

Treue Seele

  • »Daedra22« ist der Autor dieses Themas

Beiträge: 115

Wohnort: Osten

Beruf: Student Informationstechnik

  • Private Nachricht senden

6

19.06.2013, 13:06

Habe nach langem Studieren des Heiko Quellcodes und ein paar Tutorials langsam den Dreh raus, ich üb ein bisschen mit den Beispielen die Schorschi mir vorgeschlagen hat, danke erst einmal dafür ^^
Die Atmosphäre im Forum ist sehr angenehm und auch die Hilfsbereitschaft ist beeindruckend, großes Lob an die Community und besonders an Schoschi, blue cobold, und Co

Schorschi ich hab mal deine Idee realisiert.

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
55
56
57
58
59
60
//Oberklasse Tier
class CTier
{
public:
void Leben();
};

void CTier::Leben()
{
cout<<"ich leben"<<endl;
}

//Unterklasse Haustier

class CHaustier
{
public:
void Streicheln();
void Können();
CTier *hTier;
};

void CHaustier::Streicheln()
{
cout<<"Mich kann man streicheln"<<endl;
}

void CHaustier::Können()
{
Streicheln();
hTier->Leben();
}

// untere Unterklasse Katze

class CKatze
{
public:
void Kuscheln();
void Können();CKatze();~CKatze();
CHaustier *kHaustier;
};
CKatze::CKatze(){ kHaustier= new CHaustier;}
CKatze::~CKatze(){delete kHaustier;}


void CKatze::Kuscheln()
{
cout<<"Ich kann kuscheln"<<endl;
}

void CKatze::Können()
{
Kuscheln();
kHaustier->Können();
}

//main Funktion

Katze.Können();


Jetzt funzt alles, hab die Init- Funktion ergänzt.
Who are you? - I am a game designer.
No you are not! - I am a game designer.
What kind of a designer ? - I am a game designer.
You mean you play games ? - I am a game designer.

Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von »Daedra22« (19.06.2013, 23:56) aus folgendem Grund: Init durch Konstruktor und Destruktor verändert


7

19.06.2013, 15:59

Das ist alles ein wenig konfus, und irgendwie überhaupt nicht richtig.

Zuerst einmal, lass bitte das "C" vor den Klassennamen weg. Das macht keiner mehr ;)

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
55
56
57
58
59
//Oberklasse Tier
class CTier
{
public:
void Leben();
};

void CTier::Leben()
{
cout<<"ich leben"<<endl;
}

//Unterklasse Haustier

class CHaustier     // du musst hier erben class CHaustier : public CTier
{
public:
void Streicheln();
void Können();     // sollte virtual sein, da du sie in CKatze ja überschreiben willst
CTier *hTier;       // unnötig!
};

void CHaustier::Streicheln()
{
cout<<"Mich kann man streicheln"<<endl;
}

void CHaustier::Können()
{
Streicheln();
hTier->Leben();      // du kannst hier doch auch einfach Leben() schreiben.
}

// untere Unterklasse Katze

class CKatze   // ebenfalls erben class CKatze : public CHaustier
{
public:
void Kuscheln();
void Können();void Init();
CHaustier *kHaustier;   // wofür brauchst du hier einen pointer?
};
void CKatze::Init(){new kHaustier;}    // unnötig! und sowas kompilert? o.O macht für mich keinen Sinn :P

void CKatze::Kuscheln()
{
cout<<"Ich kann kuscheln"<<endl;
}

void CKatze::Können()
{
Kuscheln();
kHaustier->Können();       // willst du "Können" der Basisklasse aufrufen, schreibst du CHaustier::Können(); Hier wird kein Pointer gebraucht!
}

//main Funktion

CKatze Katze;Katze.Init();  // warum Katze.Init() ? wenn du bei der Erstellung einer Instanz eine Funktion aufrufen möchtest, schreib einen Konstruktor (CKatze::CKatze())
Katze.Können();


Hoffe ich hab hier nichts vergessen, du solltest dir klassen allerdings nochmal genauestens anschauen.

8

19.06.2013, 16:48

Schorsch hat oben Beispiele mit Vererbung gemacht, und das wollte er umsetzen. Aus diesem Grund ist es falsch (zumal auch seine Zeiger keinen Sinn machen, wenn er sie nicht zuweist).

Schorsch

Supermoderator

Beiträge: 5 145

Wohnort: Wickede

Beruf: Softwareentwickler

  • Private Nachricht senden

9

19.06.2013, 18:05

Ich habe Beispiele mit Vererbung gemacht das ist richtig. Aber im Prinzip sollte das erst mal als abstrakte Idee gelten die man sich selbst ausarbeiten kann. Ich habe einige Dinge dazu schon beschrieben um Anhaltspunkte zu geben. So wäre es im Prinzip aber auch ok. Da vertritt auch jeder eine andere Meinung zu. Ein Problem ist allerdings, dass für die Pointer in Init() zwar Speicher geholt wird, der aber nirgendwo wieder freigegeben wird. Das könntest du zum Beispiel im Destruktor machen. Weiterhin versucht man Methoden wie Init() eigentlich zu umgehen. Die Sache ist, dass man beim benutzen die Init() Methode vergessen kann, also ist es einfacher den Konstruktor zu benutzen, da der eh aufgerufen wird. Ich verweise mal einfach auf das Buch "effective C++", da wird das und viele andere Dinge besprochen die den Code ungemein verbessern können. Ich bin der Auffassung, dass man die Beziehungen "hat" und "ist" so verwenden sollte wie gedacht. Also in deinem Fall "ist" eine Katze ein Haustier, hier würde ich also vererben, aber eine Katze "hat" Beine.

C-/C++-Quelltext

1
2
3
4
5
6
7
8
9
10
11
12
13
14
class Tier
{
    // ...
};

class Haustier:Tier // Haustier erbt von Tier
{
    // ...
};

class Katze:Haustier // Katze erbt von Haustier, also ist Katze Haustier und Tier
{
    // ...
};


Das schöne ist, man könnte folgendes machen:

C-/C++-Quelltext

1
2
3
4
5
6
std::list<Tier*> tiere;
tiere.pushback(new Tier());
tiere.pushback(new Haustier());
tiere.pushback(new Katze());

// ...


Natürlich sollte man hinterher dafür sorgen dass die Instanzen wieder mit delete gelöscht werden oder mit Smartpointern arbeiten. Das ganze nennt sich Polymorphie. Das ist eine ziemlich wichtige Sache bei OOP und sollte unbedingt verstanden werden. Wenn dir das alles klar ist ist es gut, ansonsten guck dir vielleicht noch mal das Kapitel zu Vererbung und Polymorphie an. Auch die schon angesprochenen virtuellen Funktionen spielen da eine wichtige Rolle. Spätestens wenn du in deinem Spiel mehrere Gegner hast, welche verschiedenes Verhalten haben sollen wirst du mit sowas in Kontakt kommen. Egal ob über virtuelle Funktionen oder Strategy Pattern.
„Es ist doch so. Zwei und zwei macht irgendwas, und vier und vier macht irgendwas. Leider nicht dasselbe, dann wär's leicht.
Das ist aber auch schon höhere Mathematik.“

Fawkes

Frischling

Beiträge: 58

Beruf: Realschüler :)

  • Private Nachricht senden

10

19.06.2013, 20:36

C-/C++-Quelltext

1
2
3
4
5
6
//Oberklasse Tier
class CTier // <- Das C vor der Klasse ist nicht sonderlich nötig, mach es einfach so: Variablen grundsätzlich klein schreiben, Namen von Funktionen klein anfangen rest Groß: doThis und Klassennamen einfach normal: Tier
{
public:
void Leben(); // Bei kleinen Klassen bietet es sich an die Funktionen direkt in der Klasse zu definieren
};

Nur so als kleine Stielhilfe. :) Kann aber jeder machen wie er will. Ausserdem, Wenn du eine große Klasse haben willst empfehle ich dir dafür auch eine neue .hpp Datei anzulegen. Ist später übersichtlicher.

Werbeanzeige