4.1. Pong (str)¶
Wersja strukturalna klasycznej gry w odbijanie piłeczki zrealizowana z użyciem biblioteki PyGame.

4.1.1. Pole gry¶
Tworzymy plik pong_str.py
w terminalu lub w wybranym edytorze, zapisujemy na dysku
i wprowadzamy poniższy kod:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | #! /usr/bin/env python2
# -*- coding: utf-8 -*-
import pygame
import sys
from pygame.locals import *
# inicjacja modułu pygame
pygame.init()
# szerokość i wysokość okna gry
OKNOGRY_SZER = 800
OKNOGRY_WYS = 400
# kolor okna gry, składowe RGB zapisane w tupli
LT_BLUE = (230, 255, 255)
# powierzchnia do rysowania, czyli inicjacja pola gry
oknogry = pygame.display.set_mode((OKNOGRY_SZER, OKNOGRY_WYS), 0, 32)
# tytuł okna gry
pygame.display.set_caption('Prosty Pong')
# pętla główna programu
while True:
# obsługa zdarzeń generowanych przez gracza
for event in pygame.event.get():
# przechwyć zamknięcie okna
if event.type == QUIT:
pygame.quit()
sys.exit()
# rysowanie obiektów
oknogry.fill(LT_BLUE) # kolor okna gry
# zaktualizuj okno i wyświetl
pygame.display.update()
# KONIEC
|
Na początku importujemy wymagane biblioteki i inicjujemy moduł pygame
.
Dużymi literami zapisujemy nazwy zmiennych określające właściwości pola gry,
które inicjalizujemy w instrukcji pygame.display.set_mode()
.
Tworzy ona powierzchnię o wymiarach 800x400 pikseli i 32 bitowej głębi kolorów,
na której umieszczać będziemy pozostałe obiekty. W kolejnej instrukcji ustawiamy tytuł okna gry.
Programy interaktywne, w tym gry, reagujące na działania użytkownika, takie jak ruchy czy kliknięcia myszą, działają w tzw. głównej pętli, której zadaniem jest:
- przechwycenie i obsługa działań użytkownika, czyli tzw. zdarzeń (ruchy, kliknięcia myszą, naciśnięcie klawiszy),
- aktualizacja stanu gry (np. obliczanie przesunięć elementów) i rysowanie go.
Zadanie z punktu a) realizuje pętla for
, która odczytuje kolejne zdarzenia
zwracane przez metodę pygame.event.get()
. Za pomocą instrukcji warunkowych
możemy przechwytywać zdarzenia, które chcemy obsłużyć, np. naciśnięcie przycisku
zamknięcia okna: if event.type == QUIT
.
Instrukcja oknogry.fill(BLUE)
wypełnia okno zdefiniowanym kolorem.
Jego wyświetlenie następuje w poleceniu pygame.display.update()
.
Uruchom aplikację, wydając w terminalu polecenie:
~$ python pong_str.py
4.1.2. Paletka gracza¶
Planszę gry już mamy, pora umieścić na niej paletkę gracza. Poniższy kod wstawiamy przed pętlą główną programu:
22 23 24 25 26 27 28 29 30 31 32 33 | # paletka gracza #########################################################
PALETKA_SZER = 100 # szerokość
PALETKA_WYS = 20 # wysokość
BLUE = (0, 0, 255) # kolor wypełnienia
PALETKA_1_POZ = (350, 360) # początkowa pozycja zapisana w tupli
# utworzenie powierzchni paletki, wypełnienie jej kolorem,
paletka1 = pygame.Surface([PALETKA_SZER, PALETKA_WYS])
paletka1.fill(BLUE)
# ustawienie prostokąta zawierającego paletkę w początkowej pozycji
paletka1_prost = paletka1.get_rect()
paletka1_prost.x = PALETKA_1_POZ[0]
paletka1_prost.y = PALETKA_1_POZ[1]
|
Elementy graficzne tworzymy za pomocą polecenia
pygame.Surface((szerokosc, wysokosc), flagi, głębia)
.
Utworzony obiekt możemy wypełnić kolorem: .fill(kolor)
.
Położenie obiektu określimy pobierając na początku prostokątny obszar (Rect),
który go reprezentuje, metodą get_rect()
. Następnie podajemy współrzędne
x
i y
wyznaczające położenie w poziomie i pionie.
Informacja
- Początek układu współrzędnych w Pygame to lewy górny róg okna głównego.
- Położenie obiektu można ustawić również podając nazwane argumenty:
obiekt_prost = obiekt.get_rect(x = 350, y =350)
. - Położenie obiektów klasy
Rect
(prostokątów) możemy odczytwyać wykorzystując właściwości, takie jak:.x, .y, .centerx, .right, .left, .top, .bottom
.
Omówiony kod utworzy obiekt reprezentujący paletkę gracza, ale trzeba ją jeszcze
umieścić na planszy gry. W tym celu użyjemy metody .blit()
, która służy
rysowaniu jednego obrazka na drugim. Poniższy kod musimy wstawić w pętli głównej
przed instrukcją wyświetlającą okno.
47 48 | # narysuj w oknie gry paletki
oknogry.blit(paletka1, paletka1_prost)
|
Pozostaje uruchomienie kodu.
4.1.3. Ruch paletki¶
W pętli przechwytującej zdarzenia dopisujemy zaznaczony poniżej kod:
35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 | # pętla główna programu
while True:
# obsługa zdarzeń generowanych przez gracza
for event in pygame.event.get():
# przechwyć zamknięcie okna
if event.type == QUIT:
pygame.quit()
sys.exit()
# przechwyć ruch myszy
if event.type == MOUSEMOTION:
myszaX, myszaY = event.pos # współrzędne x, y kursora myszy
# oblicz przesunięcie paletki gracza
przesuniecie = myszaX - (PALETKA_SZER / 2)
# jeżeli wykraczamy poza okno gry w prawo
if przesuniecie > OKNOGRY_SZER - PALETKA_SZER:
przesuniecie = OKNOGRY_SZER - PALETKA_SZER
# jeżeli wykraczamy poza okno gry w lewo
if przesuniecie < 0:
przesuniecie = 0
# zaktualizuj położenie paletki w poziomie
paletka1_prost.x = przesuniecie
# rysowanie obiektów
oknogry.fill(LT_BLUE) # kolor okna gry
# narysuj w oknie gry paletki
oknogry.blit(paletka1, paletka1_prost)
# zaktualizuj okno i wyświetl
pygame.display.update()
|
Chcemy sterować paletką za pomocą myszy. Zadaniem powyższego kodu jest
przechwycenie jej ruchu (MOUSEMOTION
), odczytanie współrzędnych kursora
z tupli event.pos
i obliczenie przesunięcia określającego nowe położenie paletki.
Kolejne instrukcje warunkowe korygują nową pozycję paletki, jeśli wykraczamy
poza granice pola gry.
Przetestuj kod.
4.1.4. Piłka w grze¶
Piłkę tworzymy podobnie jak paletkę. Przed pętlą główną programu wstawiamy poniższy kod:
35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 | # piłka #################################################################
P_SZER = 20 # szerokość
P_WYS = 20 # wysokość
P_PREDKOSC_X = 6 # prędkość pozioma x
P_PREDKOSC_Y = 6 # prędkość pionowa y
GREEN = (0, 255, 0) # kolor piłki
# utworzenie powierzchni piłki, narysowanie piłki i wypełnienie kolorem
pilka = pygame.Surface([P_SZER, P_WYS], pygame.SRCALPHA, 32).convert_alpha()
pygame.draw.ellipse(pilka, GREEN, [0, 0, P_SZER, P_WYS])
# ustawienie prostokąta zawierającego piłkę w początkowej pozycji
pilka_prost = pilka.get_rect()
pilka_prost.x = OKNOGRY_SZER / 2
pilka_prost.y = OKNOGRY_WYS / 2
# ustawienia animacji ###################################################
FPS = 30 # liczba klatek na sekundę
fpsClock = pygame.time.Clock() # zegar śledzący czas
|
Przy tworzeniu powierzchni dla piłki używamy flagi SRCALPHA
, co oznacza,
że obiekt graficzny będzie zawierał przezroczyste piksele. Samą piłkę rysujemy
za pomocą instrukcji pygame.draw.ellipse(powierzchnia, kolor, prostokąt)
.
Ostatni argument to lista zawierająca współrzędne lewego górnego i prawego dolnego
rogu prostokąta, w który wpisujemy piłkę.
Ruch piłki, aby był płynny, wymaga użycia animacji. Ustawiamy więc liczbę
generowanych klatek na sekundę (FPS = 30
) i przygotowujemy obiekt zegara,
który będzie kontrolował czas.
Teraz pod pętlą (nie w pętli!) for
, która przechwytuje zdarzenia, umieszczamy kod:
78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 | # ruch piłki ########################################################
# przesuń piłkę po obsłużeniu zdarzeń
pilka_prost.move_ip(P_PREDKOSC_X, P_PREDKOSC_Y)
# jeżeli piłka wykracza poza pole gry
# z lewej/prawej – odwracamy kierunek ruchu poziomego piłki
if pilka_prost.right >= OKNOGRY_SZER:
P_PREDKOSC_X *= -1
if pilka_prost.left <= 0:
P_PREDKOSC_X *= -1
if pilka_prost.top <= 0: # piłka uciekła górą
P_PREDKOSC_Y *= -1 # odwracamy kierunek ruchu pionowego piłki
if pilka_prost.bottom >= OKNOGRY_WYS: # piłka uciekła dołem
pilka_prost.x = OKNOGRY_SZER / 2 # więc startuję ze środka
pilka_prost.y = OKNOGRY_WYS / 2
# jeżeli piłka dotknie paletki gracza, skieruj ją w przeciwną stronę
if pilka_prost.colliderect(paletka1_prost):
P_PREDKOSC_Y *= -1
# zapobiegaj przysłanianiu paletki przez piłkę
pilka_prost.bottom = paletka1_prost.top
|
Na uwagę zasługuje metoda .move_ip(offset, offset)
, która przesuwa prostokąt
zawierający piłkę o podane jako offset
wartości. Dalej decydujemy, co ma się dziać,
kiedy piłka wyjdzie poza pole gry. Metoda .colliderect(prostokąt)
pozwala sprawdzić,
czy dwa obiekty nachodzą na siebie. Dzięki temu możemy odwrócić bieg piłeczki
po jej zetknięciu się z paletką gracza.
Piłkę trzeba umieścić na polu gry. Podaną niżej instrukcję umieszczamy poniżej polecenia rysującego paletkę gracza:
108 109 | # narysuj w oknie piłkę
oknogry.blit(pilka, pilka_prost)
|
Na koniec ograniczamy prędkość animacji wywołując metodę .tick(fps)
,
która wstrzymuje wykonywanie programu na podaną jako argument liczbę klatek na sekundę.
Podany niżej kod trzeba dopisać na końcu w pętli głównej:
114 115 | # zaktualizuj zegar po narysowaniu obiektów
fpsClock.tick(FPS)
|
Teraz możesz już zagrać sam ze sobą! Przetestuj działanie programu.
4.1.5. AI – przeciwnik¶
Dodamy do gry przeciwnika AI (ang. artificial inteligence), czyli paletkę sterowaną programowo.
Przed główną pętlą programu dopisujemy kod tworzący paletkę AI:
53 54 55 56 57 58 59 60 61 62 63 64 | # paletka ai ############################################################
RED = (255, 0, 0)
PALETKA_AI_POZ = (350, 20) # początkowa pozycja zapisana w tupli
# utworzenie powierzchni paletki, wypełnienie jej kolorem,
paletkaAI = pygame.Surface([PALETKA_SZER, PALETKA_WYS])
paletkaAI.fill(RED)
# ustawienie prostokąta zawierającego paletkę w początkowej pozycji
paletkaAI_prost = paletkaAI.get_rect()
paletkaAI_prost.x = PALETKA_AI_POZ[0]
paletkaAI_prost.y = PALETKA_AI_POZ[1]
# szybkość paletki AI
PREDKOSC_AI = 5
|
Tu nie ma nic nowego, więc od razu przed instrukcją wykrywającą kolizję piłki
z paletką gracza (if pilka_prost.colliderect(paletka1_prost)
)
dopisujemy kod sterujący ruchem paletki AI:
111 112 113 114 115 116 117 118 119 120 121 122 123 | # AI (jak gra komputer) #############################################
# jeżeli piłka ucieka na prawo, przesuń za nią paletkę
if pilka_prost.centerx > paletkaAI_prost.centerx:
paletkaAI_prost.x += PREDKOSC_AI
# w przeciwnym wypadku przesuń w lewo
elif pilka_prost.centerx < paletkaAI_prost.centerx:
paletkaAI_prost.x -= PREDKOSC_AI
# jeżeli piłka dotknie paletki AI, skieruj ją w przeciwną stronę
if pilka_prost.colliderect(paletkaAI_prost):
P_PREDKOSC_Y *= -1
# uwzględnij nachodzenie paletki na piłkę (przysłonięcie)
pilka_prost.top = paletkaAI_prost.bottom
|
Samą paletkę AI trzeba umieścić na planszy, po instrukcji rysującej paletkę gracza dopisujemy więc:
134 135 136 | # narysuj w oknie gry paletki
oknogry.blit(paletka1, paletka1_prost)
oknogry.blit(paletkaAI, paletkaAI_prost)
|
Pozostaje zmienić kod odpowiedzialny za odbijanie piłki od górnej krawędzi
planszy (if pilka_prost.top <= 0
), żeby przeciwnik AI mógł przegrywać.
W tym celu dokonujemy zmian wg poniższego kodu:
102 103 104 105 | if pilka_prost.top <= 0: # piłka uciekła górą
# P_PREDKOSC_Y *= -1 # odwracamy kierunek ruchu pionowego piłki
pilka_prost.x = OKNOGRY_SZER / 2 # więc startuję ze środka
pilka_prost.y = OKNOGRY_WYS / 2
|
Teraz można już zagrać z komputerem :-).
4.1.6. Liczymy punkty¶
Co to za gra, w której nie wiadomo, kto wygrywa... Dodamy kod zliczający i wyświetlający punkty. Przed główną pętlą programu wstawiamy poniższy kod:
66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 | # komunikaty tekstowe ###################################################
# zmienne przechowujące punkty i funkcje wyświetlające punkty
PKT_1 = '0'
PKT_AI = '0'
fontObj = pygame.font.Font('freesansbold.ttf', 64) # czcionka komunikatów
def drukuj_punkty1():
tekst1 = fontObj.render(PKT_1, True, (0, 0, 0))
tekst_prost1 = tekst1.get_rect()
tekst_prost1.center = (OKNOGRY_SZER / 2, OKNOGRY_WYS * 0.75)
oknogry.blit(tekst1, tekst_prost1)
def drukuj_punktyAI():
tekstAI = fontObj.render(PKT_AI, True, (0, 0, 0))
tekst_prostAI = tekstAI.get_rect()
tekst_prostAI.center = (OKNOGRY_SZER / 2, OKNOGRY_WYS / 4)
oknogry.blit(tekstAI, tekst_prostAI)
|
Po zdefiniowaniu zmiennych przechowujących punkty graczy, tworzymy obiekt czcionki
z podanego pliku (pygame.font.Font()
). Następnie definiujemy funkcje,
których zadaniem jest rysowanie punktacji graczy. Na początku tworzą one nowe obrazki
z punktacją gracza (.render()
), pobierają ich prostokąty (.get_rect()
),
pozycjonują je (.center()
) i rysują na głównej powierzchni gry (.blit()
).
Informacja
Plik wykorzystywany do wyświetlania tekstu (freesansbold.ttf
)
musi znaleźć się w katalogu ze skryptem.
W pętli głównej programu musimy umieścić wyrażenia zliczające punkty. Jeżeli piłka ucieknie górą, punkty dostaje gracz, w przeciwnym wypadku AI. Dopisz podświetlone instrukcje:
122 123 124 125 126 127 128 129 130 131 | if pilka_prost.top <= 0: # piłka uciekła górą
# P_PREDKOSC_Y *= -1 # odwracamy kierunek ruchu pionowego piłki
pilka_prost.x = OKNOGRY_SZER / 2 # więc startuję ze środka
pilka_prost.y = OKNOGRY_WYS / 2
PKT_1 = str(int(PKT_1) + 1)
if pilka_prost.bottom >= OKNOGRY_WYS: # piłka uciekła dołem
pilka_prost.x = OKNOGRY_SZER / 2 # więc startuję ze środka
pilka_prost.y = OKNOGRY_WYS / 2
PKT_AI = str(int(PKT_AI) + 1)
|
Obie funkcje wyświetlające punkty również trzeba wywołać z pętli głównej,
a więc po instrukcji wypełniającej okno gry kolorem (oknogry.fill(LT_BLUE)
)
dopisujemy:
153 154 155 156 157 | # rysowanie obiektów ################################################
oknogry.fill(LT_BLUE) # wypełnienie okna gry kolorem
drukuj_punkty1() # wyświetl punkty gracza
drukuj_punktyAI() # wyświetl punkty AI
|
4.1.7. Sterowanie klawiszami¶
Skoro możemy przechwytywać ruch myszy, nic nie stoi na przeszkodzie,
aby umożliwić poruszanie paletką za pomocą klawiszy.
W pętli for
odczytującej zdarzenia dopisujemy:
114 115 116 117 118 119 120 121 122 123 | # przechwyć naciśnięcia klawiszy kursora
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
paletka1_prost.x -= 5
if paletka1_prost.x < 0:
paletka1_prost.x = 0
if event.key == pygame.K_RIGHT:
paletka1_prost.x += 5
if paletka1_prost.x > OKNOGRY_SZER - PALETKA_SZER:
paletka1_prost.x = OKNOGRY_SZER - PALETKA_SZER
|
Naciśnięcie klawisza generuje zdarzenie pygame.KEYDOWN
.
Dalej w instrukcji warunkowej sprawdzamy, czy naciśnięto klawisz kursora
lewy lub prawy i przesuwamy paletkę o 5 pikseli.
Wskazówka
Kody klawiszy możemy sprawdzić w dokumentacji Pygame.
Uruchom program i sprawdź, jak działa. Szybko zauważysz, że wciśnięcie strzałki porusza paletką, ale żeby poruszyła się znowu, trzeba naciskanie powtarzać. To niewygodne, paletka powinna ruszać się dopóki klawisz jest wciśnięty. Przed pętlą główną dodamy więc poniższy kod:
86 87 | # powtarzalność klawiszy (delay, interval)
pygame.key.set_repeat(50, 25)
|
Dzięki tej instrukcji włączyliśmy powtarzalność wciśnięć klawiszy. Przetestuj, czy działa.
4.1.8. Zadania dodatkowe¶
- Zmodyfikuj właściwości obiektów (paletek, piłki) takie jak rozmiar, kolor, początkowa pozycja.
- Zmień położenie paletek tak, aby znalazły się przy lewej i prawej krawędzi okna, wprowadź potrzebne zmiany w kodzie, aby poruszały się w pionie.
- Dodaj trzecią paletkę, która co jakiś czas będzie “przelatywać” przez środek planszy i zmieniać w przypadku kolizji tor i kolor piłki.
4.1.9. Materiały¶
Źródła:
Materiały Python 101
udostępniane przez
Centrum Edukacji Obywatelskiej na licencji
Creative Commons Uznanie autorstwa-Na tych samych warunkach 4.0 Międzynarodowa.
Utworzony: | 2022-05-22 o 19:52 w Sphinx 1.5.3 |
---|---|
Autorzy: | Patrz plik “Autorzy” |