Autor |
Beitrag |
PChelper
Hält's aus hier
Beiträge: 2
|
Verfasst: So 09.05.10 16:00
Hi Leute!
Für ein Schulprojekt im Informatikunterricht soll ich mit einem Teampartner Tetris mit Delphi objektorientiert programmieren. Bisher hat alles wunderbar geklappt, jetzt kommen wir allerdings bei der Kollision zwischen zwei oder mehreren Steinen nicht weiter. Die nächsten Wochen fällt unser Informatikunterricht aus, deshalb kann ich unsere Lehrer nicht fragen und bitte euch um Hilfe.
Erstmal eine kleiner Überblick:
Wir haben das Ganze nach den Grundsätzen der OOP aufgebaut. Es gibt 3 Oberklassen (TfAnwendung, TSpielfeld und TSteine). Die Klasse TSteine hat 7 Unterklassen (für die sieben unterschiedlichen Steintypen): TStein1, TStein2, TStein3, TStein4, TStein5, TStein6 und TStein7.
Die Klasse TfAnwendung hat/besitzt die alle anderen Klassen. Die Klasse TSteine kennt dabei die Klasse TSpielfeld (um auf Anfragen zuzugreifen können, die den Spielfeldrand zurückgeben). Um das Ganze zu verdeutlichen habe ich hier mal eine vereinfachte Version des Klassendiagramms in UML hochgeladen:
Bisher klappt wie gesagt alles wunderbar. Die Steine "fallen" von unten nach oben, "wandern" nicht aus dem Spielfeld, die nächsten Steine werden per Zufallsgenerator ausgewählt - alles super.
Doch die Steine kennen sich nicht gegenseitig und fallen so ineinander. Dazu müsste jede Klasse (TStein1-TStein7) jede andere Klasse (TStein1-TStein7) kennen. Allerdings funktionieren soweit ich weiß in Delphi keine bidirektionale Assoziationen (überkreuzende kennt-Beziehungen). Jedenfalls gibt mir Delphi immer Fehlermeldungen aufgrund der Überkreuzung aus.
Oder habe ich etwas übersehen? Wie kriege ich das Programm und die Kollision zwischen den Steinen zum Laufen? Oder muss ich die Klassen anders anordnen, damit es funktioniert?
MfG, PChelper
Moderiert von Narses: Bild als Anhang hochgeladen.
Einloggen, um Attachments anzusehen!
|
|
Jakob_Ullmann
      
Beiträge: 1747
Erhaltene Danke: 15
Win 7, *Ubuntu GNU/Linux*
*Anjuta* (C, C++, Python), Geany (Vala), Lazarus (Pascal), Eclipse (Java)
|
Verfasst: So 09.05.10 16:23
Zeig doch mal deinen Code, den du bis jetzt hast.
Möglich wäre auf jeden Fall eine Lösung über Interfaces.
|
|
jaenicke
      
Beiträge: 19313
Erhaltene Danke: 1747
W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: So 09.05.10 16:49
PChelper hat folgendes geschrieben : | Doch die Steine kennen sich nicht gegenseitig und fallen so ineinander. Dazu müsste jede Klasse (TStein1-TStein7) jede andere Klasse (TStein1-TStein7) kennen. |
Nein, das wäre der falsche Weg. Denn die Steine haben erst einmal nichts mit der Logik des Spiels zu tun. Diese organisieren nur sich selbst.
TSpielfeld kennt die aktuell vorhandenen Steine alle, kann also die Kollisionen prüfen. Auf jeden Fall gehört die Kollisionsabfrage dorthin. Möglich ist zum Beispiel eine Methode CheckCollision bei TSteine unterzubringen um die Kollision zweier Steine zu prüfen. Das heißt diese bekommt einen zweiten TSteine (also die Oberklasse) und sagt, ob zwischen diesem Stein und sich selbst eine Kollision existiert.
Dafür braucht TSteine nur die Möglichkeit allgemein auf eine Kollision zu prüfen.
|
|
Kha
      
Beiträge: 3803
Erhaltene Danke: 176
Arch Linux
Python, C, C++ (vim)
|
Verfasst: So 09.05.10 16:59
 !
PChelper hat folgendes geschrieben : | Dazu müsste jede Klasse (TStein1-TStein7) jede andere Klasse (TStein1-TStein7) kennen. |
Das würde erstens nicht wirklich der OOP entsprechen (/edit: wie jaenicke schon sagte) und zweitens funktioniert Tetris so nicht. Schließlich können Steine durch das Entfernen von Zeilen ihre ursprüngliche Form verlieren, es macht also keinen Sinn, liegende Steine als einzelne Objekte darzustellen. Vielmehr musst du dein Spielfeld als zweidimensionales Array von Blöcken (im einfachsten Fall ein Boolean, später noch inkl. Farbe) speichern. Der fallende Stein überprüft dann in jedem Schritt, ob er in diesem Array Platz finden würde. Wenn nicht, haben wir eine Kollision, der Stein trägt sich an der vorherigen Position in das Array ein und die Instanz kann aufgelöst werden.
Übrigens halte ich eine TStein-(nicht TSteine!)Basisklasse für übertrieben. Vererbung dient dazu, das Verhalten zu ändern/neues hinzuzufügen, wir reden hier aber lediglich über Daten wie die Form - und dafür sind verschiedene Instanzen, nicht Klassen, zuständig. Ich würde TStein als Attribute lediglich die Position und eine Referenz auf ein TSteinTyp-Objekt geben, von dem irgendwo als Konstante ein Array aus sieben Objekten deklariert wurde. Aber falls euch das so "zu wenig OOP" sein sollte, ist wahrscheinlich auch der bisherige Aufbau akzeptabel  .
_________________ >λ=
|
|
jaenicke
      
Beiträge: 19313
Erhaltene Danke: 1747
W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: So 09.05.10 17:02
|
|
PChelper 
Hält's aus hier
Beiträge: 2
|
Verfasst: So 09.05.10 17:20
Erstmal sorry für die Crossposts, ich wusste nicht, dass man das hier angeben soll.
Vielen Dank schonmal für eure bisherige Hilfe. Hier zur besseren Übersicht mein kompletter Quellcode aus allen Units. Teilweise nicht so sauber programmiert - ich lerne ja noch. Vielleicht könnt ihr mir nochmal genauer erklären, wie das mit dem zweidimensionalen Array funktionieren soll (Deklaration müsste ja "array [1..12, 1..20 ]" (Spielfeld soll 20 Höheneinheiten hoch und 12 Höheneinheiten breit sein) sein, aber wie ich das Ganze dann aufbauen sollte. Programmieren werde ich das dann natürlich selbst. Bin dankbar für alle Tipps und Hilfen.
Moderiert von Narses: Code als Anhang hinzugefügt.
Einloggen, um Attachments anzusehen!
|
|
jaenicke
      
Beiträge: 19313
Erhaltene Danke: 1747
W11 x64 (Chrome, Edge)
Delphi 11 Pro, Oxygene, C# (VS 2022), JS/HTML, Java (NB), PHP, Lazarus
|
Verfasst: So 09.05.10 18:47
Also sofort fällt mir erst einmal auf, dass du vieles mehrfach drin hast. Und zwar, weil TSteine eigentlich die Methoden haben sollte, die dann bei Bedarf von den abgeleiteten Steinen überschrieben werden hätten sollen.
Dann noch etwas: Der Konstruktor heißt Create und des Desktruktor Destroy. Das sollte man nie nie nie ändern. Zudem gehört dahinter nicht virtual sondern override, jedenfalls beim Destruktor.
Dann ist dein Kommentar hinter protected und public ein wenig missverständlich. protected heißt nicht, dass da deine Daten hineinkommen und public, dass da die Methoden hineingehören!
Das sind die Sichtbarkeiten der darin enthaltenen Felder und Methoden. private heißt nur in der Klasse und Unit sichtbar, protected in den davon abgeleiteten Klassen auch und public für alle (ganz kurz gesagt).
So, mal ein kleines Codeschnipselchen: Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17:
| TStein = class protected public constructor Create(xPos: integer; yPos: integer; breite: integer; farbe: TColor); destructor Destroy; override; procedure ab; virtual; procedure auf; virtual; ... end;
TStein1 = class(TStein) protected public procedure ab; override; procedure auf; override; ... end; | Und wenn ab zum Beispiel immer das selbe macht, dann kann das auch in TStein stehen und muss nicht in jeder abgeleiteten Klasse noch einmal stehen. Bei Bedarf kann das aber überschrieben werden, wenn bei einem der Steine doch etwas anderes passieren soll.
|
|
|