28. Array-basierte Gitterraster¶
28.1. Einführung¶
Spiele wie Minesweeper, Tic-Tac-Toe und viele Arten von Adventures speichern die Daten für das Spiel in einem Zahlenraster. Zum Beispiel ein Tic-Tac-Toe-Brett:
O |
O |
|
X |
||
X |
… kann ein Zahlengitter verwenden, um die leeren Punkte, die O und die X wie folgt darzustellen:
0 |
2 |
2 |
0 |
1 |
0 |
1 |
0 |
0 |
Dieses Zahlenraster kann auch als zweidimensionales Array oder Matrix bezeichnet werden. (Endlich lernen wir Die Matrix kennen.) Die Werte der Zahlen im Raster geben an, was an jeder Position des Spielfelds angezeigt werden soll. Im vorherigen Beispiel steht 0 für eine Stelle, an der noch niemand gespielt hat, eine 1 für ein X und eine 2 für ein O.

Abbildung 16.1: Minesweeper-Spiel mit dem dahinterstehenden Zahlengitter¶
Die obige Abbildung ist ein Beispiel aus dem klassischen Minesweeper-Spiel. Dieses Beispiel wurde geändert, um sowohl die klassische Anzeige auf der linken Seite als auch das Zahlenraster für die Anzeige des Spielfelds auf der rechten Seite anzuzeigen.
Die Zahl 10
steht für eine Mine, die Zahl 0
für ein nicht angeklicktes Feld und die Zahl 9
für ein gelöschtes Feld. Die Zahlen 1
bis 8
geben an, wie viele Minen sich in den umliegenden acht Feldern befinden, und werden nur dann ausgefüllt, wenn der Benutzer auf das Feld klickt.
Minesweeper kann eigentlich zwei Gitter haben. Eine für die reguläre Anzeige und ein vollständig getrenntes Zahlenraster, das nachverfolgt, ob der Benutzer „Flaggen“ auf die Tafelmarkierung gesetzt hat, an der er denkt, dass sich die Minen befinden.
Klassische Adventure Maps werden mit einem auf Kacheln basierenden Karteneditor erstellt. Dies sind riesige Gitter, in denen jeder Ort einfach eine Zahl ist, die den Geländetyp darstellt, der dorthin gehört. Das Gelände könnte aus Sand, einer Straße, einem Pfad, grünem Gras, braunem Gras und so weiter bestehen. Mit Programmen wie Tiled, das in der folgenden Abbildung gezeigt werden, kann ein Entwickler diese Karten auf einfache Weise erstellen und das Raster auf die Festplatte schreiben.

Figure 16.2: Using Tiled Map Editor to create an adventure map¶
In Adventures werden auch mehrere Zahlenraster verwendet, genau wie bei Minesweeper ein Raster für die Minen und ein separates Raster für die Flaggen. Ein Raster oder „Layer“ im Adventure steht für begehbares Gelände. Ein anderes für Dinge, auf denen man nicht laufen kann wie Mauern und Bäume. Ein Layer für Dinge, die dich sofort töten können, wie Lava oder bodenlose Gruben. Einer für Gegenstände, die aufgenommen und bewegt werden können und noch eine Ebene für die Anfangspositionen von Monstern.
Maps like these can be loaded in a Python program, which is shown in more detail with the Python Arcade Library documentation.
28.2. Anwendung¶
Genug geredet, schreiben wir etwas Code. In diesem Beispiel wird ein Raster erstellt, das auslösen wird, ob ein weißer oder grüner Block angezeigt wird. Wir können den Rasterwert ändern und ihn grün machen, indem wir darauf klicken. Dies ist ein erster Schritt zu einem rasterbasierten Spiel wie Minesweeper, Schiffe versenken, Vier gewinnt und so weiter. (In einem Schuljahr wurde ich von einer Studierenden an ihren Rechner gerufen. Sie hatte ein Programm wie dieses so modifiziert, dass mein Name in blinkenden Feldern angezeigt wurde. Das war … beunruhigend. Bitte nutze dieses Wissen nur zum Guten!)
Beginnen wir mit dieser Vorlage:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | import arcade
SCREEN_WIDTH = 500
SCREEN_HEIGHT = 600
class MyGame(arcade.Window):
"""
Main application class.
"""
def __init__(self, width, height):
super().__init__(width, height)
arcade.set_background_color(arcade.color.WHITE)
def on_draw(self):
"""
Render the screen.
"""
arcade.start_render()
def on_mouse_press(self, x, y, button, key_modifiers):
"""
Called when the user presses a mouse button.
"""
pass
def main():
window = MyGame(SCREEN_WIDTH, SCREEN_HEIGHT)
arcade.run()
if __name__ == "__main__":
main()
|
Beginne mit der obigen Datei und versuche, dieses Programm neu zu erstellen, indem Sie den Anweisungen hier folgen. Das endgültige Programm befindet sich am Ende dieses Kapitels, aber springe nicht direkt dahin und kopiere es! Wenn du das tust, hast du nichts gelernt. Jeder kann den Code kopieren und einfügen, aber wenn du dieses Programm neu erstellen kannst, verfügst du über Fähigkeiten, für die die Leute bereit sind, zu bezahlen. Wenn du nur kopieren und einfügen kannst, hast du hier deine Zeit verschwendet.
28.2.1. Raster zeichnen¶
Erstelle Variablen mit den Namen
WIDTH
,HEIGHT
undMARGIN
. Stelle die Breite und Höhe auf 20 ein. Dies gibt an, wie groß die einzelnen Rasterpositionen sind. Setze denMARGIN
auf 5. Dies stellt den Rand zwischen den einzelnen Rasterpositionen und den Rändern des Bildschirms dar. Erstelle diese Variablen oben im Programm nach denimport
-Anweisungen. Erstelle auch die VariablenROW_COUNT
undCOLUMN_COUNT
. Stelle sie auf 10 ein. Dadurch wird gesteuert, wie viele Zeilen und Spalten wir haben werden.Berechne
SCREEN_WIDTH
undSCREEN_HEIGHT
basierend auf den oben erstellten Variablen. Wenn wir 10 Zeilen haben und jede Zeile 20 hoch ist, sind das 200 Pixel. Wenn wir 10 Zeilen haben, sind das auch 11 Ränder. (Neun zwischen den Zellen und zwei an jeder Kante.) Das sind 55 weitere Pixel für insgesamt 255. Schreibe die Gleichung so, dass sie mit allen in Schritt 1 erstellten Konstanten funktioniert.Ändere den Hintergrund in Schwarz. Zeichne ein weißes Kästchen in die linke untere Ecke. Zeichne das gezeichnete Feld mit den zuvor erstellten Höhen- und Breitenvariablen. (Wenn du magst kannst du die Farben ändern.) Verwende die Funktion draw_rectangle_filled. Du solltest das Rechteck nicht auf (0, 0) zentrieren, sondern auf eine Koordinate, die die Höhe und Breite des Rechtecks berücksichtigt, zum Beispiel \(\frac{width}{2}\). Wenn du fertig bist, sollte das Fenster deines Programms so aussehen:

Abbildung 16.3: Schritt 3¶
Verwende eine
for
-Schleife, umCOLUMN_COUNT
Kästchen in einer Reihe zu zeichnen. Verwendecolumn
für den Variablennamen in derfor
-Schleife. Die Ausgabe sieht aus wie eine lange Box, bis der Rand zwischen den Boxen hinzugefügt wird. Siehe Abbildung 16.4.

Abbildung 16.4: Schritt 4¶
Passe die Zeichnung des Rechtecks an, um die Variable
MARGIN
hinzuzufügen. Jetzt sollte es Lücken zwischen den Rechtecken geben. Siehe Abbildung 16.5.

Abbildung 16.5: Schritt 5¶
Füge den Rand hinzu, bevor du die Rechtecke zeichnest, zusätzlich zu jedem Rechteck. Dies sollte verhindern, dass das Kästchen direkt neben der Fensterkante angezeigt wird. Siehe Abbildung 16.6. Am Ende erhälst du eine Gleichung wie: \((Rand + Breite)\cdot Spalte + Rand + \frac{Rand}{2}\)

Abbildung 16.6: Schritt 6¶
Füge eine weitere
for
-Schleife, die durch die Zeilen iteriert, hinzu. Nenne die Variable in dieserfor
-Schleiferow
. Jetzt sollten wir ein vollständiges Raster von Kisten haben. Siehe Abbildung 16.7.

Abbildung 16.7: Schritt 7¶
28.2.2. Auffüllen des Rasters¶
Jetzt müssen wir ein zweidimensionales Array erstellen. Wir müssen dies einmal beim Programmstart erstellen. Das geht also in die
__init__
-Methode des Programms. Das Erstellen eines zweidimensionalen Arrays in Python ist leider nicht so einfach wie in einigen anderen Computersprachen. Es gibt einige Bibliotheken, die für Python heruntergeladen werden können, um das zu vereinfachen (zum Beispiel numpy), aber für dieses Beispiel werden sie nicht verwendet. Verwende den folgenden Code, um ein zweidimensionales Array zu erstellen als ein Beispiel:
ROW_COUNT = 10
COLUMN_COUNT = 10
# --- Create grid of numbers
# Create an empty list
self.grid = []
# Loop for each row
for row in range(ROW_COUNT):
# For each row, create a list that will
# represent an entire row
self.grid.append([])
# Loop for each column
for column in range(COLUMN_COUNT):
# Add a the number zero to the current row
self.grid[row].append(0)
Ein viel kürzeres Beispiel findest du weiter unten, aber in diesem Beispiel werden einige merkwürdige Teile von Python verwendet, die ich in diesem Buch nicht näher erläutere:
self.grid = [[0 for x in range(10)] for y in range(10)]
Verwende eines dieser beiden Beispiele und platziere den Code vor deiner Hauptprogrammschleife, um unser Array zu erstellen.
Setze einen beispielhaften Ort im Array auf 1.
Zweidimensionale Arrays werden normalerweise dargestellt, indem zuerst ihre Zeile und dann die Spalte adressiert werden. Dies wird als zeilendominiert bezeichnet. Die meisten Sprachen, mit Ausnahme von Fortran und MATLAB, verwenden zeilendominierte Speicherung. Fortran und MATLAB verwenden Spaltendominanz.
# Set row 1, column 5 to one
self.grid[1][5] = 1
Platziere diesen Code irgendwo vor der Hauptprogrammschleife.
Wähle die Farbe des Rechtecks basierend auf dem Wert einer Variablen namens
color
. Suche dazu zuerst die Codezeile, in der das Rechteck gezeichnet wird. Erstelle davor eine Variable mit dem Namencolor
und setze sie aufwhite
. Ersetze dann die weiße Farbe in der Rechteckdeklaration durch die Variablecolor
.Wähle die Farbe basierend auf dem Wert im Raster. Nachdem du die Farbe auf Weiß gesetzt haben, platziere eine
if
-Anweisung, die den Wert ingrid[row][column]
betrachtet und die Farbe in grün ändert, wenn der Rasterwert gleich 1 ist. Es sollte jetzt ein grünes Quadrat geben. Siehe Abbildung 16.8.

Abbildung 16.8: Schritt 11¶
Gebe „klick“ auf dem Bildschirm aus, wenn der Benutzer mit der Maustaste klickt. Siehe Mausklicks, wenn du vergessen hast, wie das geht.
Gib die Mauskoordinaten aus, wenn der Benutzer mit der Maus klickt.
Konvertiere die Mauskoordinaten in Rasterkoordinaten. Gebe diese stattdessen aus. Denke daran, die Breite und Höhe jeder Rasterposition in Kombination mit dem Rand zu verwenden. Es ist notwendig, den Endwert in eine Ganzzahl umzuwandeln. Dies kann durch Verwendung von
int
oder durch Verwendung des Integer-Divisionsoperators//
anstelle des normalen Divisionsoperators/
erfolgen. Siehe Abbildung 16.9.

Abbildung 16.9: Schritt 14¶
Setze die Rasterposition, auf die geklickt wurde, auf 1. Siehe Abbildung 16.10.

Abbildung 16.10: Schritt 15¶
28.2.3. Resulting Program¶
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 | """
Array Backed Grid
Show how to use a two-dimensional list/array to back the display of a
grid on-screen.
"""
import arcade
# Set how many rows and columns we will have
ROW_COUNT = 10
COLUMN_COUNT = 10
# This sets the WIDTH and HEIGHT of each grid location
WIDTH = 20
HEIGHT = 20
# This sets the margin between each cell
# and on the edges of the screen.
MARGIN = 5
# Do the math to figure out our screen dimensions
SCREEN_WIDTH = (WIDTH + MARGIN) * COLUMN_COUNT + MARGIN
SCREEN_HEIGHT = (HEIGHT + MARGIN) * ROW_COUNT + MARGIN
class MyGame(arcade.Window):
"""
Main application class.
"""
def __init__(self, width, height):
"""
Set up the application.
"""
super().__init__(width, height)
# Create a 2 dimensional array. A two dimensional
# array is simply a list of lists.
self.grid = []
for row in range(ROW_COUNT):
# Add an empty array that will hold each cell
# in this row
self.grid.append([])
for column in range(COLUMN_COUNT):
self.grid[row].append(0) # Append a cell
arcade.set_background_color(arcade.color.BLACK)
def on_draw(self):
"""
Render the screen.
"""
# This command has to happen before we start drawing
arcade.start_render()
# Draw the grid
for row in range(ROW_COUNT):
for column in range(COLUMN_COUNT):
# Figure out what color to draw the box
if self.grid[row][column] == 1:
color = arcade.color.GREEN
else:
color = arcade.color.WHITE
# Do the math to figure out where the box is
x = (MARGIN + WIDTH) * column + MARGIN + WIDTH // 2
y = (MARGIN + HEIGHT) * row + MARGIN + HEIGHT // 2
# Draw the box
arcade.draw_rectangle_filled(x, y, WIDTH, HEIGHT, color)
def on_mouse_press(self, x, y, button, modifiers):
"""
Called when the user presses a mouse button.
"""
# Change the x/y screen coordinates to grid coordinates
column = x // (WIDTH + MARGIN)
row = y // (HEIGHT + MARGIN)
print(f"Click coordinates: ({x}, {y}). Grid coordinates: ({row}, {column})")
# Make sure we are on-grid. It is possible to click in the upper right
# corner in the margin and go to a grid location that doesn't exist
if row < ROW_COUNT and column < COLUMN_COUNT:
# Flip the location between 1 and 0.
if self.grid[row][column] == 0:
self.grid[row][column] = 1
else:
self.grid[row][column] = 0
def main():
window = MyGame(SCREEN_WIDTH, SCREEN_HEIGHT)
arcade.run()
if __name__ == "__main__":
main()
|