Programming lesson
Vererbung und abstrakte Klassen in Java: Baue dein eigenes Tetris-Spiel mit BlockAddiction
Lerne in diesem Tutorial, wie du Vererbung und abstrakte Klassen in Java anhand eines Tetris-ähnlichen Spiels namens BlockAddiction anwendest. Perfekt für deine COMS 2270 Hausaufgabe.
Einführung: Warum Vererbung und abstrakte Klassen wichtig sind
Stell dir vor, du entwickelst ein Spiel wie Tetris, bei dem verschiedene Spielsteine (Pieces) fallen – jeder mit eigener Form, Farbe und Bewegung. Ohne Vererbung müsstest du für jeden Stein die gleiche Logik immer wieder neu schreiben. Mit Vererbung und abstrakten Klassen kannst du gemeinsamen Code in einer Basisklasse bündeln und trotzdem spezifische Eigenschaften in Unterklassen definieren. Genau das ist die Aufgabe in Homework 4: BlockAddiction für COMS 2270.
In diesem Tutorial zeige ich dir Schritt für Schritt, wie du eine abstrakte Klasse AbstractPiece erstellst und davon sechs konkrete Unterklassen ableitest: LShapedPiece, TeePiece, QuotesPiece, RotatingSPiece, CirclingPiece und SnakingPiece. Du lernst, wie du 2D-Arrays und Listen effizient einsetzt, um die Positionen und Farben der Blöcke zu verwalten. Und das Beste: Du bekommst einen Einblick, wie moderne Spieleentwicklung mit Java funktioniert.
Was ist BlockAddiction?
BlockAddiction ist eine Mischung aus Tetris und Bejeweled. Das Spielfeld ist ein 2D-Gitter. Jede Zelle kann leer sein oder einen farbigen Block (Icon) enthalten. Ein aktueller Spielstein (Piece) fällt von oben. Du kannst ihn mit den Pfeiltasten bewegen, drehen (transform()) oder die Farben der Blöcke durchlaufen lassen (cycle()). Sobald der Stein nicht mehr weiter fällt, werden seine Blöcke ins Gitter eingefügt. Das Spiel prüft dann, ob drei oder mehr benachbarte Blöcke gleicher Farbe eine „Collapsible Set“ bilden – diese werden entfernt und die darüber liegenden Blöcke rücken nach.
Das Besondere: Du implementierst nur die sechs konkreten Stein-Klassen und die abstrakte Oberklasse. Die restliche Spiellogik (Kollisionserkennung, Entfernen von Sets, UI) ist bereits im bereitgestellten Code enthalten. Deine Aufgabe ist es, die Vererbungshierarchie so zu gestalten, dass du Code-Duplikate vermeidest.
Grundlagen: Package und API
Dein gesamter Code muss im Package hw4 liegen. Du erhältst ein API mit den Klassen api.Position (für Zeile/Spalte), api.Icon (für Farben) und dem Interface api.Piece. Dieses Interface definiert Methoden wie:
getCells()– gibt ein Array vonCell-Objekten zurück (jede Zelle hat Position und Icon)transform()– verändert die relativen Positionen der Blöcke innerhalb des 3x3-Begrenzungsrahmenscycle()– rotiert die Farben der Blöcke, ohne die Form zu ändernshiftLeft(),shiftRight(),shiftDown()– verschieben den Stein
Ein wichtiger Punkt: Alle Steine haben einen 3x3-Begrenzungsrahmen (Bounding Square). Die Position des Rahmens im Gitter wird durch getRow() und getCol() angegeben. Die Zellen des Steins werden relativ zu diesem Rahmen gespeichert. Zum Beispiel hat ein LShapedPiece Zellen an den relativen Positionen (0,0), (0,1), (1,1), (2,1) – das ergibt die L-Form.
Schritt 1: Abstrakte Klasse AbstractPiece
Erstelle eine abstrakte Klasse AbstractPiece, die das Interface Piece implementiert. Hier sammelst du den Code, der für alle Steine gleich ist:
- Speichern der aktuellen Position (Zeile, Spalte) des Rahmens
- Speichern des Zellen-Arrays (
Cell[]) - Implementierung von
getCells(),getRow(),getCol() - Methoden zum Verschieben:
shiftLeft(),shiftRight(),shiftDown()– diese ändern einfach die Position des Rahmens
Aber Achtung: transform() und cycle() sind in jeder Unterklasse anders. Mach diese Methoden abstrakt, sodass jede Unterklasse ihre eigene Implementierung bereitstellen muss. So stellst du sicher, dass alle Steine die gleichen Methoden haben, aber unterschiedlich reagieren.
Beispiel für shiftLeft():
public void shiftLeft() {
col--;
}Das war's schon. Die Kollisionsprüfung übernimmt das Framework.
Schritt 2: Konkrete Unterklassen
Jetzt erstellst du sechs Klassen, die von AbstractPiece erben. Jede Klasse muss:
- Im Konstruktor die Zellen für die spezifische Form setzen
transform()implementieren – meist eine Drehung der relativen Positionencycle()implementieren – meist eine Rotation der Farben
Beispiel: LShapedPiece
Die L-Form hat vier Blöcke. Im Konstruktor erstellst du ein Array mit vier Cell-Objekten. Die relativen Positionen sind z.B. (0,0) mit Icon.YELLOW, (0,1) mit Icon.CYAN, (1,1) mit Icon.GREEN, (2,1) mit Icon.RED. Für transform() könntest du die Blöcke um 90 Grad drehen: (0,0) -> (0,2), (0,1) -> (1,2), (1,1) -> (1,1), (2,1) -> (1,0). Das erfordert eine Umrechnung der Koordinaten.
Beispiel: TeePiece
Die T-Form: Blöcke an (0,0), (0,1), (0,2), (1,1). Hier ist cycle() interessant: Du verschiebst die Farben zyklisch, z.B. von Block 1 zu Block 2, Block 2 zu Block 3 usw. Die Form bleibt gleich, nur die Farben ändern sich.
Tipps aus der Praxis: Wie du Zeit sparen kannst
Die Aufgabenstellung warnt vor typischen Fehlern: „Fang nicht erst in der Nacht vor der Abgabe an!“ – das ist kein Scherz. Ich habe selbst schon erlebt, wie Kommilitonen stundenlang an einem Semikolon gesessen haben, weil sie den Code nicht getestet haben. Mein Rat: Lies zuerst die bereitgestellten Dateien SamplePiece und Piece im API. Verstehe, wie die Zellen und Positionen funktionieren. Schreibe dann Tests für jeden Stein, bevor du den nächsten machst.
Ein weiterer Tipp: Nutze die 2D-Arrays geschickt. Statt die relativen Positionen jedes Mal neu zu berechnen, kannst du für jeden Stein eine Transformationsmatrix definieren. Das macht den Code sauberer und leichter erweiterbar.
Häufige Fallstricke
- Falsche Package-Deklaration: Deine Klassen müssen in
hw4sein, sonst findet das Framework sie nicht. - Vergessen,
super()im Konstruktor aufzurufen: Die abstrakte Klasse braucht Position und Zellen. - Falsche Implementierung von
getCells(): Die Methode muss ein neues Array zurückgeben, das die aktuellen Positionen im Gitter widerspiegelt (relative Position plus Rahmenposition).
Trend-Verbindung: Wie Tetris die KI-Welt inspiriert
Tetris ist nicht nur ein Kultspiel, sondern auch ein beliebtes Testfeld für Künstliche Intelligenz. Im Mai 2026 haben Forscher auf der International Conference on Machine Learning gezeigt, wie Reinforcement Learning Agenten Tetris spielen – mit verblüffend ähnlichen Konzepten wie deiner Hausaufgabe: Die Agenten lernen, welche Züge (transform, cycle, shift) in welcher Situation am besten sind. Deine Implementierung von AbstractPiece ist quasi die Grundlage für solche KI-Systeme. Wer weiß, vielleicht entwickelst du später einen Algorithmus, der den Highscore von 1 Million Punkten knackt!
Auch in der App-Entwicklung ist das Vererbungsprinzip allgegenwärtig. Denk an die Spiel-Apps auf deinem Smartphone: Jedes Spiel hat eine Basisklasse GameObject, von der alle Figuren erben. Das spart Code und macht Updates einfacher. Wenn du also heute AbstractPiece schreibst, übst du für echte Projekte.
Zusammenfassung
In diesem Tutorial hast du gelernt, wie du mit Vererbung und abstrakten Klassen in Java ein Tetris-ähnliches Spiel strukturierst. Du hast eine abstrakte Oberklasse AbstractPiece erstellt und sechs konkrete Unterklassen implementiert. Du hast gesehen, wie 2D-Arrays und Listen verwendet werden, um Spielsteine zu verwalten. Und du hast einen Ausblick bekommen, wie diese Konzepte in KI und App-Entwicklung angewendet werden.
Jetzt bist du dran: Öffne Eclipse, importiere das Skeleton-Projekt und leg los. Denk daran: Teste früh und oft. Viel Erfolg bei deiner BlockAddiction Hausaufgabe!