Kompilierzeitprobleme mit modularer Programmierung
-
drakon schrieb:
Ja. Das stimmt. Der Compiler kann sich das merken und muss die Datei dann nicht mehr Öffnen, wenn das pragma vorhanden ist. Und afaik unterstützen pragma auch noch andere neuere Compiler, abgesehen von Windows.
Vermutlich kann man einem heutigen Präprozessor getrost zutrauen, daß er in der Lage ist, einen gewöhnlichen Header-Guard als solchen zu erkennen und die Datei infolgedessen nicht zweimal zu laden. #pragma once ist lediglich ein Auswuchs von Microsofts historischer Tendenz, lieber proprietäre Compilererweiterungen zu erfinden, anstatt mit geringfügig mehr Aufwand eine standardkonforme Lösung zu entwickeln.
Und Windows ist übrigens kein Compiler. SCNR.
-
Pragmatiker schrieb:
Schreib mal in jeden deiner Header [...] das sollte die Compile-Time bei Änderungen an einem Header enorm reduzieren, wenn du keine vorkompilierten Header benutzt sogar ständig.
Das wird kaum etwas bringen, da die Include-Guards nur mehrfache Einbindung verhindern (was so oder so zu Fehlern beim Kompilieren führt).
Dravere schrieb:
Nein, es ist absolut richtig und wird auch voll durchgezogen. Es ist doch nicht die Aufgabe des Spielers sich zu bewegen, sondern der Spieler muss bewegt werden. Der Spieler ist ein Zustand, die Regeln verändern diesen Zustand.
Das erfordert teilweise ein wenig ein Knick im Denken, aber es ist eben schon korrekt.
Ok, dann werd ich mal versuchen, mein Denken zu knicken :p
Aber ich kann nicht ganz nachvollziehen, dass gerade der Spieler nur ein Zustand ist... Ist das generell bei Eigenschaften so? Und hat das den Vorteil, dass Änderungen nur an der zentralen Stelle (Game) geschehen?Dravere schrieb:
Theoretisch könnten die sehr gut unabhängig erstellt werden. Es kommt da allerdings auch ein wenig drauf an wie man das Strukturiert. Grundsätzlich würde ich persönlich schon auch eher etwas wie eine Map einführen. Die Sache also aus dem Game herauskapseln. So kann man die Maps auch austauschen

Und dann den Spieler von der Map abhängig machen? Oder noch mehr Unabhängigkeiten erstellen?
Dravere schrieb:
Und man könnte natürlich auch Static-Tiles und Dynamic-Tiles einführen. Zumindest würde ich das nun so nennen. Die Tiles werden ja oft gleich aussehen und womöglich gleiche Eigenschaften aufweisen. So ein Tile, wird nur ein einziges mal erstellt, quasi das Static-Tile. Während die tatsächliche Map aus Dynamic-Tiles besteht. Diese verweisen auf ein Static-Tile. Also ein Dynamic-Tile vereint die Koordinaten mit dem Static-Tile. So spart man auch Speicher!
Grundsätzlich ist das eine gute Idee, aber ich weiss nicht, ob es sich wegen des Speicherplatzes lohnt. Ich habe bei meiner Grafikbibliothek (SFML) eine Sprite-Klasse, die ich auch für Tiles verwende. Wenn ich das Sprite nun in die Static-Tiles auslagern würde, müsste ich ständig die Position ändern und neu zeichnen. Zudem haben viele Tiles noch spezifische Eigenschaften, manche sind auch animiert etc.
Dravere schrieb:
Ja, ein Spieler könnte auch mit dem Spiel erstellt werden, sollte aber nicht vom Spiel abhängig werden

Das musst du jetzt endlich mal erklären. Der Spieler (nur als Spielfigur, mal ohne Profile etc.) hat meiner Ansicht nach nichts ausserhalb von
Gamezu suchen, ist also schon abhängig...Dravere schrieb:
So verstehst du das. Ich würde mit dem Begriff etwas vorsichtiger umgehen. Stackvariablen befinden sich normalerweise auf dem Stack und nicht auf dem Heap.
Ja, das wollte ich zuerst auch schreiben, aber ich habs nun so formuliert, wie ichs bis jetzt verwendet habe, damit es nicht zu Missverständnissen kommt. Gibt es überhaupt ein Wort für Variablen, deren Speicherbereich statisch angefordert wurde (unabhängig ob im Heap oder Stack)?
Dravere schrieb:
Für Polymorphie braucht es übrigens kein Heap, das kann man auch mit Variablen auf dem Stack machen.
Wie denn? Ein Zeiger
Base*kann ja aufDerivedzeigen, aber ein ObjektBase? Oder meinst du einfach, statt dynamischer Speicheranforderung irgendwo statisch anfordern und dann die Adresse übergeben?Dravere schrieb:
Bei solchen Variablen muss man aber nicht vergessen, dass man sie extrem bindet. Man kann sie nicht mehr einfach austauschen, was oft ein Nachteil sein kann.
Sorry, jetzt verstehe ich nicht gerade, was du meinst...
Dravere schrieb:
Eine Klasse kann auch für mehrere andere Klassen zuständig sein. Und ja, es ist dann eine Art von Baum, der sich selber aufbaut. Er wuchert dann von der Wurzel aus nach oben oder unten. Die Aktionen finden dann aber meistens an den äussersten Spitzen statt.
Okay, erscheint irgendwie logisch. Und für das ständige Erhalten des Programms bleiben die Aktionen auch fast ganz aussen, die Wurzel/das "eher Innere" wird nur am Anfang benötigt?
Dravere schrieb:
Um deine C++ Kentnisse weiter aufzufrischen, empfehle ich ein paar mehr Bücher zu lesen.
Vielen für die Bücherliste, ich werde mich mal erkundigen. Danke auch dir, drakon. Ich weiss nur nicht, ob ich diesen Sommer noch dazu komme, viel Neues in C++ zu lernen, zumal ich auch noch ein Spiel programmieren möchte

drakon schrieb:
Dravere schrieb:
Naja, ich vergleiches vielleicht mit meinem "Hobbyprogrammieren". Aber ich bin ja so extrem, dass ich kaum mehr nach Aussen gehe und nur noch vor dem Computer sitze. Ich bin ein richtiges Klischee
Och. So geht mir das eigl. auch. - Ich freue mich schon, wenn die Prüfungen vorbei sind und ich micht voll und ganz den Computer widmen kann. - Wen interessiert schon, was das für Wolken über meinem Kopf sind?! - Im Keller regnet es ja nicht.
Was seid ihr doch für Freaks

Dravere schrieb:
drakon schrieb:
Nö. Aber wir haben uns auch schon in einem anderen Forum getroffen.

Dann kennt ihr euch aus dem VL.
Lag ich doch nicht all zu weit daneben.
Ihr wohnt doch beide auch in der Schweiz, oder? Vielleicht haben wir uns schon längst im RL gesehen, wissen es nur nicht

-
Und Windows ist übrigens kein Compiler. SCNR.
Sorry, war in Eile, hast natürlich Recht.

Ihr wohnt doch beide auch in der Schweiz, oder? Vielleicht haben wir uns schon längst im RL gesehen, wissen es nur nicht
Ach.. Ich wusste gar nicht, dass ihr beiden auch Schweizer seid.

Jetzt ist auch klar, woher die Symphatie kommt.

Naja. Kantone? Ich bin TG'ler.Sorry für OT.

-
Nexus schrieb:
Ok, dann werd ich mal versuchen, mein Denken zu knicken :p
*sich jetzt gerade an den Löffel in Matrix erinnert*

Nexus schrieb:
Aber ich kann nicht ganz nachvollziehen, dass gerade der Spieler nur ein Zustand ist... Ist das generell bei Eigenschaften so? Und hat das den Vorteil, dass Änderungen nur an der zentralen Stelle (
Game) geschehen?Nein, die Änderungen sollten zum Beispiel von den Regeln, welche auch Objekte sein können, durchgeführt werden. Wenn die Regeln Objekte sind, hat das übrigens durchaus auch Vorteile, da man sie dann als XML auslagern könnte und somit parsen. Damit lassen sich dann auch schnell Änderungen in den Regeln durchführen, ohne das Programm neu kompilieren zu müssen

Ehm, aber zurück zum Spieler. Ich habe da ein gutes Beispiel gefunden, denke ich zumindest. Stell dir ein normales Brettspiel vor. Die Spielfigur, welche da drauf steht, ist nur ein Zustand. Die Spielfigur wird sich NIE von selbst bewegen. Egal ob du Magie oder sonst was anwendest, diese Figur wird immer dort stehen bleiben, ohne Einfluss von Aussen. Nur jemand oder etwas von Aussen, kann den Zustand der Spielfigur verändern.
Es ist also nicht die Aufgabe der Spielfigur, ihren Zustand zu ändern.Nexus schrieb:
Und dann den Spieler von der Map abhängig machen? Oder noch mehr Unabhängigkeiten erstellen?
Wieso von der Map abhängig? Der Spieler speichert nur den Zeiger, wo er gerade steht. Er macht nicht mal etwas mit diesem Zeiger und dieser Zeiger verweist nur auf ein einziges Tile. Der Spieler muss überhaupt nichts über die Map wissen.
Nexus schrieb:
Grundsätzlich ist das eine gute Idee, aber ich weiss nicht, ob es sich wegen des Speicherplatzes lohnt. Ich habe bei meiner Grafikbibliothek (SFML) eine Sprite-Klasse, die ich auch für Tiles verwende. Wenn ich das Sprite nun in die Static-Tiles auslagern würde, müsste ich ständig die Position ändern und neu zeichnen. Zudem haben viele Tiles noch spezifische Eigenschaften, manche sind auch animiert etc.
Naja, es kommt manchmal etwas drauf an, wie die anderen Bibliotheken, welche man verwendet, aufgebaut sind. Wenn deine Sprites eine feste Position haben und es aufwendig ist, denen eine neue Position zu geben, dann ist das vielleicht nicht so geschickt. Ich kenne mich jetzt nicht mit SFML aus, aber meistens ist die Position eines Sprite nur als Zahlen gespeichert. Diese überschreibt man kurzerhand mit der neuen Position. Bei Animationen eines Tiles wird das aber teilweise schon etwas schwieriger. Bzw. man bekommt das Problem, das alle Animationen so verdammt synchron laufen

Zeichnen musst du die Dinger sowieso und ob du nun vorher noch 3 Zahlen überschreibst oder nicht, fällt meistens nicht so ins Gewicht.Nexus schrieb:
Das musst du jetzt endlich mal erklären. Der Spieler (nur als Spielfigur, mal ohne Profile etc.) hat meiner Ansicht nach nichts ausserhalb von
Gamezu suchen, ist also schon abhängig...Nehmen wir am besten wieder das Spielbrett. Du kannst die Spielfigur nehmen und neben das Brett legen. Bzw. am Anfang nimmst du sie aus der Schachtel und setzt sie auf das Brett drauf. Es gibt auch die Möglichkeit, dass man eine Spielfigur überhaupt nicht nimmt.
Die Sache ist halt, wenn du die Spielfigur fest ins Spiel einbindest, dann verbarrikadierst du dir viele Möglichkeiten.
Nexus schrieb:
Gibt es überhaupt ein Wort für Variablen, deren Speicherbereich statisch angefordert wurde (unabhängig ob im Heap oder Stack)?
Ehm ... Membervariable?
Nexus schrieb:
Wie denn? Ein Zeiger
Base*kann ja aufDerivedzeigen, aber ein ObjektBase? Oder meinst du einfach, statt dynamischer Speicheranforderung irgendwo statisch anfordern und dann die Adresse übergeben?Hmmm, ich weiss jetzt nicht, ob wir uns aneinander vorbeireden, aber um es mal zu verdeutlichen, was ich meinte:
class Base { public: virtual void output() const { std::cout << "Base!" << std::endl; }; }; class Left : public Base { public: virtual void output() const { std::cout << "Left!" << std::endl; }; }; class Right : public Base { public: virtual void output() const { std::cout << "Right!" << std::endl; }; }; void foo(Base* base) { if(base) { base->output(); } } int main() { // left und right liegen beide auf dem Stack. Left left; Right right; // Polymorphie ist trotzdem möglich. foo(&left); foo(&right); // Sie müssen halt einfach auch gültig bleiben ;) return 0; }Nexus schrieb:
Sorry, jetzt verstehe ich nicht gerade, was du meinst...
Ehm, dazu vielleicht auch etwas Code, welcher mehr sagt, als tausend Wörter.
class Inner { }; class HeavyBind { private: Inner m_inner; public: Inner& get_inner() { return m_inner; }; Inner const& get_inner() const { return m_inner; }; // Oops, für das set, brauchen wir bereits einen op= void set_inner(Inner const& inner) { m_inner = inner; }; }; class LightBind { private: Inner* m_inner; public: Inner* get_inner() { return m_inner; }; void set_inner(Inner* inner) { m_inner = inner; }; }; int main() { // Mal zwei Inner Variablen. Inner firstInner; Inner secondInner; // Schauen wir uns den HeavyBind an. HeavyBind heavyBind; // Wir können heavyBind kein Objekt von aussen geben. Es wird immer eine Kopie vorgenommen! heavyBind.set_inner(fisrtInner); // get_inner() wird immer das gleiche Objekt liefern! heavyBind.get_inner(); // Schauen wir uns LightBind an. LightBind lightBind; // Übergeben wir nun firstInner. lightBind wird absofort mit firstInner arbeiten. lightBind.set_inner(&firstInner); // Wir können hier auch ausserhalb noch Änderungen an firstInner durchführen, // wodurch sich das Verhalten von lightBind verändert! // Wir bekommen hier die Adresse auf firstInner! lightBind.get_inner(); // Und wir können das Objekt auch einfach ändern. lightBind.set_inner(&secondInner); // Und wir bekommen ein anderes Objekt zurück. lightBind.get_inner(); // Auch zu beachten ist, dass überhaupt nie eine Kopie nötig war! return 0; }Nexus schrieb:
Okay, erscheint irgendwie logisch. Und für das ständige Erhalten des Programms bleiben die Aktionen auch fast ganz aussen, die Wurzel/das "eher Innere" wird nur am Anfang benötigt?
Und am Ende zum aufräumen. Und dazwischen um das Konstrukt aufrecht zu erhalten, allerdings ist das meistens den Speicher halten und warten bis er freigegeben werden darf.
Es kann natürlich auch möglich sein, dass man einmal Nachrichten nach unten schickt um ganz andere Stränge zu benachrichtigen. Im allgemeinen weiss der eine Strang aber nichts über den anderen und sendet nur eine Nachricht nach unten. Und die Klasse unten, bzw. das Objekt, weiss dann, was es damit auf sich hat und reagiert entsprechend.Nexus schrieb:
Was seid ihr doch für Freaks

Freak? Nerd? Geek? Oder sonst was? Vielleicht ... dafür herrsche ich über die WELT!!! MUAHAHAHAHAHA ... *auf den Bildschirm schaut*
segfault in function world_domination at location 0x0badc0de
Ehm ... soon
LoL, alles Schweizer hier? Ist ja mal wieder interessant. So Diskussionen entstehen nur mit Schweizern!

Ich wohne aktuell im Kanton BL. In meinem Pass steht als Bürgerort allerdings Rothenburg, LU. Und ich fühle mich auch eher dorthin gezogen, bzw. zurück ins Zentrum der Schweiz oder noch besser, einfach nur weg von Basel!Grüssli
-
Nur jemand oder etwas von Aussen, kann den Zustand der Spielfigur verändern.
Es ist also nicht die Aufgabe der Spielfigur, ihren Zustand zu ändern.Ja, aber die Figur weiss, wie man geht. Man muss der Figur also nur noch sagen: "geh rauf!". Und dann erledigt die Figur den Rest, was hald dazugehört.
(Bin mir jetzt nicht ganz sicher, ob das in etwa die Frage war.. )Zeichnen musst du die Dinger sowieso und ob du nun vorher noch 3 Zahlen überschreibst oder nicht, fällt meistens nicht so ins Gewicht.
Genau. Also bei meinem Partikelsystem läuft das so, dass ich ein Sprite habe, was den Partikel simuliert und dann habe ich eine Liste von Partikeln, die dann Position, Rotation usw. beinhalten. Und dann wird das Sprite hald jedes mal verschoben und rotiert und gerendert. Das ist reine Berechnung und im Release Stadium sind auch 10000 Partikel nicht das Problem der verschiebung des Sprites, sondern eher das rendern selbst.
segfault in function world_domination at location 0x0badc0de
Hmm. Hatte ich auch. In diesem Falle liegt das nicht an uns. Da stimmt was mit dem Code der Realität nicht.

LoL, alles Schweizer hier? Ist ja mal wieder interessant. So Diskussionen entstehen nur mit Schweizern!
Hehe. Ich glaube wir sind alle aktiven Schweizer hier.
Ich schick euch beiden mal eine Runde msn-Adressen. Wir können ja mal wenig quatschen. 
-
drakon schrieb:
Ja, aber die Figur weiss, wie man geht. Man muss der Figur also nur noch sagen: "geh rauf!". Und dann erledigt die Figur den Rest, was hald dazugehört.
(Bin mir jetzt nicht ganz sicher, ob das in etwa die Frage war.. )Ich will mal sehen, wie du einer Figur auf dem Brett sagst, wohin sie gehen soll. Da kannst du lange "geh rauf!" sagen. Die Figur weisst nicht, wie sie sich bewegen soll. Die Regeln schreiben vor, wie sie sich zu bewegen hat. Jemand kennt die Regeln und wendet diese auf die Figur an.
Der Figur kann so auch scheiss egal sein, wie die Regeln aussehen. Man kann also Änderungen an den Regeln vornehmen, ohne etwas an der Figur zu ändern.Grüssli
-
Ja, das ist schon klar.
Aber wenn ich die Figur jetzt bewegen will, dann muss ich das Objekt ja verändern. Ob das jetzt mit einer Funktion "gehrauf" oder mit einem Vector gemacht wird, spielt ja keine Rolle, aber irgendwie musst du das dem Objekt ja mitteilen.
-
drakon schrieb:
Ja, das ist schon klar.
Aber wenn ich die Figur jetzt bewegen will, dann muss ich das Objekt ja verändern. Ob das jetzt mit einer Funktion "gehrauf" oder mit einem Vector gemacht wird, spielt ja keine Rolle, aber irgendwie musst du das dem Objekt ja mitteilen.Du veränderst das Objekt, in dem du dem Objekt am Ende der Bewegung mitteilst, wo es ist. Wie ich schon mal früher als Beispiel hingeschrieben habe, zum Beispiel über eine
set_locationFunktion. Es ist eigentlich wie mit dem Sprite, du veränderst nur die Koordinaten/das Feld, wo die Figur steht. Somit hat die Figur kein "geh irgendwohin", sondern eine art "setze neuer Standort". Und die Figur wird damit nichts anfangen, sondern diesen neuen Ort einfach nur intern abspeichern. Und wenn das nächste Mal eine Bewegung erfolgt, weiss die Regel, durch abrufen der Funktionget_location, wo die Figur aktuell steht.Grüssli
-
Noch eine Sache wegen der Klassenaufteilung.
Ich weis nicht, ob das eventuell schon einmal so deutlich gesagt wurde, zwischenzeitlich wurde es durch die Menge an quotes extrem unübersichtlich.
Als Designziel einer Klasse sollte immer gelten: Eine Klasse, eine Aufgabe.
Keine großen, monolithischen Gebilde. Einfach kleine aber feine Klassen mit einem einfach zu definierendem Interface. Und so funktionierts: du schaust dir deine Klasse an, und versuchst ihre Aufgabe zu beschreiben. Sobald du in der Beschreibung ein "und" benutzt, tut die Klasse zuviel. so einfach ist das.Beispiel Spielbrett:
gut:
Das Spielbrett verwaltet die Schachfiguren
schlecht:
Das Spielbrett verwaltet die Schachfiguren und erstellt den SpielerDiese Art der Programmierung senkt automatisch die Compilezeit, da die Abhängigkeiten drastisch reduziert werden. Die gute Klasse in unserem Beispiel hat nur die Abhängigkeit zu den Schachfiguren, die schlechte Klasse zudem noch die Abhängigkeit zum Spieler. Ein weiterer Positiver Nebeneffekt ist, dass man nur sehr wenig an den einzelnen Interfaces rumbasteln muss. Erweiterung ist bei dem Prinzip das Mittel der Wahl.
Das zweite Designziel sollte sein(das ist aber die hohe Kunst), dass man als Methode einer Klasse nur das definiert, was wirklich eine Methode sein muss.
Beispiel Kollisionsfunktion:Wenn du 2 Objekte A und B mit der Methode testKollision hast, dann kannst du schonmal folgendes feststellen:
A.testKollision(B); B.testKollision(A);irgendwie ist kein logischer Unterschied zwischen beiden Aufrufen, oder?
Dann gibt es sicherlich auch keinen logischen Unterschied dazu:testKollision(A,B);Und das ist ein sicheres Indiz dafür, dass testKollision keine Methode sein sollte.Das ist allerdings auch nicht das einzige Kriterium. Das wichtigere Kriterium ist: braucht die Methode nur private Daten des Objekts,die bereits nach außen zugänglich sind? Wenn ja, dann spricht wirklich nichts dagegen, dass die Methode zur Funktion wird. Wenn die Methode logisch als Funktion existieren kann, und dabei keine weiteren Daten der Klasse öffentlich gemacht werden müssen, dann sollte die Methode auch eine Funktion sein.
Was bringt das? Das Interface wird dadurch viel Stabiler. Man muss nur noch selten Änderungen an der Klasse vornehmen, meistens reicht es, wenn einfach eine weitere Funktion hinzugefügt wird. Das Interface der Klasse kann desweiteren generalisiert werden, sodass das Gesamtdesign stimmiger und einfacher zu überschauen ist.
-
Dravere schrieb:
*sich jetzt gerade an den Löffel in Matrix erinnert*

Nicht denken... Wissen. :p
Dravere schrieb:
[...] Es ist also nicht die Aufgabe der Spielfigur, ihren Zustand zu ändern.
Okay, das war wirklich ein gutes Beispiel. Jetzt wird es mir auch relativ klar.
Dravere schrieb:
Zeichnen musst du die Dinger sowieso und ob du nun vorher noch 3 Zahlen überschreibst oder nicht, fällt meistens nicht so ins Gewicht.
Okay. Animationen würde ich wahrscheinlich mit Frames lösen, d.h. wenn eine Animation ausgelöst wird, wird eine int-Variable auf 0 gesetzt. In jedem weiteren Frame wird sie inkrementiert und so wird je nach der vergangenen "Zeit" ein anderes Bild dargestellt. Da müsste man eventuell länger berechnen, wenn man keine synchronen Animationen will. Aber auch das (Division/Modulo) sollte kein Problem darstellen.
Dravere schrieb:
Nehmen wir am besten wieder das Spielbrett. Du kannst die Spielfigur nehmen und neben das Brett legen. Bzw. am Anfang nimmst du sie aus der Schachtel und setzt sie auf das Brett drauf. Es gibt auch die Möglichkeit, dass man eine Spielfigur überhaupt nicht nimmt.
Zu dem Schachspiel passt das Beispiel ja schön und gut, aber in meinem Spiel hat eine Spielfigur eigentlich nichts ausserhalb des Spiels verloren. Sorry, wenn ich so stur bin

Dravere schrieb:
Die Sache ist halt, wenn du die Spielfigur fest ins Spiel einbindest, dann verbarrikadierst du dir viele Möglichkeiten.
Das kann schon sein, aber momentan kann ich mir nicht vorstellen, welche Möglichkeiten? Naja, vielleicht ist hier auch wieder ein Zuerst-auf-die-Schnauze-Fallen-bevor-man-es-kapiert-Effekt angebracht

Dravere schrieb:
Nexus schrieb:
Gibt es überhaupt ein Wort für Variablen, deren Speicherbereich statisch angefordert wurde (unabhängig ob im Heap oder Stack)?
Ehm ... Membervariable?
Membervariablen würde ich eher die innerhalb einer Klasse deklarierten Variablen nennen...
Dravere schrieb:
Hmmm, ich weiss jetzt nicht, ob wir uns aneinander vorbeireden, aber um es mal zu verdeutlichen, was ich meinte: [...]
Danke für das Beispiel. Ja, in etwa so dachte ich es mir (natürlich nicht mit Light und Heavy Binding). Aber das Light Binding wird ja benötigt, wenn man einen ganzen Spieler an eine Klasse übergeben will (sodass die Klasse nun machen kann, was sie will). Daran hab ich gar nie gedacht, aber genau so wenig an Heavy Binding, da ich ja immer von festen Besitzen ausgegangen bin...
Dravere schrieb:
Freak? Nerd? Geek? Oder sonst was? Vielleicht ... dafür herrsche ich über die WELT!!! MUAHAHAHAHAHA ... *auf den Bildschirm schaut*
segfault in function world_domination at location 0x0badc0de
Ehm ... soon
Irgendwie haben wir ja alle etwas gemeinsam... Der Versuch einer Masterklasse ist schon das erste Anzeichen bei mir

BTW: Zentrum der Schweiz ist gut, ich wohne im Kanton Luzern (in Kriens)

Dravere schrieb:
Somit hat die Figur kein "geh irgendwohin", sondern eine art "setze neuer Standort". Und die Figur wird damit nichts anfangen, sondern diesen neuen Ort einfach nur intern abspeichern. Und wenn das nächste Mal eine Bewegung erfolgt, weiss die Regel, durch abrufen der Funktion
get_location, wo die Figur aktuell steht.Streng gesehen sind dann auch Methoden der Figur wie z.B.
Move()oder ähnliches zur Vereinfachung verboten? D.h. gerade mal Getter und Setter? Oder gleich ein Struct verwenden?@ otze: Okay, dann verwende ich bei dir keine Quotes :p
Hey, vielen Dank für die ausführliche Beschreibung, jetzt wird mir einiges viel klarer in der OOP! Ich hatte bisher immer nur so eine schwammige Vorstellung - vom Konzept "eine Klasse - eine Aufgabe" hab ich auch schon gehört, aber ich weite meine Aufgaben meistens zu stark aus...
-
...
ausserdem möchte ich noch eine buchempfehlung aussprechen.
vielleicht ist das buch ein wenig zu massiv, aber es befasst sich sich
grad mit der thematik des physikalischen designs (compiletime coupling, ...).large scale c++ design (john lakos) http://www.amazon.de/Large-Scale-Software-Addison-Wesley-Professional-Computing/dp/0201633620/ref=sr_1_1?ie=UTF8&s=books-intl-de&qid=1219008234&sr=8-1
ich lese es gerade (bin grad irgendwo in der mitte).
es ist in einigen wenigen sachen outdated (bzw nicht state of the art), aber es vermittelt irrsinnig viel knowhow
-
Nexus schrieb:
Zu dem Schachspiel passt das Beispiel ja schön und gut, aber in meinem Spiel hat eine Spielfigur eigentlich nichts ausserhalb des Spiels verloren. Sorry, wenn ich so stur bin

Nexus schrieb:
Das kann schon sein, aber momentan kann ich mir nicht vorstellen, welche Möglichkeiten?
Ich habe bisher immer sehr allgemein probiert zu reden. Da ich dein Spiel nicht kenne, kann ich dir auch nicht direkt irgendwelche Möglichkeiten für dein Spiel aufzählen. Du bleibst einfach viel flexibler.
Aber ich denke es ist, wie du selbst sagt:
Nexus schrieb:
Naja, vielleicht ist hier auch wieder ein Zuerst-auf-die-Schnauze-Fallen-bevor-man-es-kapiert-Effekt angebracht

Nexus schrieb:
Dravere schrieb:
Nexus schrieb:
Gibt es überhaupt ein Wort für Variablen, deren Speicherbereich statisch angefordert wurde (unabhängig ob im Heap oder Stack)?
Ehm ... Membervariable?
Membervariablen würde ich eher die innerhalb einer Klasse deklarierten Variablen nennen...
Und auf welche Variablen trifft deine Beschreibung sonst zu?
Static Variablen kommen schliesslich weder auf den Heap noch Stack, sondern in den Global-Speicher.Nexus schrieb:
Aber das Light Binding wird ja benötigt, wenn man einen ganzen Spieler an eine Klasse übergeben will (sodass die Klasse nun machen kann, was sie will).
Genau, und so sollte es meiner Meinung nach mit dem Spieler geschehen

Nexus schrieb:
BTW: Zentrum der Schweiz ist gut, ich wohne im Kanton Luzern (in Kriens)

Habt ihr zufällig eine UBS-Filiale dort? Dann wickeln nämlich meine Eltern einen Teil ihrer Finanzen dort ab. Mein Vater ist im Kanton LU eingebürgert

Nexus schrieb:
Streng gesehen sind dann auch Methoden der Figur wie z.B.
Move()oder ähnliches zur Vereinfachung verboten?Haargenau! Die Vereinfachung findet dadurch statt, dass du genügend Klassen baust, also deine Aufgaben aufteilst.
Nexus schrieb:
D.h. gerade mal Getter und Setter?
Am besten wäre es wohl.
Nexus schrieb:
Oder gleich ein Struct verwenden?
Von einem Struct empfehle ich ab. Ein Struct hat alle Membervariablen normalerweise als public. Mit den Getter/Setter hast du im ersten Moment zwar auch nichts anderes und der Compiler wird dir alles dermassen optimieren, dass es auch nichts anderes sein wird, aber die Getter und Setter haben einen riesigen Vorteil beim Debuggen!
Du kannst genau nachprüfen, WANN eine Variable abgerufen oder verändert wird. Entweder durch das setzen eines Breakpoints oder durch das dazufügen von irgendwelchen Log-Möglichkeiten.@otze,
Super Beitrag! Das hat nochmals einiges auf den Punkt gebracht. Vor allem die Definition mit dem "und" finde ich extrem gut. Das kann man wirklich vollständig so anwenden.Grüssli
-
Nexus schrieb:
[Streng gesehen sind dann auch Methoden der Figur wie z.B.
Move()oder ähnliches zur Vereinfachung verboten? D.h. gerade mal Getter und Setter? Oder gleich ein Struct verwenden?ein sehr gutes beispiel für eine MVC architektur in einem spiel wäre der sourcecode von ut2004 (unreal script, sehr c/java ähnlich, leicht lesbar, gibt gute editoren die die objekthierarchie darstellen können [wotgreal])
da ist es in etwa so aufgebaut:
es gibt eine Scene (kann irgendeine form von strkutur sein, im einfachsten fall eine Liste, oder ein Grid), in dieser scene gibt es Aktoren.
Aktoren sind grob gesagt alle objekte die in einer Scene plaziert werden können (position + dimension im raum).jetzt mal einige leafnodes aufgezählt (alles aktoren):
spieler, waffen, fahrzeuge, factories (weapon, spawnpoints), pathnodes, triggers, sounds, kameras (views). (nicht aber controller, und diverse helfer klassen)die interaktion mit dem spieler ist so aufgebaut:
die basis klasse eines spielers ist der Pawn (der Pawn ist ein Aktor welcher eine grafische Darstellung referenziert (mesh, sprite, was auch immer) )dann gibt es das Controller interface klasse + die Ableitungen davon.
ein Pawn wird durch entweder einen PlayerController / BotController (frei erfunden: NetController, MakroController/ DemoController) modifiziert.Der PlayerController ist im prinzip nur ein Leichter wrapper der das Input modell aufs Controller Interface biegt, und die position und zustand des Pawns modifiziert.
Der BotController modifiziert den Pawn durch irgendwelche Ausgeklügelten AI berechnungen.
Der NetController (frei erfunden) würde den Pawn durch Server Daten bewegen.
ein MacroController / DemoController (frei erfunden) könnte zb eine aufgenommene demo mit den bewegungen eines spieles wiedergeben.
der Pawn sollte auch keine kollisionserkennungsfunkion beinhalten.
für sowas ist die Scene zuständig.
die kollisionserkennung sollte aber auf der ebene des controllers aufgerufen werden, da erst der controller die notwendigen subsysteme den richtigen kontext setzen kann.fürs rendern ist auch nicht die Scene zuständig, sondern der Aktuelle Viewport.
wäre zb so: (vorweg möchte ich mich für meinen hühnerkrallenähnlichen schreibstil entschuldigen :-), weiters behandelt es nicht das problem mit der compilezeit!)
class Scene : public Singleton<Scene> { public: ... struct CollisionStats //collision locations, safe position / orientation { ... }; ... CollisionStats TestCollisions(Actor const& actor) const; //test against world CollisionStats TestCollisions(Actor const& actor1, Actor const& actor2) const; // hittest two actors enum { MAX_ACTORS = 2 } Actor* actors[MAX_ACTORS]; void Think(double frameTimeMS) { foreach (actor in actors) actor->Think(frameTimeMS); } Camera& GetCamera() {... } }; class Actor { ... protected: Vector3d location; virtual ~Actor(); public: Vector3d get_Location() const; void set_Location(Vector3d location); virtual void Think(double frameTimeMS) = 0; }; class Pawn: public Actor { Mesh& m_Mesh; public: Pawn(Mesh& mesh) : m_Mesh(mesh) { } Mesh& get_Mesh() const; void Move(Vector3d diff) { location += diff; } virtual void Think(double frameTimeMS) { m_Mesh.Animate(frameTimeMS); ... } }; class Controller { public: virtual void Think(double frameTimeMS) = 0; ... protected: Pawn& m_Pawn; Controller(Pawn& pawn) : m_Pawn(pawn) { } virtual ~Controller(); }; class PlayerController: public Controller { PlayerController(PlayerController& other); public: PlayerController(Pawn& pawn) : Controller(pawn) { } virtual void Think(double frameTimeMS) { Vector3d diff; float MoveSpeed = PlayerSettings::MoveSpeed(); diff.x = InputSystem::IsKeyDown(VK_LEFT) * -1.0 * MoveSpeed * frameTimeMS; diff.x = InputSystem::IsKeyDown(VK_RIGHT) * MoveSpeed * frameTimeMS; diff.z = InputSystem::IsKeyDown(VK_UP) * -1.0 * MoveSpeed * frameTimeMS; diff.z = InputSystem::IsKeyDown(VK_UP) * MoveSpeed * frameTimeMS; Scene::CollisionStats stats = Scene::Instance().TestCollision(m_Pawn); if (stats.HasCollision()) { m_Pawn.set_Location(stats.safeLocation); m_Pawn.set_Orientation(stats.safeOrientation); } else m_Pawn.Move(diff); } }; class Camera { ... }; class Viewport { //frustrum definitions void Draw(Scene const& scene) { foreach (actor in scene.Actors()) { if (IsVisible(actor)) Graphics::DrawActor(actor); } } bool IsVisible(Actor const& actor) { //test actor AABB against Frustrum, cull not visible actors } }; int main() { ResourceManager& resMgr = ResourceManager::Instance(); Pawn player(resMgr.GetMesh("someMesh")); Camera camera(10,10,10, //position 10, 10, -10, //look at 0, 1, 0); //topside orientation Scene &scene = Scene::Instance(); scene.LoadGeometry("someMap"); scene.AddActor(player); scene.SetCamera(camera); PlayerController controller(player); ViewPort viewport(scene.GetCamera()); ... //game loop bool bGameRunning = true; double frameTimeMS = HighResTimer::GetTimeDiff(); do { controller.Think(frameTimeMS); scene.Think(frameTimeMS); viewport.Draw(scene); frameTimeMS = HighResTimer::GetTimeDiff(); } while(bGameRunning) }
-
@ inpimp
Danke für deinen ausführlichen Beitrag. Ich fürchte nur, momentan übersteigt das meine Erfahrungen. Ich muss auch zuerst mal ein grundlegendes Design gut hinkriegen, bevor ich alles auf MVC setze und komplett neu beginne...Dravere schrieb:
Und auf welche Variablen trifft deine Beschreibung sonst zu?
Static Variablen kommen schliesslich weder auf den Heap noch Stack, sondern in den Global-Speicher.Naja, ist nun auch egal, wir wissen ja, was wir meinen

Hab mich nur gefragt, obs dafür extra ein Wort gäbe, aber scheinbar nicht...Dravere schrieb:
Habt ihr zufällig eine UBS-Filiale dort? Dann wickeln nämlich meine Eltern einen Teil ihrer Finanzen dort ab. Mein Vater ist im Kanton LU eingebürgert

Ja, die UBS gibts ja fast überall... Kennst du Kriens (hast du überhaupt schon davon gehört)?

-
Nexus schrieb:
@ inpimp
Danke für deinen ausführlichen Beitrag. Ich fürchte nur, momentan übersteigt das meine Erfahrungen. Ich muss auch zuerst mal ein grundlegendes Design gut hinkriegen, bevor ich alles auf MVC setze und komplett neu beginne...also von dem thread her finde ich das du das ziemlich schnell drauf haben solltest

einfach die fühler nach neuen informationsquellen ausstrecken und einsaugen
und wg dem physikalischem design solltest du dir wirklich "large scale c++ development" durchlesen / anlesen.
-
Okay, ich werd mal schauen, was sich machen lässt
