8. diel - Skákačka v Pygame - Obrázky
V minulej lekcii, Skákačka v Pygame - Tvorba okna a pohyb postavy , sme sa dozvedeli o tvorbe okna, vytvorení a pohybe postavy, alebo aj ako vkladať obrázky v Pygame.
V tomto tutoriále Pygame v Pythone v našom projekte
Skákačky pokročíme k práci s obrázkami. Vytvoríme si pre
nich triedu Image
.
Súbor pre obrázky a zvuky
Aby sa nám s obrázkami dobre pracovalo, vytvoríme si pre nich vlastnú
triedu. Pretože ale z rozboru vieme, že podobným spôsobom budeme chcieť
pracovať aj so zvukmi, vytvoríme pre tieto dve triedy jeden súbor, aby boli
pohromade. Súbor pomenujeme media.py
a vytvoríme ho v priečinku
engine/
. Budeme tu používať knižnice pygame
a
logging
, takže ich naimportujeme:
import pygame import logging
Teraz krátko odbočíme na logovanie. Chceme vedieť, či nám hra hlási
niečo o priebehu z pohľadu hráča (napríklad posun jeho figúrky) alebo z
pohľadu programu (naimportovanie obrázku). Vytvoríme si preto novú
inštanciu triedy Logger
, ktorú budeme používať na tieto
programové udalosti. Na začiatok súboru game.py
(kde sa bude
odohrávať väčšina týchto udalostí) hneď pod import pridáme:
import logging GameEngineLogger = logging.getLogger("Engine")
Aby sme sa k tomuto objektu mohli dostať aj v media.py
, musíme
ho tam tiež naimportovať:
from engine.game import GameEngineLogger
Trieda Image
Teraz sa konečne môžeme vrhnúť na samotnú triedu Image
.
Napíšeme hlavičku class Image:
, než sa však vrhneme na
konštruktor, pridáme ešte premenné. Tieto budú prístupné všetkým
inštanciám triedy Image
a pôjdu zmeniť iba cez meno triedy.
Premenné triedy Image
Ako prvé pridáme premennú default_dir
, z ktorej sa budú
načítavať obrázky. Uložíme do nej .
, čiže zložku, v ktorej
sa momentálne nachádzame. Ďalšou bude _accepted_extensions
, kam
uložíme zoznam možných prípon obrázkov. Poslednú premennú pomenujeme
_images_cache
. Do nej budeme ukladať už načítané obrázky.
Lenže ako chceme obrázky vlastne ukladať?
Aby naša hra vyzerala o niečo živšie, budeme chcieť, aby sa nám
obrázky menili. Vlajka bude viať vo vetre, lietajúci hmyz bude mávať
krídlami, nepriatelia nebudú pri pohybe tam a späť cúvať, ale otočia sa a
pôjdu iným smerom. Ale ako to urobiť? Použijeme na to ten najjednoduchší
(aj keď nie práve algoritmický) spôsob. Jednoducho si uložíme viac
obrázkov a budeme ich periodicky meniť. Budeme si však musieť pamätať,
ktoré skupinky obrázkov patria k sebe. Preto bude fajn mať ich všetky v
rámci jedného Image
objektu. V _images_cache
bude
teda uložený slovník. Jeho kľúče budú mená objektov v hre a hodnoty
budú zoznamy všetkých pygame
verzií obrázkov (budú to teda
objekty typu pygame.surface
). Aby sme na toto nezabudli, radšej si
to poznamenáme pomocou knižnice typing
. Predpripravíme si u nej
aj iné dátové typy, než potrebujeme, aby sme sa sem neskôr nemuseli
vracať. Celý súbor media.py
teraz teda vyzerá takto:
import pygame import logging from typing import Dict, Set, Optional, List, Callable from engine.game import GameEngineLogger class Image: default_dir = '.' _accepted_extensions = ['.png', '.jpg', '.gif', '.bmp'] _images_cache: Dict[str, List[pygame.Surface]] = {}
Konštruktor
A teraz už konečne prejdeme ku konštruktoru. Na to, aby sme mohli obrázok správne načítať, budeme potrebovať:
- meno obrázku,
- veľkosť, na ktorú ho chceme zmeniť,
- info, či chceme obrázok vôbec načítať.
__init__()
teda bude vyzerať
takto:
def __init__(self, image_name: str, convert_size: (int, int) = None, load: bool = True):
Teraz spíšeme atribúty inštancie triedy Image
:
self.name
– meno obrázku z__init__
,self.speed
- koľko iterácií hernej slučky musí prebehnúť, aby sa obrázok premenil na svoju ďalšiu „fázu“. Ako základná hodnota tu bude nula, ktorú použijeme ako deaktiváciu zmeny obrázku,self._image_index
– index obrázku, ktorý sa práve zobrazuje,self._image_tick
– koľko slučiek ubehlo od poslednej zmeny,self._size
– veľkosť obrázku vo formáte (šírka, výška), zatiaľ nastavená na nulu, pretože nemáme žiadny obrázok načítaný.
self._game
(konkrétne
inštancie práve bežiacej hry) a self._subimages
(zoznam
obrázkov v rámci jedného objektu):
self.name = image_name self.speed = 0 self._image_index = 0 self._image_tick = 0 self._size = (0, 0) self._game = None self._subimages = None
Ďalšou vecou, ktorú musíme urobiť, je nevytvárať znovu obrázok,
pokiaľ už je v našej cache pamäti. Pokiaľ tam teda je, do
self._subimages
príde hodnota cache pod menom obrázku. Zároveň
ohlásime načítanie z pamäte cez logging
:
if image_name in self._images_cache: GameEngineLogger.debug(f"Image {image_name} is in cache") self._subimages = self._images_cache[image_name]
Teraz však už máme načítané obrázky av atribúte
self_size
tak nemôže zostať hodnota (0,0)
. Budeme
ju musieť zmeniť a aby sa nám nestalo, že bude niektorý z obrázkov
väčší ako uložená hodnota, prejdeme ich všetky. Do atribútu
self_size
uložíme najväčšiu šírku a najväčšiu dĺžku.
Vytvoríme na to metódu self._get_size()
, ktorá bude vyzerať
takto:
def _count_size(self) -> (int, int): max_w = 0 max_h = 0 for sub in self._subimages: w, h = sub.get_size() if w > max_w: max_w = w if h > max_h: max_h = h self._size = (max_w, max_h) return self._size
Túto metódu zavoláme v našej momentálnej vetve konštruktora
__init__()
a potom zavoláme return
, aby sme
konštruktor v tejto vetve ukončili a neprepísali si načítané obrázky
ďalším kódom:
if image_name in self._images_cache: GameEngineLogger.debug(f"Image {image_name} is in cache") self._subimages = self._images_cache[image_name] self._count_size() return
Ďalšia okrajová podmienka sa týka našej premennej load
z
hlavičky __init__()
. Ak by totiž bola False
, nemalo
by sa nič načítať. Navyše by sme mali z cache pamäte odstrániť
podobrázky, pokiaľ by tam boli. Táto vetva teda bude vyzerať takto:
if not load: GameEngineLogger.debug(f"Image {image_name} is not due to load") if not self._subimages: GameEngineLogger.debug(f"Image {image_name} has not been loaded, emptying all subimages") self._images_cache[image_name] = self._subimages = [] return
V tejto lekcii sme síce nijako nezmenili vizuál hry, avšak pripravili sme si podstatnú časť kódu pre budúcu podobu Skákačky. Kompletný zdrojový kód je na stiahnutie pod lekciou.
V ďalšej lekcii, Skákačka v Pygame - Obrázky - Načítanie, dokončíme načítanie obrázkov pre Skákačku.
Zameriame sa tiež na funkcie map()
a lambda()
.
Mal si s čímkoľvek problém? Stiahni si vzorovú aplikáciu nižšie a porovnaj ju so svojím projektom, chybu tak ľahko nájdeš.
Stiahnuť
Stiahnutím nasledujúceho súboru súhlasíš s licenčnými podmienkami
Stiahnuté 10x (9.14 kB)
Aplikácia je vrátane zdrojových kódov v jazyku Python