JavaScript - Snake-Spiel: Wie bringe ich die Schlange dazu, zu wachsen und sich wie im Spiel zu bewegen?

2 Antworten

Deine Schlange sollte ihre Segmente (Objekte mit x-/y-Position) in Form eines Arrays / einer Liste speichern. Um sie zu zeichnen, gehst du durch diese Liste, ermittelst die jeweilige Position (des Vorgängers) und zeichnest das Segment dann an dieser Stelle. Der Kopf bildet hier eine Ausnahme, für ihn berechnet sich die Position anhand der Summe aus den aktuellen Positionskoordinaten und dem Richtungsvektor.

Um zu wachsen braucht die Schlange, wenn sie mit einer Frucht kollidiert, lediglich ein neues Segment an die Liste hängen. Es wäre wohl am einfachsten, dabei den Kopf zu ersetzen.

Beispiel:

const nextPositionX = snake.segments[0].x + snake.directionX;
const nextPositionY = snake.segments[0].y + snake.directionY;

if (nextPositionX === fruit.x && nextPositionY === fruit.y) {
  const newHead = new Segment(fruit.x, fruit.y);
  snake.segments.unshift(newHead);
}
else {
  // move along ...
}

USERUn556 
Beitragsersteller
 06.09.2021, 15:02

Hallo regex9,

ich versuche gerade schon etwas länger mit meinem Wissen die Schlange zum Wachsen und Bewegen zu bringen. Ich komme dabei jedoch nicht mehr weiter. Könntest du mir bitte näher beschreiben, wie ich das hinbekomme. Am besten mit mehr Code, wenn es geht.

Mein Problem ist es, gerade die letzten Werte (x, y) von der Schlange (Schlangenkopf) abzufangen und beim nächsten durchlauf des Loops zu zeichnen.

LG

regex9  06.09.2021, 21:49
@USERUn556

Ich würde dir bei solchen Übungen dazu raten, Skizzen anzufertigen. Gerade dieses Szenario lässt sich ganz gut in einem Koordinatensystem / auf Kästchenpapier aufzuzeichnen. Insgesamt wären zwei Zustände ausreichend: Einmal die Schlange in irgendeiner Form und Länge und dann nochmal, aber diesmal um einen Schritt versetzt.

Überlege dir auf dieser Basis erst eigene Ansätze, bevor du weiterliest.

Zwei Lösungswege von meiner Seite:

a) Bei jedem Schritt wird das letzte Segment entfernt und dafür ein neues an den Anfang gehängt. Wie man Elemente anhängt, habe ich oben schon gezeigt. Für das Entfernen des letzten Elements eines Arrays kannst du entweder in der MDN-Dokumentation nachschlagen, ob es schon eine entsprechende Funktion dafür gibt oder aber du kopierst alle Elemente deines Arrays, bis auf das Letzte, in ein neues Array, welches künftig das alte Array ersetzt.

b) Für jeden Schritt wird jedes Segment um seine Position verrückt.

Würde sich die Schlange immer nur in eine Richtung bewegen, wäre es natürlich einfach. Man bräuchte nur durch jedes Segment gehen und dessen Position mit den Richtungsvektorwerten addieren. Da die Schlange sich aber auch schlängeln kann, müsste sich jedes Segment entweder an der ursprünglichen Richtung des Vorgängers orientieren oder einfacher, dessen vorherige Position übernehmen.

Für eine Umsetzung müsste man rückwärts durch die Schlangensegmente laufen. Täte man es andersherum, würde man mit jedem Schritt die Positionswerte überschreiben, an denen sich der jeweilige Nachfolger eigentlich orientieren soll.

Vom Schlangenende also angefangen, holt sich jedes Segment also nur die Positionswerte des Vorgängers im Array und setzt sie für sich selbst. Das allererste Element (der Kopf) wird jedoch übersprungen, da er bekanntermaßen keinen Vorgänger hat. Für ihn berechnest du die neue Position separat, mit Hilfe seiner aktuellen Position und dem Richtungsvektor.

Dieser Lösungsweg ist ohne Frage aufwendiger als der andere. Dafür aber ebenso eine gute Übung.

USERUn556 
Beitragsersteller
 05.09.2021, 21:27

Hallo regex9,

was wird mit

new Segment(fruti.x, fruit.y)

gemacht. Das habe ich noch nie so gesehen bzw. das ist mir unbekannt.

regex9  06.09.2021, 06:22
@USERUn556

In der Zeile wird ein Objekt eines Typs Segment erstellt. Die Definition für Segment (eine Klasse) würde so aussehen:

class Segment {
  constructor(x, y) {
    this.x = x;
    this.y = y;
  }
}

Genauso gut könnte man für die einzelnen Segmente jeweils ein anonymes Objekt kreieren:

const newHead = { x: fruit.x, y: fruit.y };

Wenn man in Sneak einen Apfel ist, dann wird lediglich verhindert, dass das letzte Element (= das hinterste Schwanz-Stück) aus der Liste gelöscht wird.

Du hast bei Snake eine Liste aller Körper-Teile deiner Schlange. Jedesmal wenn sich die Schlange bewegt fügst du die neuen Koordinaten des Kopfes zur Liste hinzu und löscht die Koordinaten des hintersten Stücks raus. Bei diesem rauslöschen baust du nun einfach eine If-Bedingung ein, die überprüft, ob du gerade einen Apfel gegessen hast.

Woher ich das weiß:Hobby – Programmieren ist mein Hobby & Beruf

USERUn556 
Beitragsersteller
 08.09.2021, 12:05

Und wie bringe ich die Schlange dazu sich so wie im Spiel zu bewegen?

MrAmazing2  08.09.2021, 15:00
@USERUn556

Du hast eine Variable für die Richtung.

Wenn der Nutzer WASD drückt ändert sich die entsprechend.

In deinem Game-Loop, (der bei Snake vlt. 2 mal pro Sekunde läuft), nimmst du dann das vordere Stück aus dem Snake-Array (also den Kopf), machst eine Kopie davon, änderst die Koordinaten entsprechend der Richtung der Schlange (z.B. falls Richtung==rechts machst du da x+=1), und fügst die Kopie in das Array ein.

Und dann zeichnest du wieder alle Stücke der Schlange -> Sie hat sich „bewegt“.