/* * Copyright (c) 2024 Florian Fischer. All rights reserved. * This file is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * It is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ import java.awt.event.*; import java.util.Random; /** * Die Klasse Snake verwaltet ein einfaches Snake-Spiel. */ class Snake implements Zeichenfenster.AktionsEmpfaenger { Random zufall; Schlange schlange; Rechteck essen; long tick; /** * Konstruktor der Klasse Snake. * Erzeugt eine Umrandung der Spielflaeche sowie die Schlange. */ public Snake() { Zeichenfenster.AktionsEmpfängerEintragen(this); // Setze die Taktdauer auf 100ms ~ 10 Bilder/Sekunde Zeichenfenster.TaktdauerSetzen(100); // Initalisiere den Zufallsgenerator zufall = new Random(); // Initialisiere die Schlange schlange = new Schlange(380, 280); // Erzeuge den Rand der Welt Rechteck rand = new Rechteck(); rand.FarbeSetzen("schwarz"); rand.GrößeSetzen(800, 600); rand.PositionSetzen(0, 0); // Erzeuge den Hintergrund der Welt Rechteck hintergrund = new Rechteck(); hintergrund.FarbeSetzen("weiss"); hintergrund.GrößeSetzen(800 - 4, 600 - 4); hintergrund.PositionSetzen(2, 2); // Zeichne den Hintergrund ueber dem Rand hintergrund.GanzNachHintenBringen(); rand.GanzNachHintenBringen(); } /** * Haelt das Spiel an. */ void gameOver() { Zeichenfenster.TaktgeberStoppen(); Text text = new Text(); text.TextSetzen("Game Over!"); } /** * Wird bei jedem Tick ausgeführt. * Sorgt für die Bewegung der Schlange und erzeugt alle * 30 Takte neues Essen. */ public void Ausführen() { tick++; schlange.bewegen(); // Erzeuge alle dreißig Takte neues Essen if (tick % 30 == 0) { erzeugeEssen(); } // Hat die Schlage das Essen gefressen? if (essen != null && schlange.beruehrt(essen)) { schlange.wachse(essen.x, essen.y); essen.Entfernen(); essen = null; } } /** * Methode die aufgerufen wird wenn eine Taste gedrueckt wurde * @param taste die gedrueckte Taste */ public void Taste (char taste) { switch (taste) { case 's': schlange.setzeRichtung("runter"); break; case 'w': schlange.setzeRichtung("rauf"); break; case 'a': schlange.setzeRichtung("links"); break; case 'd': schlange.setzeRichtung("rechts"); break; } } /** * Methode die aufgerufen wird wenn eine Sondertaste gedrueckt wurde * @param taste das java.awt.event.KeyEvent der gedrueckten Sondertaste */ public void SonderTaste (int taste) { switch (taste) { case KeyEvent.VK_DOWN: schlange.setzeRichtung("runter"); break; case KeyEvent.VK_UP: schlange.setzeRichtung("rauf"); break; case KeyEvent.VK_LEFT: schlange.setzeRichtung("links"); break; case KeyEvent.VK_RIGHT: schlange.setzeRichtung("rechts"); break; } } public void Geklickt (int x, int y, int anzahl) { } /** * Erzeugt ein neues Essens-Rechteck an einer zufaelligen Position. */ void erzeugeEssen() { // Entferne das alte Essen if (essen != null) { essen.Entfernen(); } essen = new Rechteck(); essen.FarbeSetzen("rot"); essen.GrößeSetzen(20, 20); // Die neue X-Koordinate liegt im Intervall [20; 780] int x = zufall.nextInt((800 - 40) / 20) * 20 + 20; // Die neue Y-Koordinate liegt im Intervall [20; 580] int y = zufall.nextInt((600 - 40) / 20) * 20 + 20; essen.PositionSetzen(x, y); } /** * Die Klasse Schlange verwaltet die rechteckigen Koerperteile. */ class Schlange { /** Die Richtung in die sich die Schlange bewegt. */ String richtung; /** Koerperteile der Schlange. */ Rechteck koerper[]; /** * Konstruktor für Objekte der Klasse Schlange * @param x die X-Koordinate des Kopfes * @param y die Y-Koordinate des Kopfes */ public Schlange(int x, int y) { // Instanzvariablen initialisieren richtung = "runter"; koerper = new Rechteck[0]; // Laesst den Kopf der Schlange wachsen wachse(x, y); } /** * Laesst ein neues Koerperteil wachsen * @param x die X-Koordinate des neuen Koerperteils * @param y die Y-Koordinate des neuen Koerperteils */ void wachse(int x, int y) { Rechteck[] neuerKoerper = new Rechteck[koerper.length + 1]; Rechteck kopf = new Rechteck(); kopf.PositionSetzen(x, y); kopf.GrößeSetzen(20, 20); kopf.FarbeSetzen("gruen"); neuerKoerper[0] = kopf; // Kopiere die alten Koerperteile for (int i = 0; i < koerper.length; i++) { neuerKoerper[i + 1] = koerper[i]; } koerper = neuerKoerper; } /** * Setzt die Bewegungsrichtung der Schlange * Abrupte Richtungswechsel sind nicht erlaubt. * @param neueRichtung die neue Laufrichtung der Schlange */ void setzeRichtung(String neueRichtung) { // Erlaube keine abrupten Richtungswechsel bei denen // die Schlange sich selbst fressen wuerde. if ((richtung == "links" && neueRichtung != "rechts") || (richtung == "rechts" && neueRichtung != "links") || (richtung == "rauf" && neueRichtung != "runter") || (richtung == "runter" && neueRichtung != "rauf") ) { richtung = neueRichtung; } } /** * Testet ob der Kopf der Schlange ein Rechteck beruehrt. * @param rechteck das Rechteck, das getestet werden soll. * @return true, wenn der Kopf der Schlange das Rechteck schneidet. */ boolean beruehrt(Rechteck rechteck) { Rechteck kopf = koerper[0]; return kopf.symbol.Schneidet(rechteck.symbol.form); } /** * Bewege die Schlange um einen Schritt und behandle Kollisionen */ void bewegen() { int deltaX = 0; int deltaY = 0; switch (richtung) { case "rauf": deltaY = -20; break; case "runter": deltaY = 20; break; case "links": deltaX = -20; break; case "rechts": deltaX = 20; break; default: } Rechteck kopf = koerper[0]; // Ziehe alle Koerperteile auf die Position ihres Vorgaengers for (int i = koerper.length - 1; i > 0; i--) { Rechteck vorgaenger = koerper[i-1]; koerper[i].PositionSetzen(vorgaenger.x, vorgaenger.y); } // Bewege den Kopf der Schlange kopf.Verschieben(deltaX, deltaY); // Ist die Schlange gegen den Rand gelaufen? if (kopf.x <= 0 || kopf.x >= 800 - 20 || kopf.y <= 0 || kopf.y >= 600 - 20) { gameOver(); } // Beruehrt der Kopf ein anderes Koerperteil der Schlange? for (int i = 1; i < koerper.length; i++) { if (beruehrt(koerper[i])) { gameOver(); } } } } /** * Erzeuge und starte ein neues Spiel. */ public static void main(String[] argv) { Snake s = new Snake(); Zeichenfenster.TaktgeberStarten(); } }