Auch in diesem Semester arbeiten wir wieder mit GitHub. Erstelle für die Aufgaben zu Pygame ein neues Repository mit Namen pygame_projects
und füge diesem die jeweiligen Aufträge hinzu. Gib dieses Repo wieder frei für den Benutze anschae.
Achtung: Pygame funktioniert unter Umständen nicht mit der aktuellsten Version von Python. Im Oktober 2020 sollte Python 3.8 am besten funktionieren (nicht getestet).
Auftrag:
C:\Users\<username>\AppData\Local\Programs\Python\Python37\python.exe
"python.pythonPath"
richtig setzt.Uniforme Bewegung bedeutet, dass man sich mit konstanter Geschwindigkeit fortbewegt.
Bewegt man sich während der Zeit $t$ mit der konstanten Geschwindigkeit $v$, so legt man folgende Distanz zurück: $$s = v t$$
Betrachtet man die Bewegung in 2 Dimensionen, so ist es am einfachsten, wenn man die Position und die Geschwindigkeiten als Vektor betrachtet:
$$\vec{s} = \begin{pmatrix}s_x \\ s_y \end{pmatrix}$$ $$\vec{v} = \begin{pmatrix}v_x \\ v_y \end{pmatrix}$$
Dabei nennt man …:
Ein Vektor hat immer sowohl eine Richtung als auch eine Länge. Die Länge berechnet man mit dem Satz von Pythagoras: $$v = |\vec{v}| = \sqrt{v_x^2 + v_y^2}$$
Wie beschreibt man nun die Bewegung eines Objekts in 2 Dimensionen? Dieses Objekt befinde sich zum Zeitpunkt $t=0$ am Ort $\vec{s_0} = \begin{pmatrix}s_x^0 \\ s_y^0 \end{pmatrix}$ und es bewege sich mit der Geschwindigkeit $\vec{v_0} = \begin{pmatrix}v_x^0 \\ v_y^0 \end{pmatrix}$. Die Position des Objekts ist dann gegeben durch:
$$\vec{s}(t) = \vec{s_0} + \vec{v_0}t$$
Beachte, dass die Position eine Funktion der Zeit ist: Je länger sich das bewegt, desto weiter ist es von seiner ursprünglichen Position $\vec{s}_0$ entfernt.
Machen wir ein Beispiel.
An welcher Position befindet man sich nach der $1$ Zeiteinheit , $2$ Zeiteinheiten oder $60$ Zeiteinheiten? Die Position ist gegeben durch: $$\vec{s}(t) = \vec{s_0} + \vec{v_0}t = \begin{pmatrix}5 \\ 2 \end{pmatrix} + \begin{pmatrix}-2 \\ 4 \end{pmatrix}t = \begin{pmatrix}5 - 2 t \\2 - 4 t \end{pmatrix}$$
Nach der Zeiteinheit $1$ befindet man sich also an der Position $\vec{s}(1) = \begin{pmatrix}3 \\-2 \end{pmatrix}$
Numpy ist eine Python-Library, die dafür konzipiert wurde, Mathematik zu betreiben. Die darin vorhandenen Numpy-Arrays sind dafür geeignet, Vektoren in Python darzustellen.
Das Erstellen eines solchen Numpy-Arrays geht ganz einfach: Nimm eine normale Liste L
und wandle diese in ein solches Array um: np.array(L)
. Wichtig: Importiere dazu zuerst numpy wie folgt: import numpy as np
.
Das oben aufgeführte Beispiel kann man in Numpy wie folgt aufsetzen:
s0 = np.array([5,2]) v0 = np.array([-2,4]) t = 6 s = s0 + v0*t
Seien v1
und v2
die Geschwindigkeitsvektoren kollidierender Bälle bevor sie kollidieren. Welche Geschwindigkeitsvektoren haben sie nach der Kollision? Diese sind gegeben durch eine Formel, welche du im folgenden Dossier findest: Dossier Kollision. Eine solche Kollision heisst elastische Kollision oder elastischer Stoss.
Tipp: Am einfachsten arbeitet man mit Numpy-Arrays (z.B. v1 = np.array([3,-7])
). Dies macht die Berechnung der neuen Geschwindigkeitsvektoren relativ einfach.
Quelle: https://en.wikipedia.org/wiki/Elastic_collision#Two-dimensional (letzte beiden Formeln des Anschnitts)
play()
-Methode und gehe Schritt-für-Schritt durch den Code. Es ist wichtig, dass du verstehst, was in jedem Schritt passiert!Zusatzaufgaben:
pygame.sprite.Sprite
erben. Damit macht man sich, z.B. bei Kollisionen, das Leben sehr viel einfacher.Balls = pygame.sprite.Group()
). Dann kann die Position aller Bälle mit einem Kommando updated werden: Balls.update()Dieser Code dient als Startpunkt:
import os import pygame from PIL import Image, ImageDraw ### CONSTANTS ### # Windows WIN_HEIGHT = 800 WIN_WIDTH = 500 # Framerate FPS = 30 TIME_DELAY = int(1000 / FPS) # Colors WHITE = (255, 255, 255) BLACK = (0, 0, 0) RED = (255, 0, 0) GREEN = (0, 255, 0) BLUE = (0, 0, 255) BG_COLOR = GREEN ### METHODS ### def draw_bmp_circle(radius, color,path): """ creates circle image and saves on disk """ img = Image.new('RGBA', (radius,radius), (255, 0, 0, 0)) draw = ImageDraw.Draw(img) draw.ellipse((0,0,radius-1,radius-1), fill=color) img.save(path, 'PNG') ### CLASSES ### class Ball(pygame.sprite.Sprite): def __init__(self, xy_center, v, radius, mass, color): super().__init__() # call __init__ of parent class (i.e. of pygame.sprite.Sprite) # CREATE BALL IMAGE (if does not exist yet) file_path = f"data/ball_r={radius}_col={str(color).replace(' ','').replace(',','_')}.png" if not os.path.isfile(file_path): draw_bmp_circle(radius,color,file_path) # draw image and save on disk if doesnt exist yet # ASSIGN CLASS ATTRIBUTES self.image = pygame.image.load(file_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.mass = mass # is relevant for realistic collisions self.color = color self.vx = v[0] # velocity self.vy = v[1] self.mask = pygame.mask.from_surface(self.image) # creates a mask, used for collision detection (see manual about pygame.sprite.collide_mask()) def update(self): """ determines motion of particles: """ # TODO: # 1. General motion of particles # 2. Bouncing off walls # do NOT deal with collisions in here def collide(self,ball): """ ball self collides with other ball, given as argument this method updates velocities of BOTH balls """ # TODO # 1. two balls (self and ball) collide -> deal with collision # Theory of collision btw two balls: https://en.wikipedia.org/wiki/Elastic_collision#Two-dimensional (last formula) # 2. try to avoid 'ball dances' 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 play(self): # CREATE GAME GROUPS Balls = pygame.sprite.Group() # CREATE OBJECTS AND ASSIGN TO GROUPS balls_list = [ Ball([0.25*WIN_WIDTH, 0.75*WIN_HEIGHT], [ -4, 7], 100, 1, BLUE) ,Ball([0.25*WIN_WIDTH, 0.50*WIN_HEIGHT], [ -5, -10], 100, 1, BLACK) ,Ball([0.50*WIN_WIDTH, 0.75*WIN_HEIGHT], [ 7, -7], 100, 1, WHITE) ,Ball([0.50*WIN_WIDTH, 0.25*WIN_HEIGHT], [ -4, 7], 100, 1, RED) ] 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: run = False keys = pygame.key.get_pressed() if keys[pygame.K_ESCAPE]: self.exit() self.screen.fill(BG_COLOR) # draw empty screen # 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. # DRAW Balls.draw(self.screen) # UPDATE Balls.update() pygame.display.update() pygame.quit() if __name__ == "__main__": Game().play()
Eine Beschleunigung ist eine Änderung der Geschwindigkeit pro Zeit
$$a = \frac{\Delta v}{t}$$
wobei:
Eine Uniforme Beschleunigung ist einfach eine konstante Beschleunigung. Die Beschleunigung ist also immer gleich stark und verändert sich nicht mit der Zeit.
Bewegung entlang der $x-$Achse:
Nachdem für die Zeit $t$ mit $a$ beschleunigt wurde, befindet man sich an der Position $s$ und hat Geschwindigkeit $v$, gegeben durch:
$$v(t) = v_0 + a t$$ $$s(t) = s_0 + v_0 t + \frac12 a t^2$$
Ein Objekt befindet sich zum Zeitpunkt $t=0$ am Ort $\vec{s_0} = \begin{pmatrix}s_x^0 \\ s_y^0 \end{pmatrix}$ und bewegt sich mit der Geschwindigkeit $\vec{v_0} = \begin{pmatrix}v_x^0 \\ v_y^0 \end{pmatrix}$. Das Objekte werde jetzt durch den Beschleunigungsvektor $\vec{a} = \begin{pmatrix}a_x \\ a_y \end{pmatrix}$ beschleunigt. Dann verändern sich Geschwindigkeit und Position wie folgt mit der Zeit:
$$\vec{v}(t) = \vec{v_0} + \vec{a}t$$ $$\vec{s}(t) = \vec{s_0} + \vec{v_0}t + \frac12 \vec{a} t^2$$
Lässt man etwas fallen, so wird es mit der Beschleunigung $g = 9.81\text{m}/\text{s}^2$ nach unten beschleunigt. Da diese Beschleunigung sehr wichtig ist, hat sie einen eigenen Namen erhalten und man schreibt typischerweise $g$ anstelle von $a$. Auf der Erdoberfläche ist die Gravitationsbeschleunigung in etwa konstant, weshalb man guten Gewissens bei der Gravitation von ein uniformen Beschleunigung sprechen kann.
In 2 Dimensionen beschreibt man die Gravitation mit dem folgenden Vektor: $$\vec{g} = \begin{pmatrix}0 \\ - 9.81\text{m}/\text{s}^2 \end{pmatrix}$$
Beachte: Hier wird die übliche Konvention in Physik und Mathematik verwendet, dass nach oben durch die positive $y-$Richtung dargestellt wird. In Pygame ist dies anders. Dort sollte der Gravitationsvektor z.B. wie folgt aussehen: g = np.array([0,1])
. Beachte, dass der genaue Zahlenwert nicht relevant ist und durch ausprobieren eingestellt werden sollte.
Ganz wichtig: Eine Änderung der Geschwindigkeit in eine Richtung tritt nur dann auf, wenn in diese Richtung eine Beschleunigung wirkt. Beispiele:
Mache eine Kopie der letzten Aufgabe und benenne das File um in bouncing_balls_v02.py.
Füge deinem Programm Gravitation hinzu.
update()
Methode der Ball-Klasse: Füge Gravitation hinzu. Bei jedem Update soll nun ein Ball nach unten beschleunigt werden.Programmiere ein einfaches Spiel (ähnlich zu Jumping Dagobert), welches unten aufgeführte Eigenschaften haben soll.
Ideensammlung Game Mode:
Nutze dein Programm jump_game_v01.py als Startpunkt und speichere es ab unter jump_game_v02.py. Erweitere den Code nun so, dass du ein richtiges Spiel erhälst.
Dein Spiel soll animierte Sprites beinhalten
Erstelle Pixel Art (inkl. Animationen) mit Piskel: https://www.piskelapp.com/
Schaue dazu das Tutorial: https://www.youtube.com/watch?v=lJN2C7-dyxE&list=PLO3K3VFvlU6Akj3W29_nMLZFnwNOVbAzI&index=1
Auftrag in Kürze: Programmiert in 1er oder 2er Gruppen ein Retro Game im Jump 'n Run Stil.
Auftrag in Detail:
Ideen:
Lade Bild und lasse alle weissen Bereiche transparent werden:
im = pygame.image.load('fig.png').convert() im.set_colorkey((255,255,255))
Gratis online Programm für Pixelart: https://www.piskelapp.com/