IT rekvalifikácia. Seniorní programátori zarábajú až 6 000 €/mesiac a rekvalifikácia je prvým krokom. Zisti, ako na to!

4. diel - Pygame - Pong - Prostredie a herné objekty

Naposledy, v lekcii Pygame - Pong - Príprava , sme si zistili, ktoré znalosti potrebujeme k úspešnému vytvoreniu našej prvej hry Pong v pygame. Teraz nám teda už nič nebráni sa do nej rovno pustiť! Hra bude vyzerať takto:

Pyong – Hra Pong v pygame a Pythone - Pygame

Definícia prostredie

Podobne ako v našich predchádzajúcich kódoch, aj teraz musíme vytvoriť pevný základ našej hry.

Príprava pygame

Povedzme, že hru budeme pripravovať na rozlíšenie 1920 x 1080, ale bude spustiteľná s rozlíšením ľubovoľným. Preto si vytvoríme oddelené premenné display a screen. Zároveň by sa nám asi páčilo, aby okno malo nejaký titulok, ktorý by reprezentoval našu hru. K tomu slúži pre nás nová metóda pygame.display.set_caption(name), ktorá nastaví titulok okna na hodnotu name.

Posledná vec, ktorá by sa nám mohla hodiť, je premenná round_tick. Tú si vytvoríme a budeme ju inkrementovať každým krokom počas kola, bude nám fungovať ako časovač. Toho využijeme napríklad, keď budeme chcieť zrýchliť loptu každých pár sekúnd.

Trieda Game

Naše hry by mali mať objektovú architektúru a ideálne by všetky objekty v hre mali byť inštancie samostatných tried. Pygame nám k tomu aj ponúka niekoľko tried, z ktorých môžeme svoje objekty oddědit.

Keďže je to naša prvá pygame hra, štruktúru súboru si trochu zjednodušíme, avšak sa bude stále jednať o objekt. Celú hru implementujeme ako triedu Game, v ktorej budeme nastavovať všetky jej atribúty. To hlavne aby sme sa vyhli zbytočnému prenosu premenných funkciám hry cez parametre alebo dokonca cez nebezpečné globálne premenné. Takto budú zo všetkých metód triedy dostupné. Trieda Game bude teraz vyzerať takto:

import pygame

class Game:
     def __init__(self):
        pygame.init()
        self.running = True
        self.display: pygame.Surface = pygame.display.set_mode((1366, 768))
        pygame.display.set_caption("PyONG")
        self.screen = pygame.Surface((1920, 1080))
        self.round_tick = 0
        self.clock = pygame.time.Clock()
        self.FPS = 30  # maximální počet FPS

Príprava písiem

Vždy je dobrý nápad nemiešať mnoho druhov písiem v jednej aplikácii. Preto si vyberieme jedno písmo, ktoré budeme používať. Keďže budeme kresliť skóre, u ktorého je žiaduce, aby pozície jednotlivých znakov zostala vždy rovnaká, mohli by sme dôjsť k tom, že sa nám najviac hodí písmo s pevnou šírkou znakov (monospace).

Ak by sme ale boli nerozhodní, ktoré z tých všetkých nainštalovaných písiem si máme vybrať, mohli by sme za nás nechať vybrať také písmo náš program podľa toho, či názov písma obsahuje text 'mono':

from random import choice
    # ...
    def __init__(self):
        # ...
        font_name = choice([x for x in pygame.font.get_fonts() if 'mono' in x.lower()])

Ak poznáme priamo meno písma, ktoré chceme použiť, upravíme tento kód podobne, ako sme si ukázali v minulej lekcii.

Potom nám už zostáva iba vytvoriť jednotlivé písma. Budeme určite potrebovať jedno menšie, na stále zobrazovanie skóre a druhé väčšie, na zverejnenie výziev a informácií:

# ...
self.font_score = pygame.font.SysFont(font_name, 30)
self.font_info = pygame.font.SysFont(font_name, 50)

Príprava herných objektov

Základné pygame prostredie máme nachystané, prejdime k herným objektom.

Hráči

V našej hre budú dvaja hráči. Prvý hráč bude vľavo a druhý vpravo. Obaja sa budú môcť pohybovať nejakú rýchlosťou. Zároveň, keďže sú obaja hráči obdĺžniky, bola by škoda ich rovno ako pygame obdĺžniky nevytvoriť, keď k tomu máme pripravenú triedu. Každý hráč tiež bude mať skóre au hráčov číslo 2 budeme chcieť vedieť, či ak namiesto neho hrá počítač:

# ...
player_size = (50, 200)
self.player_speed = 10

# umisti hrace 1 ze zacatku vlevo doprostred
self.player1_orig = pygame.Rect((0, self.screen.get_height() // 2), player_size)
# a vytvor jeho kopii, abychom pri kazdem kole se mohli jednoduse vratit k puvodni pozici
self.player1 = self.player1_orig.copy()
self.player1_score = 0

# hrace cislo 2 umisti vpravo doprostred
self.player2_orig = pygame.Rect((self.screen.get_width() - player_size[0], self.screen.get_height() // 2), player_size)
self.player2 = self.player2_orig.copy()
self.player2_score = 0
self.player2_ai = False  # True pokud misto hrace c. 2 hraje pocitac

Lopta

Podobne ako hráča si vytvoríme tiež loptu. Ten sa ale, na rozdiel od hráčov, kde smer pohybu určuje vstup od užívateľa, môže pohybovať ľubovoľným smerom. Budeme si teda musieť uložiť jeho súčasný smer. Lopta sa tiež postupom času môže zrýchľovať až do určitej maximálnej rýchlosti:

ball_size = (20, 20)
# umísti mic doprostred herni plochy
self.ball_orig = pygame.Rect((self.screen.get_width() // 2, self.screen.get_height() // 2), ball_size)
self.ball = self.ball_orig.copy()
self.ball_direction = 0  # nastavi se na zacatku kazdeho kola, hodnoty <0; 360)
self.ball_speed_orig = 15
self.ball_speed_max = self.ball_speed_orig * 4
self.ball_speed = self.ball_speed_orig

Aby sme si uľahčili výpočty a zaručili, že lopta nebude mať nikdy hodnotu smere mimo rozsahu, napíšeme si rovno metódu, pomocou ktorej budeme jeho smer nastavovať:

def set_ball_direction(self, val: int):
    if abs(val) >= 360:
        # zmensi mozny rozsah hodnot na (-360; 360) pomoci zjisteni zbytku po deleni
        val %= 360
    if val < 0:
        # prevede zapornou velikost uhlu na jeji kladnou reprezentaci v kruznici
        val = 360 + val
    self.ball_direction = val

Hranice obrazovky

Posledný objekty, ktoré budeme vytvárať, síce nebudú viditeľné, ale napriek tomu sú vysoko dôležité. Jedná sa o hranice obrazovky, ktoré si vytvoríme ako pygame obdĺžniky. Vďaka tomu potom nebudeme musieť porovnávať súradnice každého ďalšieho objektu, či náhodou neopustil obrazovku, ale namiesto toho nám bude stačiť porovnať kolízii dvoch obdĺžnikov. Tieto obdĺžniky budú vždy pokrývať celú jednu hranu hernej plochy, to nás asi neprekvapí. Čo by nás ale mohlo prekvapiť, je ich hrúbka. Tá musí byť minimálne tak veľká ako maximálnu rýchlosť lopty, inak by sa totiž mohlo stať, že by lopta pri vyšších rýchlostiach naše okraje jednoducho preskočil. Tento ich presah premietneme za hranice viditeľnej časti hernej plochy:

def __init__(self):
# ...
    self.top = pygame.Rect((0, -self.ball_speed_max), (self.screen.get_width(), self.ball_speed_max))
    self.bottom = pygame.Rect((0, self.screen.get_height()), (self.screen.get_width(), self.ball_speed_max))
    self.left = pygame.Rect((-self.ball_speed_max, 0), (self.ball_speed_max, self.screen.get_height()))
    self.right = pygame.Rect((self.screen.get_width(), 0), (self.ball_speed_max, self.screen.get_height()))

A máme hotovo! Teda aspoň máme hotové naše objekty.

Herné cyklus

Pokračovať budeme tým, že si vytvoríme klasický herný cyklus, ktorý sme v predchádzajúcich lekciách robili už niekoľkokrát:

# ...
def main(self):
    while self.running:
        # UDALOSTI
        for event in pygame.event.get():
            if event.type == pygame.QUIT or (event.type == pygame.KEYUP and event.key == pygame.K_q):
                self.running = False
        # KRESLENI
        pygame.transform.scale(self.screen, self.display.get_size(), self.display)
        pygame.display.flip()
        self.clock.tick(FPS)
        self.screen.fill((0, 0, 0))

if __name__ == '__main__':
    Game().main()

Tento náš základný herný cyklus beží rýchlosťou maximálne FPS snímok za sekundu. V každej svojej iterácii skontroluje, či ak niekto nepoužil kláves Q, aby sa hra vypla, popr. jej vypnutie nebolo vyžiadané inak. Ak nie, tak vykreslí plochu screen na obrazovku.

V budúcej lekcii, Pygame - Pong - Logika stavov hry a dokončenie , sa budeme venovať stavom hry.


 

Predchádzajúci článok
Pygame - Pong - Príprava
Všetky články v sekcii
Pygame
Preskočiť článok
(neodporúčame)
Pygame - Pong - Logika stavov hry a dokončenie
Článok pre vás napísal Adam Hlaváček
Avatar
Užívateľské hodnotenie:
Ešte nikto nehodnotil, buď prvý!
vývoji užitečných aplikací zjednodušujících každodenní život
Aktivity