Pygame-Tutorial

Aus Spieleprogrammierer-Wiki
(Unterschied zwischen Versionen)
Wechseln zu: Navigation, Suche
[gesichtete Version][gesichtete Version]
K
(Zusammenbasteln)
 
(29 dazwischenliegende Versionen von 3 Benutzern werden nicht angezeigt)
Zeile 7: Zeile 7:
  
 
=== Wo bekomme ich Pygame? ===
 
=== Wo bekomme ich Pygame? ===
Die offizielle Pygame-Webseite findet sich unter http://www.pygame.org/. Dort gibt es auch immer die jeweils neuste Version, die Dokumentation, Tutorials und vieles mehr. Unter http://www.python.org/ gibt es die Python-Interpreter, und alle, die Python noch nicht kennen, finden dort alles, um es zu lernen.
+
Die offizielle Pygame-Webseite findet sich unter http://www.pygame.org/. Dort gibt es auch immer die jeweils neuste Version, die Dokumentation, Tutorials und vieles mehr. Unter http://www.python.org/ gibt es die Python-Interpreter, und alle, die Python noch nicht kennen, finden dort alles, um es zu lernen. In diesem Tutorial wird Pygame Version 1.9.2 und Python Version 2.7.9 verwendet.
  
 
== Pygame einrichten ==
 
== Pygame einrichten ==
Zeile 18: Zeile 18:
  
 
=== Testen ===
 
=== Testen ===
Ein einfaches Skript zum Testen der Installation:
+
Zuerst erstellen wir ein Python-Projekt: ''File > New > Other > Pydev > Pydev Project''. Jetzt müssen wir einen Namen für das Projekt eingeben, ''Create 'src' folder...'' auswählen und auf ''Finish'' klicken. Danach fragt Eclipse möglicherweise noch, ob wir die ''Pydev Perspective'' öffnen möchten. Dies beantworten wir mit ''Ja'', und schon haben wir ein Python-Projekt angelegt.
 +
 
 +
Fügen wir ein wenig Code hinzu. Dazu machen wir einen Rechtsklick auf den ''src''-Ordner in unserem Projekt links in der Übersicht und klicken auf ''New > Pydev Package''. Dann geben wir einen Namen ein und klicken auf ''Finish''. Pydev hat jetzt automatisch eine <tt>__init__.py</tt> Datei erstellt. Wer möchte, kann diese umbenennen, zum Beispiel in <tt>Teil_1.py</tt>. Die Datei öffnen wir jetzt durch einen Doppelklick und schreiben dieses einfache Skript zum Testen der Installation hinein:
  
 
<sourcecode lang=python line>
 
<sourcecode lang=python line>
Zeile 124: Zeile 126:
 
</sourcecode>
 
</sourcecode>
  
Zuerst erstellen wir ein <tt>time.Cock</tt>-Objekt, welches sich um die Framerate-Begrenzung kümmern soll. Um die Schleife bequem wieder verlassen zu können, verwenden wir die <tt>running</tt>-Variable. Solange sie 1 ist, bleiben wir in der Schleife. In der Schleife lassen wir das <tt>time.Clock</tt>-Objekt erstmal berechnen, wie lange wir warten müssen, um maximal 30 Frames pro Sekunde zu erhalten. Danach überschreiben wir den gesamten Bildschirm mit der Farbe Schwarz.
+
Zuerst erstellen wir ein <tt>time.Clock</tt>-Objekt, welches sich um die Framerate-Begrenzung kümmern soll. Um die Schleife bequem wieder verlassen zu können, verwenden wir die <tt>running</tt>-Variable. Solange sie 1 ist, bleiben wir in der Schleife. In der Schleife lassen wir das <tt>time.Clock</tt>-Objekt erstmal berechnen, wie lange wir warten müssen, um maximal 30 Frames pro Sekunde zu erhalten. Danach überschreiben wir den gesamten Bildschirm mit der Farbe Schwarz.
  
 
<sourcecode lang=python line start=35>
 
<sourcecode lang=python line start=35>
Zeile 206: Zeile 208:
  
 
<sourcecode lang=python>
 
<sourcecode lang=python>
if image.get_alpha() == None:
+
if image.get_alpha() is None:
 
     image = image.convert()
 
     image = image.convert()
 
else:
 
else:
Zeile 226: Zeile 228:
 
<sourcecode lang=python line start=5>
 
<sourcecode lang=python line start=5>
 
# Hilfsfunktion, um ein Bild zu laden:
 
# Hilfsfunktion, um ein Bild zu laden:
def loadImage(filename, colorkey = None):
+
def loadImage(filename, colorkey=None):
 
     # Pygame das Bild laden lassen.
 
     # Pygame das Bild laden lassen.
 
     image = pygame.image.load(filename)
 
     image = pygame.image.load(filename)
Zeile 232: Zeile 234:
 
     # Das Pixelformat der Surface an den Bildschirm (genauer: die screen-Surface) anpassen.
 
     # Das Pixelformat der Surface an den Bildschirm (genauer: die screen-Surface) anpassen.
 
     # Dabei die passende Funktion verwenden, je nach dem, ob wir ein Bild mit Alpha-Kanal haben oder nicht.
 
     # Dabei die passende Funktion verwenden, je nach dem, ob wir ein Bild mit Alpha-Kanal haben oder nicht.
     if image.get_alpha() == None:
+
     if image.get_alpha() is None:
 
         image = image.convert()
 
         image = image.convert()
 
     else:
 
     else:
Zeile 242: Zeile 244:
 
         if colorkey is -1:
 
         if colorkey is -1:
 
             colorkey = image.get_at((0,0))
 
             colorkey = image.get_at((0,0))
 
 
         image.set_colorkey(colorkey, pygame.RLEACCEL)
 
         image.set_colorkey(colorkey, pygame.RLEACCEL)
  
Zeile 292: Zeile 293:
 
     # Im Konstruktor speichern wir den Namen
 
     # Im Konstruktor speichern wir den Namen
 
     # und erstellen das Rect (den Bereich) dieses Typs auf der Tileset-Grafik.
 
     # und erstellen das Rect (den Bereich) dieses Typs auf der Tileset-Grafik.
     def __init__(self, name, startX, startY, width, height):
+
     def __init__(self, name, start_x, start_y, width, height):
         self.__name = name
+
         self.name = name
         self.__rect = pygame.rect.Rect(startX, startY, width, height)
+
         self.rect = pygame.rect.Rect(start_x, start_y, width, height)
 
+
    def getName(self):
+
        return self.__name
+
 
+
    def getRect(self):
+
        return self.__rect
+
 
</sourcecode>
 
</sourcecode>
  
Wir speichern als erstes den Namen bzw. die ID des Tile-Typs in der privaten Variable <tt>__name</tt>. Über die Funktion <tt>getName</tt> können wir ihn wieder auslesen.
+
Wir speichern als erstes den Namen bzw. die ID des Tile-Typs in der Variable <tt>name</tt>.
 
In einem <tt>Rect</tt> speichern wir gleichzeitig die Position und die Größe dieses Types in der Tileset-Grafik.
 
In einem <tt>Rect</tt> speichern wir gleichzeitig die Position und die Größe dieses Types in der Tileset-Grafik.
 
Wir gehen gleich zur nächsten Klasse weiter, der <tt>Tileset</tt>-Klasse, in der deutlich mehr passiert.
 
Wir gehen gleich zur nächsten Klasse weiter, der <tt>Tileset</tt>-Klasse, in der deutlich mehr passiert.
Zeile 311: Zeile 306:
 
Zuletzt wird noch ein Dictionary für die Tile-Typen angelegt, das aber erst einmal leer bleibt.
 
Zuletzt wird noch ein Dictionary für die Tile-Typen angelegt, das aber erst einmal leer bleibt.
  
<sourcecode lang=python line start=23>
+
<sourcecode lang=python line start=7>
 +
# Verwaltet die Tileset Grafik und eine Liste mit Tile-Typen.
 
class Tileset(object):
 
class Tileset(object):
     def __init__(self, image, colorkey, tileWidth, tileHeight):
+
    # Im Konstruktor laden wir die Grafik
         self.__image = loadImage(image, colorkey)
+
    # und erstellen ein leeres Dictionary für die Tile-Typen.
         self.__tileWidth = tileWidth
+
     def __init__(self, image, colorkey, tile_width, tile_height):
         self.__tileHeight = tileHeight
+
         self.image = Utils.load_image(image, colorkey)
 
+
         self.tile_width = tile_width
         self.__tileTypes = dict()
+
         self.tile_height = tile_height
 +
         self.tile_types = dict()
 
</sourcecode>
 
</sourcecode>
  
Um das Bild anzeigen zu können, bzw. Ausschnitte daraus, erstellen wir eine Funktion <tt>getImage</tt>, die das Bild zurückliefert. Außerdem ermöglichen wir es, das Bild gegen eine neues auszutauschen. Wenn während des Spiels so ein Tausch des Tileset-Bildes öfter vorkommen soll, ist es aber ratsamer, alle Bilder zum Beispiel in einer Liste zu speichern und nicht jedes Mal neu zu laden.
+
Die <tt>loadImage</tt>-Funktion haben wir in das Modul <tt>Utils</tt> ausgelagert. Also das <tt>import Utils</tt> am Anfang des Moduls nicht vergessen.
 
+
<sourcecode lang=python line start=31>
+
    def getImage(self):
+
        return self.__image
+
 
+
    def setImage(self, image, colorkey):
+
        self.__image = loadImage(image, colorkey)
+
</sourcecode>
+
 
+
Ebenfalls sehr einfach sind die drei Funktionen, um die Größe der Tiles zu bekommen:
+
 
+
<sourcecode lang=python line start=37>
+
    def getTileWidth(self):
+
        return self.__tileWidth
+
 
+
    def getTileHeight(self):
+
        return self.__tileHeight
+
 
+
    def getTileSize(self):
+
        return (self.__tileWidth, self.__tileHeight)
+
</sourcecode>
+
  
 
Kommen wir zur wichtigsten Funktion: Das Hinzufügen eines Tile-Typen. Das ist im Grunde auch nicht sonderlich kompliziert. Wir fügen einfach eine neue Instanz der <tt>TileType</tt>-Klasse in unser Dictionary ein, mit passenden Parametern:
 
Kommen wir zur wichtigsten Funktion: Das Hinzufügen eines Tile-Typen. Das ist im Grunde auch nicht sonderlich kompliziert. Wir fügen einfach eine neue Instanz der <tt>TileType</tt>-Klasse in unser Dictionary ein, mit passenden Parametern:
  
<sourcecode lang=python line start=46>
+
<sourcecode lang=python line start=17>
     def addTile(self, name, startX, startY):
+
    # Neuen Tile-Typ hinzufügen.
         self.__tileTypes[name] = TileType.TileType(name, startX, startY, self.__tileWidth, self.__tileHeight)
+
     def add_tile(self, name, start_x, start_y):
 +
         self.tile_types[name] = TileType.TileType(name, start_x, start_y, self.tile_width, self.tile_height)
 
</sourcecode>
 
</sourcecode>
  
 
Auch das Abfragen eines Types ist leicht, einfach über den Key den passenden Wert zurückliefern:
 
Auch das Abfragen eines Types ist leicht, einfach über den Key den passenden Wert zurückliefern:
  
<sourcecode lang=python line start=49>
+
<sourcecode lang=python line start=21>
     def getTile(self, name):
+
    # Versuchen, einen Tile-Type über seinen Namen in der Liste zu finden.
 +
    # Falls der Name nicht existiert, geben wir None zurück.
 +
     def get_tile(self, name):
 
         try:
 
         try:
             return self.__tileTypes[name]
+
             return self.tile_types[name]
 
         except KeyError:
 
         except KeyError:
 
             return None
 
             return None
Zeile 369: Zeile 348:
  
 
<sourcecode lang=python line start=7>
 
<sourcecode lang=python line start=7>
 +
# Die Tilemap Klasse verwaltet die Tile-Daten, die das Aussehen der Karte beschreiben.
 
class Tilemap(object):
 
class Tilemap(object):
     def __init__(self):      
+
     def __init__(self):
         self.__tileset = Tileset.Tileset("tileset.png", (255, 0, 255), 32, 32)
+
        # Wir erstellen ein neues Tileset.
         self.__tileset.addTile("grass", 0, 0)
+
        # Hier im Tutorial fügen wir manuell vier Tile-Typen hinzu.
         self.__tileset.addTile("mud", 32, 0)
+
         self.tileset = Tileset.Tileset("tileset.png", (255, 0, 255), 32, 32)
         self.__tileset.addTile("water", 64, 0)
+
         self.tileset.add_tile("grass", 0, 0)
         self.__tileset.addTile("block", 0, 32)
+
         self.tileset.add_tile("mud", 32, 0)
 +
         self.tileset.add_tile("water", 64, 0)
 +
         self.tileset.add_tile("block", 0, 32)
 
</sourcecode>
 
</sourcecode>
  
Zeile 381: Zeile 363:
 
Was passiert: Wir erstellen eine neue Instanz der <tt>Tileset</tt>-Klasse, übergeben ein passendes Bild, den Colorkey und die Größe eines einzelne Tiles (32x32 Pixel). Danach fügen wir per Hand vier Tile-Typen hinzu.
 
Was passiert: Wir erstellen eine neue Instanz der <tt>Tileset</tt>-Klasse, übergeben ein passendes Bild, den Colorkey und die Größe eines einzelne Tiles (32x32 Pixel). Danach fügen wir per Hand vier Tile-Typen hinzu.
  
<sourcecode lang=python line start=15>
+
<sourcecode lang=python line start=18>
         self.__cameraX = 0
+
        # Festlegen der Startposition der Kamera. Hier (0, 0).
         self.__cameraY = 0
+
         self.camera_x = 0
           
+
         self.camera_y = 0
         self.__width = 10
+
       
         self.__height = 10
+
        # Die Größe der Maps in Tiles.
 +
         self.width = 30
 +
         self.height = 25
  
         self.__tiles = list()
+
        # Erstellen einer leeren Liste für die Tile Daten.
 +
         self.tiles = list()
 
</sourcecode>
 
</sourcecode>
  
 
Wir legen Standardwerte für die Position der Kamera und die Größe der Karte fest. Die Größe der Karten wird in Tiles angegeben, nicht in Pixeln. Auch hier gilt wieder: Die Größe kann flexibler gestaltet werden, etwa durch einen Parameter in dem Konstruktor oder eine Datei. Zudem erstellen wir eine noch leere Liste, in der wir später die Tiles speichern wollen.
 
Wir legen Standardwerte für die Position der Kamera und die Größe der Karte fest. Die Größe der Karten wird in Tiles angegeben, nicht in Pixeln. Auch hier gilt wieder: Die Größe kann flexibler gestaltet werden, etwa durch einen Parameter in dem Konstruktor oder eine Datei. Zudem erstellen wir eine noch leere Liste, in der wir später die Tiles speichern wollen.
  
<sourcecode lang=python line start=23>
+
<sourcecode lang=python line start=29>
         for i in range(0, self.__width):
+
        # Manuelles Befüllen der Tile-Liste:
             self.__tiles.append(list())
+
        # Jedes Feld bekommt ein zufälliges Tile zugewiesen.
             for j in range(0, self.__height):
+
         for i in range(0, self.height):
 +
             self.tiles.append(list())
 +
             for j in range(0, self.width):
 
                 x = random.randint(0, 4)
 
                 x = random.randint(0, 4)
 
                 if x == 0:
 
                 if x == 0:
                     self.__tiles[i].append("grass")
+
                     self.tiles[i].append("grass")
 
                 elif x == 1:
 
                 elif x == 1:
                     self.__tiles[i].append("water")
+
                     self.tiles[i].append("water")
 
                 elif x == 2:
 
                 elif x == 2:
                     self.__tiles[i].append("mud")
+
                     self.tiles[i].append("mud")
 
                 else:
 
                 else:
                     self.__tiles[i].append("block")
+
                     self.tiles[i].append("block")
 
</sourcecode>
 
</sourcecode>
  
Mit diesem Code endet der Konstruktor. Wir befüllen hier die Liste der Tiles. Dazu verwenden wir zwei <tt>for</tt>-Schleifen, die die Breite bzw. Höhe der Karte durchlaufen. Für jedes <tt>i</tt> (eine Zeile) erstellen wir eine neue Liste, in der wir die Spalten mit einem Zufallswert füllen (hier gibt es eine Auswahl zwischen den vier hinzugefügten Tiles). Standardmäßig sollte hier jedes Feld auf <tt>""</tt> oder <tt>None</tt> gesetzt werden, oder man liest gleich alle Positionsdaten aus einer Datei.
+
Mit diesem Code endet der Konstruktor. Wir befüllen hier die Liste der Tiles. Dazu verwenden wir zwei <tt>for</tt>-Schleifen, die die Höhe bzw. Breite der Karte durchlaufen. Für jedes <tt>i</tt> (eine Zeile) erstellen wir eine neue Liste, die wir mit Zufallswerten füllen (hier gibt es eine Auswahl zwischen den vier hinzugefügten Tiles). Standardmäßig sollte hier jedes Feld auf <tt>""</tt> oder <tt>None</tt> gesetzt werden, oder man liest gleich alle Positionsdaten aus einer Datei.
 
Schauen wir uns die komplizierteste, aber auch wichtigste Funktion an: Die Funktion <tt>render</tt>, die die Karte auf den Bildschirm (oder eine beliebige andere Surface rendert):
 
Schauen wir uns die komplizierteste, aber auch wichtigste Funktion an: Die Funktion <tt>render</tt>, die die Karte auf den Bildschirm (oder eine beliebige andere Surface rendert):
  
<sourcecode lang=python line start=37>
+
<sourcecode lang=python line start=45>
 +
    # Hier rendern wir den sichtbaren Teil der Karte.
 
     def render(self, screen):
 
     def render(self, screen):
         for y in range(0, int(screen.get_height() / self.__tileset.getTileHeight()) + 1):
+
        # Zeilenweise durch die Tiles durchgehen.
             ty = y + self.__cameraY
+
         for y in range(0, int(screen.get_height() / self.tileset.tile_height) + 1):
             if ty >= self.__height or ty < 0:
+
            # Die Kamera Position mit einbeziehen.
 +
             ty = y + self.camera_y
 +
             if ty >= self.height or ty < 0:
 
                 continue
 
                 continue
             col = self.__tiles[ty]
+
             # Die aktuelle Zeile zum einfacheren Zugriff speichern.
             for x in range(0, int(screen.get_width() / self.__tileset.getTileWidth()) + 1):
+
            line = self.tiles[ty]
                 tx = x + self.__cameraX
+
            # Und jetzt spaltenweise die Tiles rendern.
                 if tx >= self.__width or tx < 0:
+
             for x in range(0, int(screen.get_width() / self.tileset.tile_width) + 1):
 +
                # Auch hier müssen wir die Kamera beachten.
 +
                 tx = x + self.camera_x
 +
                 if tx >= self.width or tx < 0:
 
                     continue
 
                     continue
                 tilename = col[tx]
+
                # Wir versuchen, die Daten des Tiles zu bekommen.
                 tile = self.__tileset.getTile(tilename)
+
                 tilename = line[tx]
 +
                 tile = self.tileset.get_tile(tilename)
 +
                # Falls das nicht fehlschlägt können wir das Tile auf die screen-Surface blitten.
 
                 if tile is not None:
 
                 if tile is not None:
                     screen.blit(self.__tileset.getImage(), (x * self.__tileset.getTileWidth(), y * self.__tileset.getTileHeight()), tile.getRect())
+
                     screen.blit(self.tileset.image, (x * self.tileset.tile_width, y * self.tileset.tile_height), tile.rect)
 
</sourcecode>
 
</sourcecode>
  
 
Wir werden die Funktion Zeile für Zeile durchgehen:
 
Wir werden die Funktion Zeile für Zeile durchgehen:
  
# Wir wollen nur so viele Tiles rendern, wie auf den Bildschirm passen. Um das herauszufinden, dividieren wir einfach die Höhe des Bildschirms durch die Höhe der Tiles. Um schwarze Rahmen zu vermeiden, addieren wir noch 1 hinzu.
+
* Zeile 48: Wir wollen nur so viele Tiles rendern, wie auf den Bildschirm passen. Um das herauszufinden, dividieren wir einfach die Höhe des Bildschirms durch die Höhe der Tiles. Um schwarze Rahmen zu vermeiden, addieren wir noch 1 hinzu.
# Die y-Position auf dem Bildschirm ist nicht gleich der Position in unserer Tile-Liste, diese Position speichern wir in der Variable <tt>ty</tt>. Um den korrekten Wert zu erhalten, müssen wir nur die Kameraposition hinzuaddieren.
+
* Zeile 50: Die y-Position auf dem Bildschirm ist nicht gleich der Position in unserer Tile-Liste, diese Position speichern wir in der Variable <tt>ty</tt>. Um den korrekten Wert zu erhalten, müssen wir nur die Kameraposition hinzuaddieren.
# Wir testen, ob wir uns überhaupt noch innerhalb der Map-Grenze befinden. Wenn nicht, überspringen wir das aktuelle Tile.
+
* Zeile 51: Wir testen, ob wir uns überhaupt noch innerhalb der Map-Grenze befinden. Wenn nicht, überspringen wir das aktuelle Tile.
# Die Tiles werden spaltenweise gerendert, hier speichern wir die aktuelle Spalte.
+
* Zeile 54: Die Tiles werden gleich spaltenweise gerendert, hier speichern wir die aktuelle Zeile.
# In der zweiten <tt>for</tt>-Schleife durchlaufen wie die Breite des Bildschrims.
+
* Zeile 56: In der zweiten <tt>for</tt>-Schleife durchlaufen wie die Breite des Bildschrims.
# Auch die x-Position wird berechnet, genau wie die y-Position.
+
* Zeile 58: Auch die x-Position wird berechnet, genau wie die y-Position.
# Natürlich muss auch hier getestet werden, ob wir die Grenzen der Map einhalten.
+
* Zeile 59: Natürlich muss auch hier getestet werden, ob wir die Grenzen der Map einhalten.
# Als Nächstes holen wir uns den Namen des Tiles, das wir rendern wollen, aus der aktuellen Spalte.
+
* Zeile 62: Als Nächstes holen wir uns den Namen des Tiles, das wir rendern wollen, aus der aktuellen Zeile.
# Über das Tileset erhalten wir das Tile, oder <tt>None</tt>, wenn keines existiert.
+
* Zeile 63: Über das Tileset erhalten wir das Tile, oder <tt>None</tt>, wenn keines existiert.
# Und wenn wir ein Tile haben, wird dieses auf den Bildschrim gerendert.
+
* Zeile 65: Und wenn wir ein Tile haben, wird dieses auf den Bildschrim gerendert.
# In der Tilemap ist das Bild mit allen Tiles gespeichert, das ist unsere Quelle für die <tt>blit</tt>-Funktion. Die Positionen multiplizieren wir noch mit der Größe der Tiles, um keine Überschneidung mit dem vorherigen Tile zu bekommen. Zuletzt der wichtigste Parameter: Der Ausschnitt, der von dem Bild verwendet werden soll, ist in der Tile-Klasse gespeichert.
+
* Zeile 66: In der Tilemap ist das Bild mit allen Tiles gespeichert, das ist unsere Quelle für die <tt>blit</tt>-Funktion. Die Positionen multiplizieren wir noch mit der Größe der Tiles, um keine Überschneidung mit dem vorherigen Tile zu bekommen. Zuletzt der wichtigste Parameter: Der Ausschnitt, der von dem Bild verwendet werden soll, ist in der Tile-Klasse gespeichert.
  
 
Damit sind wir fast fertig.
 
Damit sind wir fast fertig.
 
Die Veränderung der Kameraposition ist im Gegensatz zur letzten Funktion sehr einfach:
 
Die Veränderung der Kameraposition ist im Gegensatz zur letzten Funktion sehr einfach:
  
<sourcecode lang=python line start=53>
+
<sourcecode lang=python line start=69>
     def handleInput(self, key):
+
    # Tastendrücke verarbeiten:
 +
     def handle_input(self, key):
 +
        # Pfeiltaste links oder rechts erhöht bzw. verringert die x-Position der Kamera.
 
         if key == pygame.K_LEFT:
 
         if key == pygame.K_LEFT:
             self.__cameraX += 1
+
             self.camera_x += 1
 
         if key == pygame.K_RIGHT:
 
         if key == pygame.K_RIGHT:
             self.__cameraX -= 1
+
             self.camera_x -= 1
 
+
       
 +
        # Und das gleiche nochmal für die y-Position.
 
         if key == pygame.K_UP:
 
         if key == pygame.K_UP:
             self.__cameraY += 1
+
             self.camera_y += 1
 
         if key == pygame.K_DOWN:
 
         if key == pygame.K_DOWN:
             self.__cameraY -= 1
+
             self.camera_y -= 1
 
</sourcecode>
 
</sourcecode>
  
Zeile 462: Zeile 460:
 
Zum Schluss bauen wir die Tilemap noch in das Skript aus dem letzten Tutorial ein. Zuerst erstellen wir eine Tilemap:
 
Zum Schluss bauen wir die Tilemap noch in das Skript aus dem letzten Tutorial ein. Zuerst erstellen wir eine Tilemap:
  
<sourcecode lang=python line start=19>
+
<sourcecode lang=python line start=28>
 
     map = Tilemap.Tilemap()
 
     map = Tilemap.Tilemap()
 
</sourcecode>
 
</sourcecode>
Zeile 468: Zeile 466:
 
In der Event-Schleife geben wir die gedrückten Tasten an die Map weiter:
 
In der Event-Schleife geben wir die gedrückten Tasten an die Map weiter:
  
<sourcecode lang=python line start=35>
+
<sourcecode lang=python line start=53>
                 map.handleInput(event.key)
+
                 map.handle_input(event.key)
 
</sourcecode>
 
</sourcecode>
  
 
Und direkt vor dem Anzeigen des Bildpuffers rendern wir die Map:
 
Und direkt vor dem Anzeigen des Bildpuffers rendern wir die Map:
  
<sourcecode lang=python line start=37>
+
<sourcecode lang=python line start=56>
 
         map.render(screen)
 
         map.render(screen)
 
</sourcecode>
 
</sourcecode>
Zeile 481: Zeile 479:
 
Und hier nochmal der gesamte Code im Überblick:
 
Und hier nochmal der gesamte Code im Überblick:
  
<tt>TileType.py</tt>:
+
<tt>Utils.py</tt>:
 
<sourcecode lang=python line>
 
<sourcecode lang=python line>
 
# -*- coding: UTF-8 -*-
 
# -*- coding: UTF-8 -*-
Zeile 487: Zeile 485:
 
import pygame
 
import pygame
  
class TileType(object):
+
# Hilfsfunktion, um ein Bild zu laden:
    def __init__(self, name, startX, startY, width, height):
+
def load_image(filename, colorkey=None):
        self.__name = name
+
    # Pygame das Bild laden lassen.
        self.__rect = pygame.rect.Rect(startX, startY, width, height)
+
 
+
    def getName(self):
+
        return self.__name
+
 
+
    def getRect(self):
+
        return self.__rect
+
</sourcecode>
+
 
+
<tt>Tileset.py</tt>:
+
<sourcecode lang=python line>
+
# -*- coding: UTF-8 -*-
+
 
+
import pygame
+
import TileType
+
 
+
def loadImage(filename, colorkey = None):
+
 
     image = pygame.image.load(filename)
 
     image = pygame.image.load(filename)
  
     if image.get_alpha() == None:
+
    # Das Pixelformat der Surface an den Bildschirm (genauer: die screen-Surface) anpassen.
 +
    # Dabei die passende Funktion verwenden, je nach dem, ob wir ein Bild mit Alpha-Kanal haben oder nicht.
 +
     if image.get_alpha() is None:
 
         image = image.convert()
 
         image = image.convert()
 
     else:
 
     else:
 
         image = image.convert_alpha()
 
         image = image.convert_alpha()
  
 +
    # Colorkey des Bildes setzen, falls nicht None.
 +
    # Bei -1 den Pixel im Bild an Position (0, 0) als Colorkey verwenden.
 
     if colorkey is not None:
 
     if colorkey is not None:
 
         if colorkey is -1:
 
         if colorkey is -1:
 
             colorkey = image.get_at((0,0))
 
             colorkey = image.get_at((0,0))
 
 
         image.set_colorkey(colorkey, pygame.RLEACCEL)
 
         image.set_colorkey(colorkey, pygame.RLEACCEL)
  
 
     return image
 
     return image
 +
</sourcecode>
  
 +
<tt>TileType.py</tt>:
 +
<sourcecode lang=python line>
 +
# -*- coding: UTF-8 -*-
  
class Tileset(object):
+
import pygame
    def __init__(self, image, colorkey, tileWidth, tileHeight):
+
        self.__image = loadImage(image, colorkey)
+
        self.__tileWidth = tileWidth
+
        self.__tileHeight = tileHeight
+
  
        self.__tileTypes = dict()
+
# Speichert die Daten eines Tile-Typs:
       
+
class TileType(object):
     def getImage(self):
+
    # Im Konstruktor speichern wir den Namen
         return self.__image
+
    # und erstellen das Rect (den Bereich) dieses Typs auf der Tileset-Grafik.
 +
     def __init__(self, name, start_x, start_y, width, height):
 +
         self.name = name
 +
        self.rect = pygame.rect.Rect(start_x, start_y, width, height)
 +
</sourcecode>
  
    def setImage(self, image, colorkey):
+
<tt>Tileset.py</tt>:
        self.__image = loadImage(image, colorkey)
+
<sourcecode lang=python line>
       
+
# -*- coding: UTF-8 -*-
    def getTileWidth(self):
+
        return self.__tileWidth
+
  
    def getTileHeight(self):
+
import pygame
        return self.__tileHeight
+
import Utils
 +
import TileType
  
     def getTileSize(self):
+
# Verwaltet die Tileset Grafik und eine Liste mit Tile-Typen.
         return (self.__tileWidth, self.__tileHeight)
+
class Tileset(object):
 +
    # Im Konstruktor laden wir die Grafik
 +
    # und erstellen ein leeres Dictionary für die Tile-Typen.
 +
     def __init__(self, image, colorkey, tile_width, tile_height):
 +
         self.image = Utils.load_image(image, colorkey)
 +
        self.tile_width = tile_width
 +
        self.tile_height = tile_height
 +
        self.tile_types = dict()
 
      
 
      
     def addTile(self, name, startX, startY):
+
    # Neuen Tile-Typ hinzufügen.
         self.__tileTypes[name] = TileType.TileType(name, startX, startY, self.__tileWidth, self.__tileHeight)
+
     def add_tile(self, name, start_x, start_y):
 +
         self.tile_types[name] = TileType.TileType(name, start_x, start_y, self.tile_width, self.tile_height)
 
          
 
          
     def getTile(self, name):
+
    # Versuchen, einen Tile-Type über seinen Name in der Liste zu finden.
 +
    # Falls der Name nicht existiert, geben wir None zurück.
 +
     def get_tile(self, name):
 
         try:
 
         try:
             return self.__tileTypes[name]
+
             return self.tile_types[name]
 
         except KeyError:
 
         except KeyError:
 
             return None
 
             return None
Zeile 560: Zeile 557:
 
# -*- coding: UTF-8 -*-
 
# -*- coding: UTF-8 -*-
  
import pygame
 
 
import random
 
import random
 +
import pygame
 
import Tileset
 
import Tileset
  
 +
# Die Tilemap Klasse verwaltet die Tile-Daten, die das Aussehen der Karte beschreiben.
 
class Tilemap(object):
 
class Tilemap(object):
     def __init__(self):      
+
     def __init__(self):
         self.__tileset = Tileset.Tileset("tileset.png", (255, 0, 255), 32, 32)
+
        # Wir erstellen ein neues Tileset.
         self.__tileset.addTile("grass", 0, 0)
+
        # Hier im Tutorial fügen wir manuell vier Tile-Typen hinzu.
         self.__tileset.addTile("mud", 32, 0)
+
         self.tileset = Tileset.Tileset("tileset.png", (255, 0, 255), 32, 32)
         self.__tileset.addTile("water", 64, 0)
+
         self.tileset.add_tile("grass", 0, 0)
         self.__tileset.addTile("block", 0, 32)
+
         self.tileset.add_tile("mud", 32, 0)
 +
         self.tileset.add_tile("water", 64, 0)
 +
         self.tileset.add_tile("block", 0, 32)
 
          
 
          
         self.__cameraX = 0
+
        # Festlegen der Startposition der Kamera. Hier (0, 0).
         self.__cameraY = 0
+
         self.camera_x = 0
           
+
         self.camera_y = 0
         self.__width = 10
+
       
         self.__height = 10
+
        # Die Größe der Maps in Tiles.
 +
         self.width = 30
 +
         self.height = 25
  
         self.__tiles = list()
+
        # Erstellen einer leeren Liste für die Tile Daten.
 +
         self.tiles = list()
 
          
 
          
         for i in range(0, self.__width):
+
        # Manuelles Befüllen der Tile-Liste:
             self.__tiles.append(list())
+
        # Jedes Feld bekommt ein zufälliges Tile zugewiesen.
             for j in range(0, self.__height):
+
         for i in range(0, self.height):
 +
             self.tiles.append(list())
 +
             for j in range(0, self.width):
 
                 x = random.randint(0, 4)
 
                 x = random.randint(0, 4)
 
                 if x == 0:
 
                 if x == 0:
                     self.__tiles[i].append("grass")
+
                     self.tiles[i].append("grass")
 
                 elif x == 1:
 
                 elif x == 1:
                     self.__tiles[i].append("water")
+
                     self.tiles[i].append("water")
 
                 elif x == 2:
 
                 elif x == 2:
                     self.__tiles[i].append("mud")
+
                     self.tiles[i].append("mud")
 
                 else:
 
                 else:
                     self.__tiles[i].append("block")  
+
                     self.tiles[i].append("block")
                   
+
 
                      
 
                      
 +
   
 +
    # Hier rendern wir den sichtbaren Teil der Karte.
 
     def render(self, screen):
 
     def render(self, screen):
         for y in range(0, int(screen.get_height() / self.__tileset.getTileHeight()) + 1):
+
        # Zeilenweise durch die Tiles durchgehen.
             ty = y + self.__cameraY
+
         for y in range(0, int(screen.get_height() / self.tileset.tile_height) + 1):
             if ty >= self.__height or ty < 0:
+
            # Die Kamera Position mit einbeziehen.
 +
             ty = y + self.camera_y
 +
             if ty >= self.height or ty < 0:
 
                 continue
 
                 continue
             col = self.__tiles[ty]
+
             # Die aktuelle Zeile zum einfacheren Zugriff speichern.
             for x in range(0, int(screen.get_width() / self.__tileset.getTileWidth()) + 1):
+
            line = self.tiles[ty]
                 tx = x + self.__cameraX
+
            # Und jetzt spaltenweise die Tiles rendern.
                 if tx >= self.__width or tx < 0:
+
             for x in range(0, int(screen.get_width() / self.tileset.tile_width) + 1):
 +
                # Auch hier müssen wir die Kamera beachten.
 +
                 tx = x + self.camera_x
 +
                 if tx >= self.width or tx < 0:
 
                     continue
 
                     continue
                 tilename = col[tx]
+
                # Wir versuchen, die Daten des Tiles zu bekommen.
                 tile = self.__tileset.getTile(tilename)
+
                 tilename = line[tx]
 +
                 tile = self.tileset.get_tile(tilename)
 +
                # Falls das nicht fehlschlägt können wir das Tile auf die screen-Surface blitten.
 
                 if tile is not None:
 
                 if tile is not None:
                     screen.blit(self.__tileset.getImage(), (x * self.__tileset.getTileWidth(), y * self.__tileset.getTileHeight()), tile.getRect())
+
                     screen.blit(self.tileset.image, (x * self.tileset.tile_width, y * self.tileset.tile_height), tile.rect)
 
                      
 
                      
 
      
 
      
     def handleInput(self, key):
+
    # Tastendrücke verarbeiten:
 +
     def handle_input(self, key):
 +
        # Pfeiltaste links oder rechts erhöht bzw. verringert die x-Position der Kamera.
 
         if key == pygame.K_LEFT:
 
         if key == pygame.K_LEFT:
             self.__cameraX += 1
+
             self.camera_x += 1
 
         if key == pygame.K_RIGHT:
 
         if key == pygame.K_RIGHT:
             self.__cameraX -= 1
+
             self.camera_x -= 1
 
+
       
 +
        # Und das gleiche nochmal für die y-Position.
 
         if key == pygame.K_UP:
 
         if key == pygame.K_UP:
             self.__cameraY += 1
+
             self.camera_y += 1
 
         if key == pygame.K_DOWN:
 
         if key == pygame.K_DOWN:
             self.__cameraY -= 1
+
             self.camera_y -= 1
 
</sourcecode>
 
</sourcecode>
  
<tt>Tutorial.py</tt>:
+
<tt>Teil_2.py</tt>:
 
<sourcecode lang=python line>
 
<sourcecode lang=python line>
 
# -*- coding: UTF-8 -*-
 
# -*- coding: UTF-8 -*-
  
 +
# Pygame Modul importieren.
 
import pygame
 
import pygame
  
if not pygame.font: print('Fehler: pygame.font Modul konnte nicht geladen werden!')
+
# Unser Tilemap Modul ebenfalls importieren.
if not pygame.mixer: print('Fehler: pygame.mixer Modul konnte nicht geladen werden!')
+
 
+
 
import Tilemap
 
import Tilemap
 +
 +
# Überprüfen, ob die optionalen Text- und Sound-Module geladen werden konnten.
 +
if not pygame.font: print('Fehler pygame.font Modul konnte nicht geladen werden!')
 +
if not pygame.mixer: print('Fehler pygame.mixer Modul konnte nicht geladen werden!')
  
 
def main():
 
def main():
 +
    # Initialisieren aller Pygame-Module und
 +
    # Fenster erstellen (wir bekommen eine Surface, die den Bildschirm repräsentiert).
 
     pygame.init()
 
     pygame.init()
 
     screen = pygame.display.set_mode((800, 600))
 
     screen = pygame.display.set_mode((800, 600))
     pygame.display.set_caption("Pygame Tutorial")
+
   
 +
    # Titel des Fensters setzen, Mauszeiger nicht verstecken und Tastendrücke wiederholt senden.
 +
     pygame.display.set_caption("Pygame-Tutorial: Tilemap")
 
     pygame.mouse.set_visible(1)
 
     pygame.mouse.set_visible(1)
 
     pygame.key.set_repeat(1, 30)
 
     pygame.key.set_repeat(1, 30)
  
 +
    # Clock Objekt erstellen, das wir benötigen, um die Framerate zu begrenzen.
 
     clock = pygame.time.Clock()
 
     clock = pygame.time.Clock()
 
      
 
      
 +
    # Wir erstellen eine Tilemap.
 
     map = Tilemap.Tilemap()
 
     map = Tilemap.Tilemap()
 
+
   
     running = 1
+
    # Die Schleife, und damit unser Spiel, läuft solange running == True.
 +
     running = True
 
     while running:
 
     while running:
 +
        # Framerate auf 30 Frames pro Sekunde beschränken.
 +
        # Pygame wartet, falls das Programm schneller läuft.
 
         clock.tick(30)
 
         clock.tick(30)
  
 +
        # screen Surface mit Schwarz (RGB = 0, 0, 0) füllen.
 
         screen.fill((0, 0, 0))
 
         screen.fill((0, 0, 0))
  
 +
        # Alle aufgelaufenen Events holen und abarbeiten.
 
         for event in pygame.event.get():
 
         for event in pygame.event.get():
 +
            # Spiel beenden, wenn wir ein QUIT-Event finden.
 
             if event.type == pygame.QUIT:
 
             if event.type == pygame.QUIT:
                 running = 0
+
                 running = False
 
+
           
 +
            # Wir interessieren uns auch für "Taste gedrückt"-Events.
 
             if event.type == pygame.KEYDOWN:
 
             if event.type == pygame.KEYDOWN:
 +
                # Wenn Escape gedrückt wird posten wir ein QUIT-Event in Pygames Event-Warteschlange.
 
                 if event.key == pygame.K_ESCAPE:
 
                 if event.key == pygame.K_ESCAPE:
                     pygame.event.post(pygame.event.Event(pygame.QUIT))  
+
                     pygame.event.post(pygame.event.Event(pygame.QUIT))
                               
+
               
                 map.handleInput(event.key)
+
                # Alle Tastendrücke auch der Tilemap mitteilen.
 
+
                 map.handle_input(event.key)
 +
       
 +
        # Die Tilemap auf die screen-Surface rendern.
 
         map.render(screen)
 
         map.render(screen)
 
          
 
          
 +
        # Inhalt von screen anzeigen
 
         pygame.display.flip()
 
         pygame.display.flip()
  
  
 +
# Überprüfen, ob dieses Modul als Programm läuft und nicht in einem anderen Modul importiert wird.
 
if __name__ == '__main__':
 
if __name__ == '__main__':
 +
    # Unsere Main-Funktion aufrufen.
 
     main()
 
     main()
 
</sourcecode>
 
</sourcecode>
Zeile 681: Zeile 719:
  
 
<sourcecode lang=python line start=5>
 
<sourcecode lang=python line start=5>
 +
# Die Klasse kümmert sich um eine einfache Animation:
 
class Animation(object):
 
class Animation(object):
     def __init__(self, image, startX, startY, num, width, height, duration):
+
     def __init__(self, image, start_x, start_y, num, width, height, duration):
         self.__image = image
+
         # Die Surface speichern,
         self.__startX = startX
+
         # alle Einzelbilder müssen in einer Reihe liegen.
         self.__startY = startY
+
         self.image = image
        self.__num = num
+
        self.__width = width
+
        self.__height = height
+
        self.__duration = duration
+
 
          
 
          
         self.__time = 0
+
        # Dazu müssen wir wissen, an welcher Position die Frames beginnen,
         self.__current = 0
+
        # wie viele Frames die Animation hat,
 +
        # sowie die Breite und Höhe der Animation kennen.
 +
        self.start_x = start_x
 +
        self.start_y = start_y
 +
        self.num = num
 +
        self.width = width
 +
        self.height = height
 +
       
 +
        # Und natürlich auch, nach welchem Zeitraum wir das nächsten Frame anzeigen sollen.
 +
        self.duration = duration
 +
       
 +
        # Die aktuelle Zeit und das aktuellen Frame speichern wir ebenfalls.
 +
         self.time = 0
 +
         self.current = 0
 
</sourcecode>
 
</sourcecode>
  
Im Konstruktor passiert nicht viel. Um die Animation darstellen zu können, benötigen wir ein paar Daten: Natürlich das Bild, wir erwarten hier aber keinen Dateinamen sondern, direkt eine Surface mit den Bilddaten. Das hat den Vorteil, dass wir in einem Bild mehrere Animationen unterbringen können und nicht unnötigerweise das Bild für jede Animation komplett geladen wird. Aus diesem Grund müssen wir auch wissen, an welcher Position im Bild die Animation beginnt: <tt>startX</tt> und <tt>startY</tt>. Dazu brauchen wir die Abmessungen der Animation (<tt>width</tt>, <tt>height</tt>) und die Anzahl der Einzelbilder (<tt>num</tt>). Jetzt fehlt noch die Zeitspanne, für die ein Frame angezeigt, wird bevor zum nächsten gewechselt wird: <tt>duration</tt>.
+
Im Konstruktor passiert nicht viel. Um die Animation darstellen zu können, benötigen wir ein paar Daten: Natürlich das Bild, wir erwarten hier aber keinen Dateinamen sondern, direkt eine Surface mit den Bilddaten. Das hat den Vorteil, dass wir in einem Bild mehrere Animationen unterbringen können und nicht unnötigerweise das Bild für jede Animation komplett geladen wird. Aus diesem Grund müssen wir auch wissen, an welcher Position im Bild die Animation beginnt: <tt>start_x</tt> und <tt>start_y</tt>. Dazu brauchen wir die Abmessungen der Animation (<tt>width</tt>, <tt>height</tt>) und die Anzahl der Einzelbilder (<tt>num</tt>). Jetzt fehlt noch die Zeitspanne, für die ein Frame angezeigt, wird bevor zum nächsten gewechselt wird: <tt>duration</tt>.
  
 
Das Rendern der Animation erledigen wir in einer Zeile:
 
Das Rendern der Animation erledigen wir in einer Zeile:
  
<sourcecode lang=python line start=29>
+
<sourcecode lang=python line start=44>
def render(self, screen, pos):
+
    # Das aktuelle Frame an einer bestimmten Position rendern:
         screen.blit(self.__image, pos, pygame.Rect(self.__startX + (self.__width * self.__current), self.__startY, self.__width, self.__height))
+
    def render(self, screen, pos):
 +
        # Welchen Bereich aus der Grafik müssen wir anzeigen?
 +
        # Die x-Position können wir aus der Breite und der Start-Position berechnen,
 +
        # die restlichen Werte kennen wir bereits.
 +
         screen.blit(self.image, pos, pygame.Rect(self.start_x + (self.width * self.current), self.start_y, self.width, self.height))
 
</sourcecode>
 
</sourcecode>
  
Wir benötigen hier wieder eine Surface, auf die wir zeichnen sollen, und dazu eine Positionsangabe. Diese beiden Parameter geben wir so direkt an die <tt>blit</tt>-Methode weiter. Fehlt nur noch die Information, welcher Bereich des Bilds genau dargestellt werden soll. Dazu basteln wir ein Rechteck (<tt>pygame.Rect</tt>) mit den folgenden Daten: Der Beginn unserer Animation ist in <tt>__startX</tt> und <tt>__startY</tt> gespeichert. Da wir festgelegt haben, dass sich unsere Frames alle in einer Reihe befinden müssen, addieren wir die Breite des Einzelbildes multipliziert mit der Nummer des aktuellen Einzelbild zur x-Position dazu. Das bringt uns exakt zu dem Einzelbild, das wir anzeigen wollen. Die Größe der Animation kennen wir bereits.
+
Wir benötigen hier wieder eine Surface, auf die wir zeichnen sollen, und dazu eine Positionsangabe. Diese beiden Parameter geben wir so direkt an die <tt>blit</tt>-Methode weiter. Fehlt nur noch die Information, welcher Bereich des Bilds genau dargestellt werden soll. Dazu basteln wir ein Rechteck (<tt>pygame.Rect</tt>) mit den folgenden Daten: Der Beginn unserer Animation ist in <tt>start_x</tt> und <tt>start_y</tt> gespeichert. Da wir festgelegt haben, dass sich unsere Frames alle in einer Reihe befinden müssen, addieren wir die Breite des Einzelbildes multipliziert mit der Nummer des aktuellen Einzelbild zur x-Position dazu. Das bringt uns exakt zu dem Einzelbild, das wir anzeigen wollen. Die Größe der Animation kennen wir bereits.
  
 
Jetzt müssen wir die Animation nur noch aktualisieren, denn momentan würde immer nur das gleiche Bild (das erste) angezeigt.
 
Jetzt müssen wir die Animation nur noch aktualisieren, denn momentan würde immer nur das gleiche Bild (das erste) angezeigt.
  
<sourcecode lang=python line start=19>
+
<sourcecode lang=python line start=29>
def update(self, time = 1):
+
    # Die update-Methode rufen wir einmal pro Frame auf:
         self.__time += time
+
    def update(self, time=1):
 +
        # Die vergangene Zeit addieren
 +
         self.time += time
 
          
 
          
         if self.__time > self.__duration:
+
        # Falls wir den Anzeige-Zeitraum überschreiten, ...
             self.__time = 0
+
         if self.time > self.duration:
             self.__current += 1
+
            # ... setzten wir die Zeit zurück und gehen zum nächsten Frame.
             if self.__current >= self.__num:
+
             self.time = 0
                 self.__current = 0
+
             self.current += 1
 +
            # Sicherstellen, dass das aktuelle Frame auch verfügbar ist.
 +
             if self.current >= self.num:
 +
                 self.current = 0
 
</sourcecode>
 
</sourcecode>
  
Ein Einzelbild der Animation soll immer nur eine bestimmte Zeit lang angezeigt werden. Der <tt>update</tt>-Methode übergeben wir einfach die Zeit, die seit dem letzten Aufruf der Methode vergangen ist. Hier in dem Tutorial machen wir das auf Frame-Basis, deshalb erhöhen wir standardmäßig die Zeit um 1 und rufen die <tt>update</tt>-Methode später in jedem Frame genau einmal auf. Ab Zeile 22 überprüfen wir, ob genügend viel Zeit vergangen ist und wir ein neues Frame anzeigen müssen. Wenn die Zeit größer als die Anzeigedauer für ein Einzelbild ist, setzen wir zuerst die Zeit zurück auf 0 und erhöhen die Nummer des aktuellen Einzelbildes. Im Tutorial lassen wir unsere Animation einfach am Anfang weiterspielen wenn sie fertig ist (Looping), deshalb sorgen wir noch dafür, dass <tt>__current</tt> im erlaubten Wertebereich bleibt.
+
Ein Einzelbild der Animation soll immer nur eine bestimmte Zeit lang angezeigt werden. Der <tt>update</tt>-Methode übergeben wir einfach die Zeit, die seit dem letzten Aufruf der Methode vergangen ist. Hier in dem Tutorial machen wir das auf Frame-Basis, deshalb erhöhen wir standardmäßig die Zeit um 1 und rufen die <tt>update</tt>-Methode später in jedem Frame genau einmal auf. Ab Zeile 22 überprüfen wir, ob genügend viel Zeit vergangen ist und wir ein neues Frame anzeigen müssen. Wenn die Zeit größer als die Anzeigedauer für ein Einzelbild ist, setzen wir zuerst die Zeit zurück auf 0 und erhöhen die Nummer des aktuellen Einzelbildes. Im Tutorial lassen wir unsere Animation einfach am Anfang weiterspielen wenn sie fertig ist (Looping), deshalb sorgen wir noch dafür, dass <tt>current</tt> im erlaubten Wertebereich bleibt.
  
 
Und damit ist unsere Animations-Klasse schon fertig. Den Rest erledigt die <tt>Player</tt>-Klasse.
 
Und damit ist unsere Animations-Klasse schon fertig. Den Rest erledigt die <tt>Player</tt>-Klasse.
Zeile 727: Zeile 784:
  
 
<sourcecode lang=python line start=7>
 
<sourcecode lang=python line start=7>
 +
# Die Player Klasse verwendet zwei Animationen, um eine steuerbare Spielfigur dazustellen.
 
class Player(object):
 
class Player(object):
 
     def __init__(self):
 
     def __init__(self):
         self.__anim_image_right = loadImage("tileset.png", (255, 0, 255))
+
        # Bild laden und erste Animation erstellen:
         self.__anim_right = Animation.Animation(self.__anim_image_right, 32, 32, 2, 32, 64, 15)
+
         self.anim_image_right = Utils.loadImage("tileset.png", (255, 0, 255))
 +
         self.anim_right = Animation.Animation(self.anim_image_right, 32, 32, 2, 32, 64, 15)
 
</sourcecode>
 
</sourcecode>
  
Zeile 743: Zeile 802:
 
Damit können wir unser Bild einfach horizontal (das brauchen wir) oder vertikal spiegeln. Dabei wird eine neue Surface erstellt, die wir für unsere zweite Animation verwenden können.
 
Damit können wir unser Bild einfach horizontal (das brauchen wir) oder vertikal spiegeln. Dabei wird eine neue Surface erstellt, die wir für unsere zweite Animation verwenden können.
  
<sourcecode lang=python line start=12>
+
<sourcecode lang=python line start=14>
         self.__anim_image_left = pygame.transform.flip(self.__anim_image_right, True, False)
+
        # Die Grafik spiegeln und in einer neuen Surface speichern,
         self.__anim_left = Animation.Animation(self.__anim_image_left, 32, 32, 2, 32, 64, 15)
+
        # dann können wir die linke Animation erstellen.
 +
         self.anim_image_left = pygame.transform.flip(self.anim_image_right, True, False)
 +
         self.anim_left = Animation.Animation(self.anim_image_left, 32, 32, 2, 32, 64, 15)
 
</sourcecode>
 
</sourcecode>
  
 
Und diese zweite Animation erstellen wir nach dem selben Muster wie zuvor. Wir können uns außerdem für später merken: Im <tt>transform</tt>-Modul gibt es noch weitere Funktionen zum Skalieren oder Rotieren von Surfaces. Der Rest des Konstruktors speichert nur noch die Position des Spielers, die Blickrichtung und ob wir gerade laufen oder nicht:
 
Und diese zweite Animation erstellen wir nach dem selben Muster wie zuvor. Wir können uns außerdem für später merken: Im <tt>transform</tt>-Modul gibt es noch weitere Funktionen zum Skalieren oder Rotieren von Surfaces. Der Rest des Konstruktors speichert nur noch die Position des Spielers, die Blickrichtung und ob wir gerade laufen oder nicht:
  
<sourcecode lang=python line start=15>
+
<sourcecode lang=python line start=19>
         self.__posX = 10*32
+
        # Startposition des Players festlegen und
         self.__posY = 13*32         
+
        # merken, in welche Richtung wir schauen, und ob wir überhaupt laufen.
         self.__dir = 0
+
         self.pos_x = 10*32
         self.__walking = False
+
         self.pos_y = 13*32         
 +
         self.dir = 0
 +
         self.walking = False
 
</sourcecode>
 
</sourcecode>
  
Diese Werte kann der Spieler später über die Pfeiltasten beeinflussen. Das wird in der <tt>handleInput</tt>-Methode erledigt:
+
Diese Werte kann der Spieler später über die Pfeiltasten beeinflussen. Das wird in der <tt>handle_input</tt>-Methode erledigt:
  
<sourcecode lang=python line start=34>
+
<sourcecode lang=python line start=46>
     def handleInput(self, key):
+
     def handle_input(self, key):
 +
        # Linke Pfeiltaste wird gedrückt:
 
         if key == pygame.K_LEFT:
 
         if key == pygame.K_LEFT:
             self.__posX -= 1
+
            # x-Position der Spielfigur anpassen,
             self.__dir = -1
+
            # die Blickrichtung festlegen
             self.__walking = True
+
            # und den Laufen-Zustand einschalten.
 +
             self.pos_x -= 1
 +
             self.dir = -1
 +
             self.walking = True
 +
       
 +
        # Und nochmal für die rechte Pfeiltaste.
 
         if key == pygame.K_RIGHT:
 
         if key == pygame.K_RIGHT:
             self.__posX += 1
+
             self.pos_x += 1
             self.__dir = 1
+
             self.dir = 1
             self.__walking = True
+
             self.walking = True
 
</sourcecode>
 
</sourcecode>
  
Das ist sehr ähnlich zur <tt>handleInput</tt>-Methode unserer <tt>Tilemap</tt>-Klasse. Je nach dem, welche Pfeiltaste gedrückt wird, erhöhen bzw. verringern wir die x-Position des Spielers (damit kann er nach rechts und links laufen) und passen die Blickrichtung an: -1 für links und +1 für rechts. Außerdem sind wir in beiden Fällen am laufen, setzen <tt>__walking</tt> also auf <tt>True</tt>. Diese Information benötigen wir zum Rendern:
+
Das ist sehr ähnlich zur <tt>handle_input</tt>-Methode unserer <tt>Tilemap</tt>-Klasse. Je nach dem, welche Pfeiltaste gedrückt wird, erhöhen bzw. verringern wir die x-Position des Spielers (damit kann er nach rechts und links laufen) und passen die Blickrichtung an: -1 für links und +1 für rechts. Außerdem sind wir in beiden Fällen am laufen, setzen <tt>walking</tt> also auf <tt>True</tt>. Diese Information benötigen wir zum Rendern:
  
<sourcecode lang=python line start=21>
+
<sourcecode lang=python line start=27>
     def render(self, screen):      
+
     def render(self, screen):
         if self.__dir == -1:  
+
        # Die Blickrichtung ist links:
             if self.__walking:
+
         if self.dir == -1:
                 self.__anim_left.update()
+
            # Wenn der Spieler die linke oder rechte Pfeiltaste gedrückt hat sind wir am laufen,
             self.__anim_left.render(screen, (self.__posX, self.__posY))   
+
             if self.walking:              
         else:    
+
                # nur dann die Animation updaten.
             if self.__walking:
+
                 self.anim_left.update()
                 self.__anim_right.update()
+
            # Blickrichtung links rendern.
             self.__anim_right.render(screen, (self.__posX, self.__posY))
+
             self.anim_left.render(screen, (self.pos_x, self.pos_y))   
           
+
         else:
         self.__walking = False;
+
            # Und das gleiche nochmal für rechts:
 +
             if self.walking:
 +
                 self.anim_right.update()
 +
             self.anim_right.render(screen, (self.pos_x, self.pos_y))
 +
       
 +
        # De Laufen-Zustand zurücksetzen, im nächsten Frame bleiben wir stehen.
 +
         self.walking = False
 
</sourcecode>
 
</sourcecode>
  
Die <tt>render</tt>-Methode sieht komplizierter aus als sie es ist. Wir haben drei mögliche Zustände, in denen sich der Spieler befinden kann: Stehen, links gehen und rechts gehen. Dafür haben wir vorher die Blickrichtung gespeichert. Schauen wir zuerst mal, was passiert, wenn der Spieler weder die rechte noch die linke Pfeiltaste gedrückt hat: <tt>__dir </tt> ist 0 (das haben wir im Konstruktor so festgelegt), wir kommen also zu Zeile 26. Der Einfachheit halber lassen wir in diesem Fall den Spieler einfach immer nach rechts schauen, später wird die Blickrichtung erhalten bleiben. Da <tt>__walking = False</tt> ist, aktualisieren wir die Animation nicht, das Einzelbild verändert sich also nicht. Am Ende der Methode setzen wir <tt>__walking</tt> wieder auf <tt>False</tt>, damit wir im nächsten Frame stehen (natürlich nur falls, der Spieler keine Pfeiltaste mehr drückt). Die Blickrichtung setzen wir nicht zurück, damit wissen wir im nächsten Frame noch, welche Animation wir anzeigen sollen, auch wenn der Spieler keine Pfeiltaste gedrückt hat. Der Zustand für "rechts gehen" (mit <tt>__dir</tt> ist 1) ist exakt der gleiche wie der, den wir eben angesehen haben, und für die Variante "links gehen" (<tt>__dir</tt> ist -1) aktualisieren und zeichnen wir anstatt der rechten Animation eben die linke.
+
Die <tt>render</tt>-Methode sieht komplizierter aus als sie es ist. Wir haben drei mögliche Zustände, in denen sich der Spieler befinden kann: Stehen, links gehen und rechts gehen. Dafür haben wir vorher die Blickrichtung gespeichert. Schauen wir zuerst mal, was passiert, wenn der Spieler weder die rechte noch die linke Pfeiltaste gedrückt hat: <tt>dir </tt> ist 0 (das haben wir im Konstruktor so festgelegt), wir kommen also zu Zeile 26. Der Einfachheit halber lassen wir in diesem Fall den Spieler einfach immer nach rechts schauen, später wird die Blickrichtung erhalten bleiben. Da <tt>walking = False</tt> ist, aktualisieren wir die Animation nicht, das Einzelbild verändert sich also nicht. Am Ende der Methode setzen wir <tt>walking</tt> wieder auf <tt>False</tt>, damit wir im nächsten Frame stehen (natürlich nur falls, der Spieler keine Pfeiltaste mehr drückt). Die Blickrichtung setzen wir nicht zurück, damit wissen wir im nächsten Frame noch, welche Animation wir anzeigen sollen, auch wenn der Spieler keine Pfeiltaste gedrückt hat. Der Zustand für "rechts gehen" (mit <tt>dir</tt> ist 1) ist exakt der gleiche wie der, den wir eben angesehen haben, und für die Variante "links gehen" (<tt>dir</tt> ist -1) aktualisieren und zeichnen wir anstatt der rechten Animation eben die linke.
  
 
Jetzt fehlt nur noch die Einbindung der <tt>Player</tt>-Klasse in unser Beispielspiel.
 
Jetzt fehlt nur noch die Einbindung der <tt>Player</tt>-Klasse in unser Beispielspiel.
Zeile 794: Zeile 869:
 
Wann erstellen wir ein <tt>Player</tt>-Objekt? Wenn das Level geladen wird. Hier im Tutorial passiert das alles im Konstruktor der <tt>Tilemap</tt>-Klasse. Zuerst erweitern wir das Tileset um zwei neue Tiles:
 
Wann erstellen wir ein <tt>Player</tt>-Objekt? Wenn das Level geladen wird. Hier im Tutorial passiert das alles im Konstruktor der <tt>Tilemap</tt>-Klasse. Zuerst erweitern wir das Tileset um zwei neue Tiles:
  
<sourcecode lang=python line start=16>
+
<sourcecode lang=python line start=15>
         self.__tileset.addTile("grass-mud", 0, 64)
+
         self.tileset.add_tile("grass-mud", 0, 64)
         self.__tileset.addTile("empty", 0, 96)
+
         self.tileset.add_tile("empty", 0, 96)
 
</sourcecode>
 
</sourcecode>
  
 
Und dann passen wir die Generierung der Tilemap etwas an:
 
Und dann passen wir die Generierung der Tilemap etwas an:
  
<sourcecode lang=python line start=27>
+
<sourcecode lang=python line start=30>
         for i in range(0, self.__width):
+
         for i in range(0, self.height):
             self.__tiles.append(list())
+
             self.tiles.append(list())
             for j in range(0, self.__height):
+
             for j in range(0, self.width):
 
                 if i == 14:
 
                 if i == 14:
                     self.__tiles[i].append("grass")  
+
                     self.tiles[i].append("grass")  
 
                 elif i == 15:
 
                 elif i == 15:
                     self.__tiles[i].append("grass-mud")                  
+
                     self.tiles[i].append("grass-mud")
 
                 elif i > 15:
 
                 elif i > 15:
                     self.__tiles[i].append("mud")
+
                     self.tiles[i].append("mud")
 
                 else:
 
                 else:
                     self.__tiles[i].append("empty")
+
                     self.tiles[i].append("empty")
 
</sourcecode>
 
</sourcecode>
  
 
Um am Ende des Konstuktors endlich einen Spieler erstellen zu können:
 
Um am Ende des Konstuktors endlich einen Spieler erstellen zu können:
  
<sourcecode lang=python line start=39>
+
<sourcecode lang=python line start=43>
         self.__player = Player.Player()
+
         self.player = Player.Player()
 
</sourcecode>
 
</sourcecode>
  
 
In der <tt>render</tt>-Methode rendern wir den Spieler:
 
In der <tt>render</tt>-Methode rendern wir den Spieler:
  
<sourcecode lang=python line start=56>
+
<sourcecode lang=python line start=70>
         self.__player.render(screen)
+
         self.player.render(screen)
 
</sourcecode>
 
</sourcecode>
  
Und in der <tt>handleInput</tt>-Methode reichen wir die Tastendrücke durch:
+
Und in der <tt>handle_input</tt>-Methode reichen wir die Tastendrücke durch:
  
<sourcecode lang=python line start=60>
+
<sourcecode lang=python line start=75>
         self.__player.handleInput(key)
+
         self.player.handle_input(key)
 
</sourcecode>
 
</sourcecode>
  
Zeile 836: Zeile 911:
  
 
Hier nochmal der neue Code im Überblick:
 
Hier nochmal der neue Code im Überblick:
 +
 +
<tt>TileType.py</tt>, <tt>Tileset.py</tt> und <tt>Utils.py</tt> haben sich nicht verändert.
  
 
<tt>Animation.py</tt>:
 
<tt>Animation.py</tt>:
Zeile 843: Zeile 920:
 
import pygame
 
import pygame
  
 +
# Die Klasse kümmert sich um eine einfache Animation:
 
class Animation(object):
 
class Animation(object):
     def __init__(self, image, startX, startY, num, width, height, duration):
+
     def __init__(self, image, start_x, start_y, num, width, height, duration):
         self.__image = image
+
         # Die Surface speichern,
         self.__startX = startX
+
         # alle Einzelbilder müssen in einer Reihe liegen.
         self.__startY = startY
+
         self.image = image
        self.__num = num
+
        self.__width = width
+
        self.__height = height
+
        self.__duration = duration
+
 
          
 
          
         self.__time = 0
+
        # Dazu müssen wir wissen, an welcher Position die Frames beginnen,
         self.__current = 0
+
        # wie viele Frames die Animation hat,
 +
        # sowie die Breite und Höhe der Animation kennen.
 +
        self.start_x = start_x
 +
        self.start_y = start_y
 +
        self.num = num
 +
        self.width = width
 +
        self.height = height
 +
       
 +
        # Und natürlich auch, nach welchem Zeitraum wir das nächsten Frame anzeigen sollen.
 +
        self.duration = duration
 +
       
 +
        # Die aktuelle Zeit und das aktuellen Frame speichern wir ebenfalls.
 +
         self.time = 0
 +
         self.current = 0
 
          
 
          
 
      
 
      
     def update(self, time = 1):
+
    # Die update-Methode rufen wir einmal pro Frame auf:
         self.__time += time
+
     def update(self, time=1):
 +
        # Die vergangene Zeit addieren
 +
         self.time += time
 
          
 
          
         if self.__time > self.__duration:
+
        # Falls wir den Anzeige-Zeitraum überschreiten, ...
             self.__time = 0
+
         if self.time > self.duration:
             self.__current += 1
+
            # ... setzten wir die Zeit zurück und gehen zum nächsten Frame.
             if self.__current >= self.__num:
+
             self.time = 0
                 self.__current = 0
+
             self.current += 1
 +
            # Sicherstellen, dass das aktuelle Frame auch verfügbar ist.
 +
             if self.current >= self.num:
 +
                 self.current = 0
 
                  
 
                  
       
+
   
 +
    # Das aktuelle Frame an einer bestimmten Position rendern:
 
     def render(self, screen, pos):
 
     def render(self, screen, pos):
         screen.blit(self.__image, pos, pygame.Rect(self.__startX + (self.__width * self.__current), self.__startY, self.__width, self.__height))
+
        # Welchen Bereich aus der Grafik müssen wir anzeigen?
 +
        # Die x-Position können wir aus der Breite und der Start-Position berechnen,
 +
        # die restlichen Werte kennen wir bereits.
 +
         screen.blit(self.image, pos, pygame.Rect(self.start_x + (self.width * self.current), self.start_y, self.width, self.height))
 
</sourcecode>
 
</sourcecode>
  
Zeile 876: Zeile 972:
  
 
import pygame
 
import pygame
 +
import Utils
 
import Animation
 
import Animation
import Tileset              # für loadImage
 
  
 +
# Die Player Klasse verwendet zwei Animationen, um eine steuerbare Spielfigur dazustellen.
 
class Player(object):
 
class Player(object):
 
     def __init__(self):
 
     def __init__(self):
         self.__anim_image_right = Tileset.loadImage("tileset.png", (255, 0, 255))
+
        # Bild laden und erste Animation erstellen:
         self.__anim_right = Animation.Animation(self.__anim_image_right, 32, 32, 2, 32, 64, 15)   
+
         self.anim_image_right = Utils.load_image("tileset.png", (255, 0, 255))
 +
         self.anim_right = Animation.Animation(self.anim_image_right, 32, 32, 2, 32, 64, 15)   
 
          
 
          
         self.__anim_image_left = pygame.transform.flip(self.__anim_image_right, True, False)
+
        # Die Grafik spiegeln und in einer neuen Surface speichern,
         self.__anim_left = Animation.Animation(self.__anim_image_left, 32, 32, 2, 32, 64, 15)
+
        # dann können wir die linke Animation erstellen.
 +
         self.anim_image_left = pygame.transform.flip(self.anim_image_right, True, False)
 +
         self.anim_left = Animation.Animation(self.anim_image_left, 32, 32, 2, 32, 64, 15)
 
          
 
          
         self.__posX = 10*32
+
        # Start-Position des Players festlegen und
         self.__posY = 13*32         
+
        # merken in welche Richtung wir schauen und ob wir überhaupt laufen.
         self.__dir = 0
+
         self.pos_x = 10*32
         self.__walking = False
+
         self.pos_y = 13*32         
 +
         self.dir = 0
 +
         self.walking = False
 
          
 
          
 
          
 
          
     def render(self, screen):        
+
     def render(self, screen):
         if self.__dir == -1:  
+
        # Die Blickrichtung ist links:
             if self.__walking:
+
         if self.dir == -1:
                 self.__anim_left.update()
+
            # Wenn der Spieler die linke oder rechte Pfeiltaste gedrückt hat sind wir am laufen,
             self.__anim_left.render(screen, (self.__posX, self.__posY))   
+
             if self.walking:              
         else:    
+
                # nur dann die Animation updaten.
             if self.__walking:
+
                 self.anim_left.update()
                 self.__anim_right.update()
+
            # Blickrichtung links rendern.
             self.__anim_right.render(screen, (self.__posX, self.__posY))
+
             self.anim_left.render(screen, (self.pos_x, self.pos_y))   
           
+
         else:
        self.__walking = False
+
            # Und das gleiche nochmal für rechts:
 +
             if self.walking:
 +
                 self.anim_right.update()
 +
             self.anim_right.render(screen, (self.pos_x, self.pos_y))
 
          
 
          
 +
        # De Laufen-Zustand zurücksetzen, im nächsten Frame bleiben wir stehen.
 +
        self.walking = False
 
          
 
          
     def handleInput(self, key):
+
   
 +
     def handle_input(self, key):
 +
        # Linke Pfeiltaste wird gedrückt:
 
         if key == pygame.K_LEFT:
 
         if key == pygame.K_LEFT:
             self.__posX -= 1
+
            # x-Position der Spielfigur anpassen,
             self.__dir = -1
+
            # die Blickrichtung festlegen
             self.__walking = True
+
            # und den Laufen-Zustand einschalten.
 +
             self.pos_x -= 1
 +
             self.dir = -1
 +
             self.walking = True
 +
       
 +
        # Und nochmal für die rechte Pfeiltaste.
 
         if key == pygame.K_RIGHT:
 
         if key == pygame.K_RIGHT:
             self.__posX += 1
+
             self.pos_x += 1
             self.__dir = 1
+
             self.dir = 1
             self.__walking = True
+
             self.walking = True
 
</sourcecode>
 
</sourcecode>
  
Zeile 922: Zeile 1.036:
  
 
import pygame
 
import pygame
import random
 
 
import Tileset
 
import Tileset
 
import Player
 
import Player
  
 +
# Die Tilemap Klasse verwaltet die Tile-Daten, die das Aussehen der Karte beschreiben.
 
class Tilemap(object):
 
class Tilemap(object):
     def __init__(self):      
+
     def __init__(self):
         self.__tileset = Tileset.Tileset("tileset.png", (255, 0, 255), 32, 32)
+
        # Wir erstellen ein neues Tileset.
         self.__tileset.addTile("grass", 0, 0)
+
        # Hier im Tutorial fügen wir manuell vier Tile-Typen hinzu.
         self.__tileset.addTile("mud", 32, 0)
+
         self.tileset = Tileset.Tileset("tileset.png", (255, 0, 255), 32, 32)
         self.__tileset.addTile("water", 64, 0)
+
         self.tileset.add_tile("grass", 0, 0)
         self.__tileset.addTile("block", 0, 32)
+
         self.tileset.add_tile("mud", 32, 0)      
 +
         self.tileset.add_tile("grass-mud", 0, 64)
 +
         self.tileset.add_tile("empty", 0, 96)
 
          
 
          
         self.__tileset.addTile("grass-mud", 0, 64)
+
         # Festlegen der Startposition der Kamera. Hier (0, 0).
         self.__tileset.addTile("empty", 0, 96)
+
         self.camera_x = 0
 +
        self.camera_y = 0
 
          
 
          
         self.__cameraX = 0
+
         # Die Größe der Maps in Tiles.
        self.__cameraY = 0
+
         self.width = 30
           
+
         self.height = 25
         self.__width = 25
+
         self.__height = 25
+
  
         self.__tiles = list()
+
        # Erstellen einer leeren Liste für die Tile Daten.
          
+
         self.tiles = list()
         for i in range(0, self.__width):
+
               
             self.__tiles.append(list())
+
         # Sehr einfache Karte basteln:
             for j in range(0, self.__height):
+
         for i in range(0, self.height):
 +
             self.tiles.append(list())
 +
             for j in range(0, self.width):
 
                 if i == 14:
 
                 if i == 14:
                     self.__tiles[i].append("grass")  
+
                     self.tiles[i].append("grass")  
 
                 elif i == 15:
 
                 elif i == 15:
                     self.__tiles[i].append("grass-mud")                  
+
                     self.tiles[i].append("grass-mud")
 
                 elif i > 15:
 
                 elif i > 15:
                     self.__tiles[i].append("mud")
+
                     self.tiles[i].append("mud")
 
                 else:
 
                 else:
                     self.__tiles[i].append("empty")
+
                     self.tiles[i].append("empty")
 
          
 
          
         self.__player = Player.Player()
+
        # Player-Objekt erstellen.
 +
         self.player = Player.Player()
 
                      
 
                      
 
      
 
      
 +
    # Hier rendern wir den sichtbaren Teil der Karte.
 
     def render(self, screen):
 
     def render(self, screen):
         for y in range(0, int(screen.get_height() / self.__tileset.getTileHeight()) + 1):
+
        # Zeilenweise durch die Tiles durchgehen.
             ty = y + self.__cameraY
+
         for y in range(0, int(screen.get_height() / self.tileset.tile_height) + 1):
             if ty >= self.__height or ty < 0:
+
            # Die Kamera Position mit einbeziehen.
 +
             ty = y + self.camera_y
 +
             if ty >= self.height or ty < 0:
 
                 continue
 
                 continue
             col = self.__tiles[ty]
+
             # Die aktuelle Zeile zum einfacheren Zugriff speichern.
             for x in range(0, int(screen.get_width() / self.__tileset.getTileWidth()) + 1):
+
            line = self.tiles[ty]
                 tx = x + self.__cameraX
+
            # Und jetzt spaltenweise die Tiles rendern.
                 if tx >= self.__width or tx < 0:
+
             for x in range(0, int(screen.get_width() / self.tileset.tile_width) + 1):
 +
                # Auch hier müssen wir die Kamera beachten.
 +
                 tx = x + self.camera_x
 +
                 if tx >= self.width or tx < 0:
 
                     continue
 
                     continue
                 tilename = col[tx]
+
                # Wir versuchen, die Daten des Tiles zu bekommen.
                 tile = self.__tileset.getTile(tilename)
+
                 tilename = line[tx]
 +
                 tile = self.tileset.get_tile(tilename)
 +
                # Falls das nicht fehlschlägt können wir das Tile auf die screen-Surface blitten.
 
                 if tile is not None:
 
                 if tile is not None:
                     screen.blit(self.__tileset.getImage(), (x * self.__tileset.getTileWidth(), y * self.__tileset.getTileHeight()), tile.getRect())
+
                     screen.blit(self.tileset.image, (x * self.tileset.tile_width, y * self.tileset.tile_height), tile.rect)
 +
       
 +
        # Und zuletzt den Player rendern.
 +
        self.player.render(screen)
 +
 
 +
       
 +
    # Tastendrücke an den Player weiterreichen:
 +
    def handle_input(self, key):       
 +
        self.player.handle_input(key)
 +
</sourcecode>
 +
 
 +
<tt>Teil_3.py</tt>
 +
<sourcecode lang=python line>
 +
# -*- coding: UTF-8 -*-
 +
 
 +
# Pygame Modul importieren.
 +
import pygame
 +
 
 +
# Unser Tilemap Modul
 +
import Tilemap
 +
 
 +
# Überprüfen, ob die optionalen Text- und Sound-Module geladen werden konnten.
 +
if not pygame.font: print('Fehler pygame.font Modul konnte nicht geladen werden!')
 +
if not pygame.mixer: print('Fehler pygame.mixer Modul konnte nicht geladen werden!')
 +
 
 +
def main():
 +
    # Initialisieren aller Pygame-Module und
 +
    # Fenster erstellen (wir bekommen eine Surface, die den Bildschirm repräsentiert).
 +
    pygame.init()
 +
    screen = pygame.display.set_mode((800, 600))
 
      
 
      
        self.__player.render(screen)
+
    # Titel des Fensters setzen, Mauszeiger nicht verstecken und Tastendrücke wiederholt senden.
                   
+
    pygame.display.set_caption("Pygame-Tutorial: Animation")
 +
    pygame.mouse.set_visible(1)
 +
    pygame.key.set_repeat(1, 30)
 +
 
 +
    # Clock-Objekt erstellen, das wir benötigen, um die Framerate zu begrenzen.
 +
    clock = pygame.time.Clock()
 
      
 
      
     def handleInput(self, key):
+
     # Wir erstellen eine Tilemap.
         self.__player.handleInput(key)
+
    map = Tilemap.Tilemap()
 +
   
 +
    # Die Schleife, und damit unser Spiel, läuft solange running == True.
 +
    running = True
 +
    while running:
 +
         # Framerate auf 30 Frames pro Sekunde beschränken.
 +
        # Pygame wartet, falls das Programm schneller läuft.
 +
        clock.tick(30)
 +
 
 +
        # screen Surface mit Schwarz (RGB = 0, 0, 0) füllen.
 +
        screen.fill((198, 209, 255))
 +
 
 +
        # Alle aufgelaufenen Events holen und abarbeiten.
 +
        for event in pygame.event.get():
 +
            # Spiel beenden, wenn wir ein QUIT-Event finden.
 +
            if event.type == pygame.QUIT:
 +
                running = False
 +
           
 +
            # Wir interessieren uns auch für "Taste gedrückt"-Events.
 +
            if event.type == pygame.KEYDOWN:
 +
                # Wenn Escape gedrückt wird posten wir ein QUIT-Event in Pygames Event-Warteschlange.
 +
                if event.key == pygame.K_ESCAPE:
 +
                    pygame.event.post(pygame.event.Event(pygame.QUIT))
 +
               
 +
                # Alle Tastendrücke auch der Tilemap mitteilen.
 +
                map.handle_input(event.key)
 +
       
 +
        # Die Tilemap auf die screen-Surface rendern.
 +
        map.render(screen)
 +
       
 +
        # Inhalt von screen anzeigen
 +
        pygame.display.flip()
 +
 
 +
 
 +
# Überprüfen, ob dieses Modul als Programm läuft und nicht in einem anderen Modul importiert wird.
 +
if __name__ == '__main__':
 +
    # Unsere Main-Funktion aufrufen.
 +
    main()
 
</sourcecode>
 
</sourcecode>
 +
 +
== Sourcecode herunterladen ==
 +
Du kannst dir den Sourcecode (und die Tileset-Grafik) zu diesem Tutorial [[Media:Pygame-Tutorial.tar.gz|hier herunterladen]]. Unter Windows kannst du die Beispiele starten, indem du doppelt auf die Dateien <tt>Teil_1.py</tt>, <tt>Teil_2.py</tt> und <tt>Teil_3.py</tt> klickst. Funktioniert das nicht, solltest du die Installationen von Python und Pygame nochmal überprüfen.
  
 
== Und jetzt? ==
 
== Und jetzt? ==

Aktuelle Version vom 13. März 2015, 14:32 Uhr

Klicke hier, um diese Version anzusehen.

Meine Werkzeuge
Namensräume
Varianten
Aktionen
Navigation
Werkzeuge