2D-Spielprogrammierung in C Tutorial: Snake

click fraud protection

Der Zweck dieses Tutorials ist es, 2D-Spielprogrammierung und C-Sprache anhand von Beispielen zu vermitteln. Der Autor programmierte Mitte der 1980er Jahre Spiele und war in den 90er Jahren ein Jahr lang Spieledesigner bei MicroProse. Obwohl vieles davon für die Programmierung der heutigen großen 3D-Spiele nicht relevant ist, wird es für kleine Gelegenheitsspiele als nützliche Einführung dienen.

Snake implementieren

Spiele wie die Schlange, bei denen sich Objekte über ein 2D-Feld bewegen, können die Spielobjekte entweder in einem 2D-Raster oder als eindimensionales Array von Objekten darstellen. "Objekt" bedeutet hier jedes Spielobjekt, kein Objekt, wie es in der objektorientierten Programmierung verwendet wird.

Spielsteuerung

Die Tasten werden mit W = oben, A = links, S = unten, D = rechts bewegt. Drücken Sie die Esc-Taste, um das Spiel zu beenden, f, um die Bildrate umzuschalten (dies ist nicht mit der Anzeige synchronisiert, kann also schnell sein), die Tabulatortaste, um die Debug-Informationen umzuschalten, und p, um sie anzuhalten. Wenn es angehalten ist, ändert sich die Beschriftung und die Schlange blinkt.

instagram viewer

In der Schlange sind die Hauptspielobjekte

  • Die Schlange
  • Fallen und Obst

Für das Gameplay enthält eine Reihe von Ints jedes Spielobjekt (oder einen Teil für die Schlange). Dies kann auch beim Rendern der Objekte in den Bildschirmpuffer hilfreich sein. Ich habe die Grafik für das Spiel wie folgt gestaltet:

  • Horizontaler Schlangenkörper - 0
  • Vertikaler Schlangenkörper - 1
  • Kopf in 4 x 90-Grad-Umdrehungen 2-5
  • Schwanz in 4 x 90-Grad-Umdrehungen 6-9
  • Kurven für Richtungsänderungen. 10-13
  • Apple - 14
  • Erdbeere - 15
  • Banane - 16
  • Falle - 17
  • Zeigen Sie die Snake-Grafikdatei snake.gif an

Es ist daher sinnvoll, diese Werte in einem Rastertyp zu verwenden, der als Block [WIDTH * HEIGHT] definiert ist. Da das Raster nur 256 Positionen enthält, habe ich es ausgewählt, um es in einem eindimensionalen Array zu speichern. Jede Koordinate im 16 x 16-Raster ist eine Ganzzahl von 0 bis 255. Wir haben Ints verwendet, damit Sie das Raster vergrößern können. Alles wird durch #defines mit WIDTH und HEIGHT definiert, beide 16. Da die Schlangengrafiken 48 x 48 Pixel groß sind (GRWIDTH und GRHEIGHT #defines), wird das Fenster zunächst als 17 x GRWIDTH und 17 x GRHEIGHT definiert, um nur geringfügig größer als das Raster zu sein.

Dies hat Vorteile für die Spielgeschwindigkeit, da die Verwendung von zwei Indizes immer langsamer als einer ist, aber anstatt 1 von den Y-Koordinaten der Schlange zu addieren oder zu subtrahieren, um sich vertikal zu bewegen, subtrahieren Sie WIDTH. Addiere 1, um nach rechts zu gehen. Da wir jedoch hinterhältig sind, haben wir auch ein Makro l (x, y) definiert, das die x- und y-Koordinaten zur Kompilierungszeit konvertiert.

Was ist ein Makro?

 #define l (X, Y) (Y * WIDTH) + X.

Die erste Zeile ist Index 0-15, die zweite 16-31 usw. Befindet sich die Schlange in der ersten Spalte und bewegt sie sich nach links, muss bei der Prüfung, die die Wand trifft, bevor sie sich nach links bewegt, geprüft werden, ob die Koordinate% WIDTH == 0 und für die rechte Wand die Koordinate% WIDTH == WIDTH-1 ist. Das% ist der C-Modul-Operator (wie die Taktarithmetik) und gibt den Rest nach der Division zurück. 31 div 16 hinterlässt einen Rest von 15.

Die Schlange verwalten

Im Spiel werden drei Blöcke (Int-Arrays) verwendet.

  • Schlange [], ein Ringpuffer
  • shape [] - Enthält Grafikindizes für Schlangen
  • dir [] - Enthält die Richtung jedes Segments in der Schlange, einschließlich Kopf und Schwanz.

Zu Beginn des Spiels ist die Schlange zwei Segmente lang mit einem Kopf und einem Schwanz. Beide können in 4 Richtungen zeigen. Für den Norden ist der Kopf Index 3, der Schwanz ist 7, für den Ostkopf ist 4, der Schwanz ist 8, für den Südkopf ist 5 und der Schwanz ist 9, und für den Westen ist der Kopf 6 und der Schwanz ist 10. Während die Schlange zwei Segmente lang ist, sind Kopf und Schwanz immer um 180 Grad voneinander entfernt, aber nachdem die Schlange gewachsen ist, können sie 90 oder 270 Grad betragen.

Das Spiel beginnt mit dem Kopf nach Norden an Position 120 und dem Schwanz nach Süden bei 136, ungefähr in der Mitte. Bei geringen Kosten von etwa 1.600 Byte Speicher können wir eine erkennbare Geschwindigkeitsverbesserung im Spiel erzielen, indem wir die Positionen der Schlange im oben erwähnten Schlangenringpuffer [] halten.

Was ist ein Ringpuffer?

Ein Ringpuffer ist ein Speicherblock, der zum Speichern einer Warteschlange mit fester Größe verwendet wird und groß genug sein muss, um alle Daten aufzunehmen. In diesem Fall ist es nur für die Schlange. Die Daten werden auf die Vorderseite der Warteschlange verschoben und von der Rückseite entfernt. Wenn die Vorderseite der Warteschlange das Ende des Blocks erreicht, wird sie umbrochen. Solange der Block groß genug ist, wird die Vorderseite der Warteschlange niemals die Rückseite einholen.

Jeder Ort der Schlange (d. H. Die einzelne int-Koordinate) vom Schwanz zum Kopf (d. H. Rückwärts) wird im Ringpuffer gespeichert. Dies bietet Geschwindigkeitsvorteile, da unabhängig von der Länge der Schlange nur der Kopf, der Schwanz und das erste Segment nach dem Kopf (falls vorhanden) während der Bewegung geändert werden müssen.

Es ist auch vorteilhaft, es rückwärts zu lagern, denn wenn die Schlange Nahrung bekommt, wächst die Schlange, wenn sie das nächste Mal bewegt wird. Dies erfolgt durch Verschieben des Kopfes um eine Stelle im Ringpuffer und Ändern der alten Kopfposition in ein Segment. Die Schlange besteht aus einem Kopf, 0-n-Segmenten und einem Schwanz.

Wenn die Schlange Futter frisst, wird die Variable atefood auf 1 gesetzt und in der Funktion DoSnakeMove () überprüft.

Die Schlange bewegen

Wir verwenden zwei Indexvariablen, headindex und tailindex, um auf die Kopf- und Schwanzpositionen im Ringpuffer zu zeigen. Diese beginnen bei 1 (Kopfindex) und 0. Position 1 im Ringpuffer enthält also die Position (0-255) der Schlange auf dem Brett. Position 0 enthält die Endposition. Wenn sich die Schlange um eine Position vorwärts bewegt, werden sowohl der Endindex als auch der Kopfindex um eins erhöht und bei Erreichen von 256 auf 0 gewickelt. Jetzt ist der Ort, an dem sich der Kopf befand, der Ort, an dem sich der Schwanz befindet.

Selbst mit einer sehr langen Schlange, die sich in etwa 200 Segmenten windet und verwickelt. Nur der Kopfindex, das Segment neben dem Kopf- und der Endindex ändern sich bei jeder Bewegung.

Beachten Sie wegen des Weges SDL funktioniert, müssen wir die gesamte Schlange in jedem Frame zeichnen. Jedes Element wird in den Bildspeicher gezogen und dann umgedreht, damit es angezeigt wird. Dies hat jedoch den Vorteil, dass wir die Schlange ein paar Pixel reibungslos bewegen können, nicht eine ganze Gitterposition.

instagram story viewer