Pygame-Tutorial
Aus Spieleprogrammierer-Wiki
(Unterschied zwischen Versionen)
[unmarkierte Version] | [unmarkierte Version] |
K (→Zusammenbasteln) |
(→Zusammenbasteln) |
||
Zeile 964: | Zeile 964: | ||
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 971: | Zeile 973: | ||
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, startX, startY, num, width, height, duration): | ||
+ | # Die Surface speichern, | ||
+ | # alle Einzelbilder müssen in einer Reihe liegen. | ||
self.__image = image | self.__image = image | ||
+ | |||
+ | # Dazu müssen wir wissen, an welcher Position die Frames beginnen, | ||
+ | # wie viele Frames die Animation hat, | ||
+ | # sowie die Breite und Höhe der Animation kennen. | ||
self.__startX = startX | self.__startX = startX | ||
self.__startY = startY | self.__startY = startY | ||
Zeile 979: | Zeile 988: | ||
self.__width = width | self.__width = width | ||
self.__height = height | self.__height = height | ||
+ | |||
+ | # Und natürlich auch, nach welchem Zeitraum wir das nächsten Frame anzeigen sollen. | ||
self.__duration = duration | self.__duration = duration | ||
+ | # Die aktuelle Zeit und das aktuellen Frame speichern wir ebenfalls. | ||
self.__time = 0 | self.__time = 0 | ||
self.__current = 0 | self.__current = 0 | ||
+ | # Die update-Methode rufen wir einmal pro Frame auf: | ||
def update(self, time = 1): | def update(self, time = 1): | ||
+ | # Die vergangene Zeit addieren | ||
self.__time += time | self.__time += time | ||
+ | # Falls wir den Anzeige-Zeitraum überschreiten, ... | ||
if self.__time > self.__duration: | if self.__time > self.__duration: | ||
+ | # ... setzten wir die Zeit zurück und gehen zum nächsten Frame. | ||
self.__time = 0 | self.__time = 0 | ||
self.__current += 1 | self.__current += 1 | ||
+ | # Sicherstellen, dass das aktuelle Frame auch verfügbar ist. | ||
if self.__current >= self.__num: | if self.__current >= self.__num: | ||
self.__current = 0 | self.__current = 0 | ||
− | + | ||
+ | # Das aktuelle Frame an einer bestimmten Position rendern: | ||
def render(self, screen, pos): | 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.__startX + (self.__width * self.__current), self.__startY, self.__width, self.__height)) | screen.blit(self.__image, pos, pygame.Rect(self.__startX + (self.__width * self.__current), self.__startY, self.__width, self.__height)) | ||
</sourcecode> | </sourcecode> | ||
Zeile 1.004: | Zeile 1.025: | ||
import pygame | import pygame | ||
+ | import Utils | ||
import Animation | import Animation | ||
− | |||
+ | # 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 = | + | # Bild laden und erste Animation erstellen: |
+ | 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) | self.__anim_right = Animation.Animation(self.__anim_image_right, 32, 32, 2, 32, 64, 15) | ||
+ | # Die Grafik spiegeln und in einer neuen Surface speichern, | ||
+ | # dann können wir die linke Animation erstellen. | ||
self.__anim_image_left = pygame.transform.flip(self.__anim_image_right, True, False) | 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.__anim_left = Animation.Animation(self.__anim_image_left, 32, 32, 2, 32, 64, 15) | ||
+ | # Start-Position des Players festlegen und | ||
+ | # merken in welche Richtung wir schauen und ob wir überhaupt laufen. | ||
self.__posX = 10*32 | self.__posX = 10*32 | ||
self.__posY = 13*32 | self.__posY = 13*32 | ||
Zeile 1.021: | Zeile 1.048: | ||
− | def render(self, screen): | + | def render(self, screen): |
− | if self.__dir == -1: | + | # Die Blickrichtung ist links: |
− | if self.__walking: | + | if self.__dir == -1: |
+ | # Wenn der Spieler die linke oder rechte Pfeiltaste gedrückt hat sind wir am laufen, | ||
+ | if self.__walking: | ||
+ | # nur dann die Animation updaten. | ||
self.__anim_left.update() | self.__anim_left.update() | ||
+ | # Blickrichtung links rendern. | ||
self.__anim_left.render(screen, (self.__posX, self.__posY)) | self.__anim_left.render(screen, (self.__posX, self.__posY)) | ||
− | else: | + | else: |
+ | # Und das gleiche nochmal für rechts: | ||
if self.__walking: | if self.__walking: | ||
self.__anim_right.update() | self.__anim_right.update() | ||
self.__anim_right.render(screen, (self.__posX, self.__posY)) | self.__anim_right.render(screen, (self.__posX, self.__posY)) | ||
− | + | ||
+ | # De Laufen-Zustand zurücksetzen, im nächsten Frame bleiben wir stehen. | ||
self.__walking = False | self.__walking = False | ||
− | + | ||
def handleInput(self, key): | def handleInput(self, key): | ||
+ | # Linke Pfeiltaste wird gedrückt: | ||
if key == pygame.K_LEFT: | if key == pygame.K_LEFT: | ||
+ | # x-Position der Spielfigur anpassen, | ||
+ | # die Blickrichtung festlegen | ||
+ | # und den Laufen-Zustand einschalten. | ||
self.__posX -= 1 | self.__posX -= 1 | ||
self.__dir = -1 | self.__dir = -1 | ||
self.__walking = True | self.__walking = True | ||
+ | |||
+ | # Und nochmal für die rechte Pfeiltaste. | ||
if key == pygame.K_RIGHT: | if key == pygame.K_RIGHT: | ||
self.__posX += 1 | self.__posX += 1 | ||
Zeile 1.050: | Zeile 1.089: | ||
import pygame | import pygame | ||
− | |||
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): |
+ | # Wir erstellen ein neues Tileset. | ||
+ | # Hier im Tutorial fügen wir manuell vier Tile-Typen hinzu. | ||
self.__tileset = Tileset.Tileset("tileset.png", (255, 0, 255), 32, 32) | self.__tileset = Tileset.Tileset("tileset.png", (255, 0, 255), 32, 32) | ||
self.__tileset.addTile("grass", 0, 0) | self.__tileset.addTile("grass", 0, 0) | ||
− | self.__tileset.addTile("mud", 32, 0) | + | self.__tileset.addTile("mud", 32, 0) |
− | + | ||
− | + | ||
− | + | ||
self.__tileset.addTile("grass-mud", 0, 64) | self.__tileset.addTile("grass-mud", 0, 64) | ||
self.__tileset.addTile("empty", 0, 96) | self.__tileset.addTile("empty", 0, 96) | ||
+ | # Festlegen der Startposition der Kamera. Hier (0, 0). | ||
self.__cameraX = 0 | self.__cameraX = 0 | ||
self.__cameraY = 0 | self.__cameraY = 0 | ||
− | + | ||
− | self.__width = | + | # Die Größe der Maps in Tiles. |
+ | self.__width = 30 | ||
self.__height = 25 | self.__height = 25 | ||
+ | # Erstellen einer leeren Liste für die Tile Daten. | ||
self.__tiles = list() | self.__tiles = list() | ||
− | for i in range(0, self. | + | # Sehr einfache Karte basteln: |
+ | for i in range(0, self.__height): | ||
self.__tiles.append(list()) | self.__tiles.append(list()) | ||
− | for j in range(0, self. | + | for j in range(0, self.__width): |
if i == 14: | if i == 14: | ||
self.__tiles[i].append("grass") | self.__tiles[i].append("grass") | ||
Zeile 1.085: | Zeile 1.127: | ||
self.__tiles[i].append("empty") | self.__tiles[i].append("empty") | ||
+ | # Player-Objekt erstellen. | ||
self.__player = Player.Player() | self.__player = Player.Player() | ||
+ | # Hier rendern wir den sichtbaren Teil der Karte. | ||
def render(self, screen): | def render(self, screen): | ||
+ | # Zeilenweise durch die Tiles durchgehen. | ||
for y in range(0, int(screen.get_height() / self.__tileset.getTileHeight()) + 1): | for y in range(0, int(screen.get_height() / self.__tileset.getTileHeight()) + 1): | ||
+ | # Die Kamera Position mit einbeziehen. | ||
ty = y + self.__cameraY | ty = y + self.__cameraY | ||
if ty >= self.__height or ty < 0: | if ty >= self.__height or ty < 0: | ||
continue | continue | ||
− | + | # Die aktuelle Zeile zum einfacheren Zugriff speichern. | |
+ | line = self.__tiles[ty] | ||
+ | # Und jetzt spaltenweise die Tiles rendern. | ||
for x in range(0, int(screen.get_width() / self.__tileset.getTileWidth()) + 1): | for x in range(0, int(screen.get_width() / self.__tileset.getTileWidth()) + 1): | ||
+ | # Auch hier müssen wir die Kamera beachten. | ||
tx = x + self.__cameraX | tx = x + self.__cameraX | ||
if tx >= self.__width or tx < 0: | if tx >= self.__width or tx < 0: | ||
continue | continue | ||
− | tilename = | + | # Wir versuchen, die Daten des Tiles zu bekommen. |
+ | tilename = line[tx] | ||
tile = self.__tileset.getTile(tilename) | tile = self.__tileset.getTile(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.getImage(), (x * self.__tileset.getTileWidth(), y * self.__tileset.getTileHeight()), tile.getRect()) | ||
+ | |||
+ | # Und zuletzt den Player rendern. | ||
+ | self.__player.render(screen) | ||
− | |||
− | |||
− | def handleInput(self, key): | + | # Tastendrücke an den Player weiterreichen: |
+ | def handleInput(self, key): | ||
self.__player.handleInput(key) | self.__player.handleInput(key) | ||
+ | </sourcecode> | ||
+ | |||
+ | <tt>Tile_3.py</tt> | ||
+ | <sourcecode lang=python line> | ||
+ | # -*- coding: UTF-8 -*- | ||
+ | |||
+ | # Pygame Modul importieren. | ||
+ | import pygame | ||
+ | |||
+ | # Unser Tilemap Modul ebenfalls importieren. | ||
+ | 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)) | ||
+ | |||
+ | # 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() | ||
+ | |||
+ | # Wir erstellen eine Tilemap. | ||
+ | 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.handleInput(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> | ||
Version vom 29. Oktober 2011, 12:33 Uhr
Klicke hier, um diese Version anzusehen.