From e7289f25089d68a070e00d522042e8aab79c62f6 Mon Sep 17 00:00:00 2001 From: Florian Fischer Date: Sat, 23 Mar 2024 11:50:01 +0100 Subject: initial commit --- Snake.java | 257 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 257 insertions(+) create mode 100644 Snake.java (limited to 'Snake.java') diff --git a/Snake.java b/Snake.java new file mode 100644 index 0000000..9f31425 --- /dev/null +++ b/Snake.java @@ -0,0 +1,257 @@ +/* + * 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(); + } +} -- cgit v1.2.3