# Python 11 (PyGame)

# Python 11 (PyGame)
Photo by Lorenzo Herrera / Unsplash

PyGame Zero to biblioteka do tworzenia gier w języku Python, stworzona z myślą o osobach początkujących, a zwłaszcza o zastosowaniach edukacyjnych. Jej głównym celem jest umożliwienie szybkiego rozpoczęcia pracy nad własnymi grami bez konieczności znajomości skomplikowanych mechanizmów programistycznych.

Dokumentacja: https://pygame-zero.readthedocs.io/en/stable/

Instalacja w terminalu: pip install pgzero

Podstawowa struktura gry:

import pgzrun
from pygame import display
from pgzero import screen as pgzero_screen
from pgzero.builtins import keyboard

# rozmiar okna - pozniej wrzucimy to do osobnego pliku
WIDTH = 800
HEIGHT = 600


screen = pgzero_screen.Screen(display.set_mode((WIDTH, HEIGHT), 0))\


def update():
    # tu aktualizujemy stan obiektow
    pass

def draw():
    # tu je rysujemy
    pass

# wywołanie pętli gry
pgzrun.go()
Niezbędne funkcje i klasy
  • draw() — funkcja rysująca zawartość ekranu
  • update() — aktualizuje logikę gry
  • Klasa Actor - reprezentuje obiekt graficzny — np. postać, przycisk czy tło. Actor jest powiązany z obrazem (.png) w folderze images/. Pojawia się wszędzie gdzie obiekty wchodzą z sobą w interakcje. Zapewnia obsługę kolizji.

Każdy Actor ma wbudowany prostokąt , który określa jego rozmiar. Metoda colliderect() sprawdza, czy prostokąt jednego aktora nachodzi na prostokąt drugiego

Gra pong:

Main

import pgzrun
import pygame
from pygame import display
from pgzero import screen as pgzero_screen
from pgzero.builtins import keyboard
from stale import *
from paletka import Paletka
from pilka import Pilka
from tablica import TablicaPunktow

# rozmiar okna - pozniej wrzucimy to do osobnego pliku

screen = pgzero_screen.Screen(display.set_mode((WIDTH, HEIGHT), 0))
paletka1 = Paletka("paletka", 10, HEIGHT // 2)
paletka2 = Paletka("paletka", WIDTH - 10, HEIGHT // 2)
pilka = Pilka(WIDTH // 2, HEIGHT // 2)
tablica = TablicaPunktow(380, 10)

# pygame uruchamia pętlę gry automatycznie
# ... ale potrzebuje do tego odpowiednio nazwanych funkcji.
# Korzystamy z funkcji update i draw.

def sterowanie():
    if keyboard.w:
        paletka1.przesun_gora()
    if keyboard.s:
        paletka1.przesun_dol()
    if keyboard.UP:
        paletka2.przesun_gora()
    if keyboard.DOWN:
        paletka2.przesun_dol()

def update():
    sterowanie()
    tablica.dodaj_pkt(pilka)
    pilka.przesun(paletka1, paletka2)


def draw():
    screen.clear()
    paletka1.draw()
    paletka2.draw()
    screen.draw.text(
        f"{tablica.licznik_l} : {tablica.licznik_p}",
        (tablica.x, tablica.y),
        fontsize=32,
    )
    pilka.draw()

# wywołanie pętli gry
pgzrun.go()

Rozkładamy kod na części pierwsze:

  1. Importy i inicjalizacja naszego kodu pong:
import pgzrun
import pygame
from pygame import display
from pgzero import screen as pgzero_screen
from pgzero.builtins import keyboard
  • pgzrun - moduł do uruchamiania gier Pygame Zero
  • pygame i display - podstawowe moduły Pygame
  • pgzero_screen - system ekranu z Pygame Zero
  • keyboard - obsługa klawiatury
from stale import *
from paletka import Paletka
from pilka import Pilka
from tablica import TablicaPunktow
  1. Import z osobnych plików .py (Ważne - muszą być w tym samym folderze):
  • stale.py - zawiera WIDTH = 800 HEIGHT = 600 ekranu.
  • paletka.py - klasa reprezentująca paletki graczy (paletka1 i paletka2)
  • pilka.py - klasa piłki
  • tablica.py - klasa tablicy do liczenia punktów
  1. Konfiguracja gry
#1
screen = pgzero_screen.Screen(display.set_mode((WIDTH, HEIGHT), 0))

Tworzy okno gry o rozmiarach WIDTH x HEIGHT; Normalnie w Pygame Zero jest automatyczny obiekt screen dostępny globalnie.
Tutaj tworzymy własny obiekt Screen i przekazujemy mu surface z pygame.

Screen to klasa; Zero to parametr flags w funkcji pygame.display.set_mode(). Określa dodatkowe opcje dla okna gry:

  • 0 = standardowe okno (żadnych specjalnych flag). Inne opcje to np.:
    • pygame.FULLSCREEN - pełny ekran
    • pygame.RESIZABLE - okno o zmiennym rozmiarze
    • pygame.NOFRAME - okno bez ramki
    • pygame.DOUBLEBUF - podwójne buforowanie
#2
paletka1 = Paletka("paletka", 10, HEIGHT // 2)
paletka2 = Paletka("paletka", WIDTH - 10, HEIGHT // 2)
pilka = Pilka(WIDTH // 2, HEIGHT // 2)
tablica = TablicaPunktow(380, 10)

#paletka1 - lewa paletka (x=10, y=środek ekranu), string"paletka" odnosi się do pliku paletka.png, który jest W TYM SAMYM FOLDERZE CO PLIKI .PY GRY
#paletka2 - prawa paletka (x=WIDTH-10, y=środek ekranu)
#pilka - startuje w środku ekranu
#tablica - wynik na pozycji (380, 10)
UWAGA! Układ współrzędnych ekranu wygląda tak:
  1. Funkcje gry

STEROWANIE:

def sterowanie():
    if keyboard.w: paletka1.przesun_gora()
    if keyboard.s: paletka1.przesun_dol()
    if keyboard.UP: paletka2.przesun_gora()
    if keyboard.DOWN: paletka2.przesun_dol()

#Gracz 1: W/S (lewa paletka)
#Gracz 2: strzałki góra/dół (prawa paletka)

Update (LOGIKA GRY):

def update():
    sterowanie()
    tablica.dodaj_pkt(pilka)
    pilka.przesun(paletka1, paletka2)
# Obsługuje sterowanie
# Sprawdza czy zdobyto punkt
# Przesuwa piłkę (z kolizjami z paletkami)

Draw (rysowanie):

def draw():
    screen.clear() #czyści ekran
    paletka1.draw() #rysuje paletke1
    paletka2.draw() #rysuje paletkę2
    screen.draw.text(f"{tablica.licznik_l} : {tablica.licznik_p}", (tablica.x, tablica.y), fontsize=32) #rysuje tablicę wyników
    pilka.draw() #rysuje piłkę
pgzrun.go()

#Startuje główną pętlę gry.

Plik Paletka

from stale import *
from pgzero.builtins import Actor


class Paletka(Actor):
    def __init__(self, nazwa_obrazka: str, wsp_x: int, wsp_y: int):
        # przekazujemy to do klasy rodzica
        # korzystając z funkcji super nie dodajemy w poniższym __init__ słowa self
        super().__init__(nazwa_obrazka, (wsp_x, wsp_y))
        # self trzeba by było przekazać jeśli do inicjalizowania
        # atrybutów z klasy rodzica użylibyśmy
        # Actor.__init__()
        self.predkosc_y = 5

    def przesun_gora(self):
        if self.y > 0:
            self.y -= self.predkosc_y

    def przesun_dol(self):
        if self.y < HEIGHT:
            self.y += self.predkosc_y

Rozkładamy kod na części pierwsze:

from pgzero.builtins import Actor
class Paletka(Actor):

#Paletka dziedziczy z Actor - to klasa Pygame Zero do obsługi 
#sprite'ów (obiektów graficznych). Actor automatycznie daje:
#Ładowanie obrazków
#Pozycjonowanie (x, y)
#Metodę draw() do rysowania
#Detekcję kolizji

    def __init__(self, nazwa_obrazka: str, wsp_x: int, wsp_y: int):
        super().__init__(nazwa_obrazka, (wsp_x, wsp_y))
        self.predkosc_y = 5

Atrybuty klasy:

  • nazwa_obrazka - nazwa pliku graficznego (np. "paletka.png")
  • (wsp_x, wsp_y) - pozycja startowa paletki
  • self.predkosc_y = 5 - prędkość ruchu paletki

Metody ruchu:

def przesun_gora(self):
    if self.y > 0:
        self.y -= self.predkosc_y

def przesun_dol(self):
    if self.y < HEIGHT:
        self.y += self.predkosc_y

przesun_gora():

  • Sprawdza czy paletka nie jest przy górnej krawędzi (self.y > 0)
  • Przesuwa w górę o predkosc_y pikseli

przesun_dol():

  • Sprawdza czy paletka nie jest przy dolnej krawędzi (self.y < HEIGHT)
  • Przesuwa w dół o predkosc_y pikseli

Plik pilka

from stale import *
from pgzero.builtins import Actor

class Pilka(Actor):
    def __init__(self, x:int, y:int) -> None:
        super().__init__("pilka", (x, y))
        self.predkosc_x = 3
        self.predkosc_y = 3

    def przesun(self, paletka1, paletka2):
        self.x += self.predkosc_x
        self.y += self.predkosc_y
        if self.x > WIDTH or self.x < 0:
            self.predkosc_x *= -1
        if self.y > HEIGHT or self.y < 0:
            self.predkosc_y *= -1
        if self.colliderect(paletka1) or self.colliderect(paletka2):
            self.predkosc_x *= -1

Konstruktor:

  • Dziedziczy z Actor (jak paletka)
  • Ładuje obrazek "pilka"
  • Ustawia pozycję startową (x, y)
  • predkosc_x = 3 - ruch w poziomie (prawo: +, lewo: -)
  • predkosc_y = 3 - ruch w pionie (dół: +, góra: -)

Metoda przesun() - główna logika piłki:

def przesun(self, paletka1, paletka2): ->Przyjmuje obie paletki jako parametry do sprawdzania kolizji.

self.x += self.predkosc_x

self.y += self.predkosc_y -> Przesuwa piłkę o wartość prędkości w obu osiach.

Odbicia od ścian góra/dół i prawo/lewo:

if self.x > WIDTH or self.x < 0:
    self.predkosc_x *= -1
if self.y > HEIGHT or self.y < 0:
    self.predkosc_y *= -1

# odbicia od ścian 
#Ściany boczne: gdy piłka wyjdzie poza WIDTH lub x<0 → odwraca kierunek #poziomy
#Ściany góra/dół: gdy piłka wyjdzie poza HEIGHT lub y<0 → odwraca kierunek #pionowy

Kolizje z paletką:

if self.colliderect(paletka1) or self.colliderect(paletka2):
    self.predkosc_x *= -1
  • colliderect() - metoda z Actor sprawdzająca kolizję prostokątów
  • Gdy piłka uderzy w którąkolwiek paletkę → odwraca kierunek poziomy

Plik tablica

from stale import *

class TablicaPunktow():
    def __init__(self, wsp_x: int, wsp_y: int):
        self.x = wsp_x
        self.y = wsp_y
        self.licznik_p = 0
        self.licznik_l = 0

    def dodaj_pkt(self, pilka):
        if pilka.x <= 0:
            self.licznik_p += 1
        if pilka.x >= WIDTH:
            self.licznik_l += 1

Logika punktacji:

  • Gdy piłka wyleci za lewą ścianę (x <= 0) → prawy gracz dostaje punkt
  • Gdy piłka wyleci za prawą ścianę (x >= WIDTH) → lewy gracz dostaje punkt

Plik stale

WIDTH = 800   # szerokość okna
HEIGHT = 600  # wysokość okna

#Okno gry ma rozmiar 800x600 pikseli.

i pliki graficzne

paletka.png

pilka.png