PyGame 2021
Setup
Auftrag 0A: PyGame einrichten
Installiere PyGame. Achtung: Typischerweise funktioniert dieses nicht mit der aktuellsten Python Version. Stand Januar 2021 läuft es sicher mit Python 3.78. Benötigt man für verschiedene Projekte verschiedene Installationen und verschiedene Versionen von Python, so lohnt sich die Verwendung von Virtual Environments. Verwende pip um Module wie PyGame zu installieren.
Recherchiere, was Virtual Environments sind, und wie man mit diesen arbeitet. Erstelle dann ein neues für Pygame Projekte und installiere darin (mit pip) PyGame.
Quellen:
- Seite „Python Setup“ in diesem Wiki
- Selber googlen macht den Meister!
Auftrag 0B: GitHub einrichten
- Erstelle einen GitHub Account, falls du noch keinen hast.
- Erstelle ein neues Repo
pygame_exercises
- Gib dieses deinem Lehrer frei (Name: anschae)
- ALLE Übungen, die du hier erledigst, müssen auf GitHub gestellt werden. Commit und pushe immer, nachdem du an einer Übung gearbeitet hast. Mache dies unaufgefordert!
Konventionen
- Für unterschiedliche Programmiersprachen gibt es unterschiedliche Konventionen dazu, wie man Variablen, Klassen usw. benennt. Zum Beispiel sollten Klassen im
UpperCamelCase
Stil und Variablen imsnake_case
Stil notiert werden. Studiere für weitere Infos den folgenden Convention Guide: https://www.fcodelabs.com/2018/12/03/Python-Coding-Standard/ - Pfade von Ordner und Dokumente werden in Windows (
myfolder\\myfile.txt
) anders angegeben als in MacOS (myfolder/myfile.txt
). Damit dein Code auf sämtlichen Plattformen funktioniert, musst du also solche Pfadangaben vermeiden. Stattdessen sollst du das os Modul verwenden:
import os.path path = os.path.join("myfolder", "myfile.txt")
Auftrag 1: Billard
Programmiere ein Spiel, welches Ähnlichkeiten mit dem Spiel Billard hat. Es soll schlussendlich beinhalten:
- Eine Kugel Player, welche man in eine beliebige Richtung mit einer bestimmten Geschwindigkeit abschiessen kann.
- Dazu musst du die ein System überlegen, wie du dies umsetzen möchtest: Pfeiltasten, Maus, …
- Mehrere weitere Kugeln, welche miteinander und mit dem Player kollidieren können. Die Kollisionen sollen physikalisch korrekt ablaufen.
- Auch an den Wänden sollen die Kollisionen korrekt ablaufen.
- Die Kugeln sollen wieder zum Stillstand kommen. Es muss also Reibung (quasi Luftwiderstand) eingebaut werden.
- Überlege dir ein Spielsystem und ein Ziel, z.B. durch das Versenken von Kugeln.
- Es kann ein One Player Game oder ein Two Player Game (abwechselnd) sein.
- Sei kreativ!
Alles was du an Mathematik und Physik wissen musst, findest du hier: Vektoren, Kinematik & Python
Version 0: Template
Dir wird ein Template (siehe ZIP-File unten) zur Verfügung gestellt, welches dir als Grundlage dienen soll. Erstelle wie weiter oben erklärt ein GitHub Repo für diesen Auftrag und füge den Inhalt des ZIPs hinzu.
Verschaffe dir nun einen Überblick über den Code. Setze dazu einen Breakpoint ganz zu Beginn der play()
-Methode und gehe Schritt-für-Schritt durch den Code. Es ist wichtig, dass du verstehst, was in jedem Schritt passiert! Konsultiere Regelmässig das Manual https://www.pygame.org/docs/, um mehr über die verschiednen Features von PyGame zu lernen!
Version 1: Kollision an Wänden
Gib dem Player eine Anfangsgeschwindigkeit. Implementiere die Kollisionen an den Wänden. Es soll noch keine Reibung vorkommen, d.h., die Geschwindigkeit muss konstant bleiben! Die Bewegung des Players ist also eine uniforme (gleichförmige) Bewegung in 2D.
Studiere zuerst die Theorie zur uniformen Bewegung und zu Vektoren mit Numpy
Version 2: Reibung
Mache eine Kopie der ersten Version des Spiels und benenne diese billard_v02.py
.
Implementiere Reibung. Ziehe dazu in jedem Schritt einen bestimmten, immer gleich bleibenden Wert vom Betrag der Geschwindigkeit ab.
Version 3: Ball schiessen
File name: billard_03.py
(Mache dies für jede Version, wird dann nicht mehr explizit erwähnt.
Der Player soll nun in eine beliebige Richtung mit einer bestimmten Geschwindigkeit abgeschossen werden können. Überlege dir zuerst, wie du das machen möchtest und setze es dann um.
Version 4: Weitere Bälle
Füge weitere Bälle hinzu. Sämtliche Kollisionen sollen physikalisch korrekt sein. Studiere dazu die Seite Kollisionen und insbesondere das PDF dort.
Version 5: Spielsystem
Überlege die ein Spielsystem und setze es um.
Version 6: Feinschliff
Der Spielstand soll angezeigt angezeigt werden. Was das ist, hängt vom Spiel ab: Gesammelte Punkte, verstrichene Zeit, …
Verschönere nun dein Spiel. Es muss enthalten:
- schöne Grafiken (Background, …)
- Animationen, z.B. wenn man einen Ball versenkt
- Sound (Hintergrundmusik, Soundeffekte)
Auftrag 2: Autoscooter
Programmiere das Spiel Autoscooter, welches wie folgt funktionieren soll:
- Steuerung: Der Spieler steuert einen Sprite (Player) in Form eines Balles. Für die Steuerung gibt es mehrere Optionen:
- direkt mit Tastatur steuern
- Der Player bewegt sich immer vorwärts. Mit der Tastatur kann man dann Impulse geben, um die Richtung zu ändern.
- irgendwie mit der Maus …
- …
- Im Spielfeld gibt es eine Anzahl Bälle, die per Zufall platziert werden.
- Ziel des Spiels: in möglichst kurzer Zeit alle Bälle eliminieren!
- Zu Beginn sind alle Bälle grün. Nach einer ersten Kollision mit dem Player oder einem anderen Ball wird ein Ball orange, nach einer weiteren Kollision rot und nach einer weiteren Kollision explodiert der Ball und verschwindet.
- An den Wänden soll der Ball abprallen, dies wird nicht als Kollision gewertet.
- Kollisionen und das abprallen an den Wänden soll nach den Gesetzen der Physik ablaufen.
- Die Bälle können etwas unterschiedliche Massen haben.
- Die aktuelle Spielzeit soll angezeigt werden.
- Der Highscore soll in einem File gespeichert und angezeigt werden.
Teil 1: Template
Dir wird ein Template (siehe ZIP-File unten) zur Verfügung gestellt, welches dir als Grundlage dienen soll. Erstelle wie weiter oben erklärt ein GitHub Repo für diesen Auftrag und füge das Template hinzu.
Verschaffe dir nun einen Überblick über den Code. Setze dazu einen Breakpoint ganz zu Beginn der play()
-Methode und gehe Schritt-für-Schritt durch den Code. Es ist wichtig, dass du verstehst, was in jedem Schritt passiert! Konsultiere Regelmässig das Manual https://www.pygame.org/docs/, um mehr über die verschiednen Features von PyGame zu lernen!
- autoscooter_template.py
import os import sys import pygame from PIL import Image, ImageDraw from pathlib import Path print(f"python executable: {sys.executable}") ### CONSTANTS ### # Convention: use capital letters for constants (= variables you leave unchanged) # Windows WIN_HEIGHT = 800 WIN_WIDTH = 500 # Define colors WHITE = (255, 255, 255) BLACK = (0, 0, 0) RED = (255, 0, 0) GREEN = (0, 255, 0) BLUE = (0, 0, 255) BG_COLOR = WHITE # Framerate FPS = 30 TIME_DELAY = int(1000 / FPS) # Game Constants SPEED = 12 ### FUNCTIONS ### ### CLASSES ### class Player(pygame.sprite.Sprite): def __init__(self, img_path, xy_center, v, mass): super().__init__() # call __init__ of parent class (i.e. of pygame.sprite.Sprite) # ASSIGN CLASS ATTRIBUTES if not os.path.exists(img_path): raise Exception("THE FOLLOWING FILE DOES NOT EXIST: {0}".format(img_path)) self.image = pygame.image.load(str(img_path)) # load image self.rect = self.image.get_rect() # create rectangle containing ball image self.rect.center = (int(xy_center[0]),int(xy_center[1])) # set center coords of ball self.mask = pygame.mask.from_surface(self.image) # creates a mask, used for collision detection (see manual about pygame.sprite.collide_mask()) self.mass = mass # give sprite a mass -> realistic collisions def update(self): # in each step, this method is called if self.rect.left <= 0: self.rect.left = 0 elif self.rect.right >= WIN_WIDTH: self.rect.right = WIN_WIDTH if self.rect.top <= 0: self.rect.top = 0 elif self.rect.bottom >= WIN_HEIGHT: self.rect.bottom = WIN_HEIGHT def collide(self,ball): pass class Ball(pygame.sprite.Sprite): """ Class for balls, derive from pygame's sprite class -> makes your life easier since you can use e.g. the collision detection of the sprite class """ def __init__(self, img_path, xy_center, v, mass): super().__init__() # call __init__ of parent class (i.e. of pygame.sprite.Sprite) # ASSIGN CLASS ATTRIBUTES if not os.path.exists(img_path): # check if folder of image exists raise Exception("THE FOLLOWING FILE DOES NOT EXIST: {0}".format(img_path)) self.image = pygame.image.load(str(img_path)) # load image self.rect = self.image.get_rect() # create rectangle containing ball image self.rect.center = (int(xy_center[0]),int(xy_center[1])) # set center coords of ball self.mask = pygame.mask.from_surface(self.image) # creates a mask, used for collision detection (see manual about pygame.sprite.collide_mask()) self.mass = mass # is relevant for realistic collisions def update(self): """ - update function gets executed in every step - determines motion of ball """ pass def collide(self,ball): """ ball self collides with other ball, given as argument this method updates velocities of BOTH balls """ pass class Game: """ Main GAME class """ def __init__(self): pygame.init() pygame.font.init() self.time_delay = TIME_DELAY self.screen = pygame.display.set_mode((WIN_WIDTH, WIN_HEIGHT)) # create screen which will display everything self.win = pygame.display.set_mode((WIN_WIDTH, WIN_HEIGHT)) pygame.display.set_caption("Bouncing Balls") # Game title def quit(self): pygame.quit() sys.exit(0) def play(self): # CREATE PLAYER player = Player(os.path.join("data","ball_r=100_col=0_255_0.png"),[150,550],[0,0],1) # CREATE GAME GROUPS Balls = pygame.sprite.Group() # CREATE OBJECTS AND ASSIGN TO GROUPS balls_list = [ Ball(os.path.join("data","ball_r=100_col=0_0_255.png"),[100,200],[0,0],1), Ball(os.path.join("data","ball_r=100_col=0_0_255.png"),[200,400],[0,0],1) ] Balls.add(balls_list) # GAME PERMANENT LOOP while True: pygame.time.delay(TIME_DELAY) # KEY EVENTS for event in pygame.event.get(): # Exit app if click quit button if event.type == pygame.QUIT: self.quit() # Naviation of player keys = pygame.key.get_pressed() if keys[pygame.K_UP]: player.rect.top -= SPEED # and so on ... # COLLISION DETECTION # see manual for all types of collisions: https://www.pygame.org/docs/ref/sprite.html#pygame.sprite.spritecollide # TODO: check for collisions between any two balls. If there is any, call the collision() method of the Ball class. for ball in balls_list: pass # UPDATE Balls.update() player.update() # DRAW self.screen.fill(BG_COLOR) # draw empty screen Balls.draw(self.screen) self.screen.blit(player.image, player.rect) pygame.display.update() pygame.quit() if __name__ == "__main__": Game().play()
Teil 2: Steuereung
Implementiere nun eine Steuerung für den Player.
Auftrag 3: Eigenes Projekt
Auftrag
Programmiere mit PyGame ein eigenes Game, welches folgende Eigenschaften haben muss:
- Originale (und originelle) Idee, also kein billiger Abklatsch von existierende Game. Natürlich darf man sich inspirieren lassen.
- Sauber programmiert:
- strikt objektorientiert (was zu Objekt gehört, kommt in entsprechende Klasse)
- Klar und sauber strukturiert. Z.B. Permanent-Loop sollte wie folgt strukturiert sein: Time delay / Key Events / Collision handling / Update / Draw / pygame.display.update()
- sauber und relativ ausführlich kommentiert
- Game-Balancing:
- Spiel ist so designed, dass der Spieler dieses gerne und längerfristig spielt.
- Dazu müssen dem Spieler Anreize gegeben werden: Schwierigkeitsgrad erhöht sich, neue Levels, besseres Gear, …
- Schwierigkeitsgrad ist so gewählt, dass Spiele nicht überfordert, aber auch nicht gelangweilt ist.
- Assets:
- enthält Animationen, Sounds und ansprechende Grafiken
- sämtliche Animationen werden komplett selbst gefertigt
- Soundeffekte werden nach Möglichkeit selbst aufgenommen
- Sound und Soundeffekte dürfen aus dem Internet genommen werden, solange sie royalty-free sind.
- Game-Testing:
- Game wird fleissig getestet. Nicht nur von sich selbst, aber auch von KollegInnen. Am besten von erfahrenen und unerfahrenen GamerInnen
- keine Bugs mehr
- Keine Errors, auch wenn Spieler komische, unvorhergesehene Dinge tut. Fehler werden abgefangen.
Ablauf
- Spiel ausdenken, grobe Skizze
- Detaillierter Projektbeschrieb (siehe unten)
- … und mit LP besprechen
- Programmier-Phase
- Test-Phase
- Feinschliff
Projektbeschrieb
- Kurzbeschrieb Game: Um was geht es?
- Klassen:
- Welche Klassen werden benötigt?
- Welche wichtigen Attribute und Methoden sollen diese haben?
- Assets:
- welche Assets (Bilder, Animationen, Sounds) werden benötigt
- zeichne von Hand grobe Skizzen der wichtigsten graphischen Assets
- Erwartet Hauptschwierigkeiten: Wo erwartest du die grössten Schwierigkeiten?
- Zeitplan: Erstelle einen relativ detaillierten Zeitplan, wann du was erledigen möchtest.