Vyjdeme z ukázkové hříčky s poníky z minulé hodiny, zde je ale trochu upravená. Můžete je všechny stáhnout jako jeden archiv, nebo zkopírovat kód a obrázek a mapu stáhnout odděleně. V druhém případě dejte pozor na pojmenování, soubory musíte uložit s názvy spritesheet.png
a map.txt
. Pythonský skript musíte buďto uložit v kódování utf-8, nebo patřičně změnit komentář coding:
na prvním řádku.
# coding: utf-8 import pygame from random import randint # všimněte si, že třída níže volá přímo funkci pygame.image.load # (o něco obyčejnější by bylo volat ji v konstruktoru, ale chceme šetřit čas) # proto je potřeba zavolat pygame.init() před samotnou definicí té třídy pygame.init() screenrect = pygame.Rect(0, 0, 640, 480) screen = pygame.display.set_mode(screenrect.size) pygame.display.set_caption('Poníci') black = pygame.Color(0, 0, 0) white = pygame.Color(255, 255, 255) class Pony(pygame.sprite.Sprite): # rychlost pohybu v pixelech za sekundu speed = 50 # všechny obrázky poníků, co budou potřeba, načteme jednou během spouštění skriptu # obrázek PNG s patřičným názvem musíte stáhnout a uložit do stejné složky jako tento skript spritesheet = pygame.image.load('spritesheet.png').convert_alpha() sprite_size = 32 # velikost jednoho čtverečku ve spritesheetu, v pixelech def __init__(self, maze): """maze: pygame.Mask; určuje, kam poník smí a kam ne""" pygame.sprite.Sprite.__init__(self) # barva. Číslo v rozsahu 0..7, protože tolik barev máme ve spritesheetu self.color = randint(0, 7) # směr chůze. 0: dolů, 1: doleva, 2: doprava, 3: nahoru, protože tak je to ve spritesheetu self.direction = 0 # poloha na mapě self.border_right = maze.get_size()[0] - self.sprite_size self.border_bottom = maze.get_size()[1] - self.sprite_size self.x, self.y = randint(0, self.border_right), randint(0, self.border_bottom) self.rect = pygame.Rect(self.x, self.y, self.sprite_size, self.sprite_size) # na začátku cíl chůze necháme na správném místě self.target = self.rect.x, self.rect.y # načteme obrázek ze spritesheetu self.update_image() # zapamatujeme si bludiště pro pozdější kontroly nárazu self.maze = maze # do masky zaznamenáme pixely obrázku, co nejsou průhledné self.mask = pygame.mask.from_surface(self.image) def update(self, time): """Posune poníka směrem k cíli, pokud to jde time: float; udává čas od posledního volání""" distance = time * self.speed # pokaždé zkoušíme, jestli poník chce jít tímhle směrem a jestli může if self.target[0] > self.rect.x + distance and self.can_move(distance, 0): self.x += distance self.direction = 2 elif self.target[0] < self.rect.x - distance and self.can_move(-distance, 0): self.x -= distance self.direction = 1 elif self.target[1] > self.rect.y + distance and self.can_move(0, distance): self.y += distance self.direction = 0 elif self.target[1] < self.rect.y - distance and self.can_move(0, -distance): self.y -= distance self.direction = 3 # skutečnou polohu překopírujeme do celočíselného self.rect self.rect.x, self.rect.y = self.x, self.y self.update_image() def update_image(self): """Vybere aktuální obrázek ze spritesheetu, pro animaci""" # začneme v levém horním rohu tabulky obsahující poníky příslušné barvy cell_x, cell_y = 3 * (self.color % 4), 4 * (self.color // 4) # najdeme buňku podle směru chůze a aktuálního času (animace 6 snímků za vteřinu) cell_y += self.direction time = pygame.time.get_ticks() / 1000.0 # čas od spuštění hry, původně v milisekundách cell_x += int(6 * time) % 3 # přepočítáme pozici v tabulce na skutečné souřadnice v pixelech a zkopírujeme obrázek x = cell_x * self.sprite_size y = cell_y * self.sprite_size self.image = self.spritesheet.subsurface(pygame.Rect((x, y), self.rect.size)) def can_move(self, horz, vert): """Ověří, jestli se maska poníka nepřekrývá s maskou překážek horz, vert: int; posuv oproti současné pozici poníka""" overlap = self.maze.overlap_area(self.mask, (int(self.x + horz), int(self.y + vert))) return overlap == 0 def set_target(self, x, y): """Změní cíl chůze na dané souřadnice""" self.target = [x, y] def draw_map(image, filename): """Vykreslí na daný obrázek černě mapu z textového souboru daného názvu""" f = open(filename, "r") for line in f.readlines(): # každý řádek souboru rozdělíme na čtyři hodnoty a ty převedeme na čísla x1, y1, x2, y2 = line.split() pygame.draw.line(image, black, (int(x1), int(y1)), (int(x2), int(y2))) f.close() background = pygame.Surface(screenrect.size) background.fill(white) draw_map(background, "map.txt") screen.blit(background, (0,0)) pygame.display.update() # vyrobíme z vykreslené mapy masku: překážka je všechno, co není bílé # (set_colorkey je malý úskok, kterým nastavíme dočasně bílou jako průhlednost) background.set_colorkey(white) maze = pygame.mask.from_surface(background) background.set_colorkey(None) ponies = pygame.sprite.RenderUpdates() peaceful = pygame.sprite.Group() for i in range(10): pony = Pony(maze) pony.add(ponies) pony.add(peaceful) killer = pony peaceful.remove(killer) clock = pygame.time.Clock() alive = True while alive: for event in pygame.event.get(): if event.type == pygame.QUIT or (event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE): alive = False elif event.type == pygame.MOUSEBUTTONDOWN: for pony in ponies: pony.set_target(event.pos[0], event.pos[1]) ponies.clear(screen, background) ponies.update(clock.get_time() / 1000.0) for pony in pygame.sprite.spritecollide(killer, peaceful, dokill=False, collided=pygame.sprite.collide_mask): pony.kill() # poníci ve skutečnosti neumřou, jen je odebereme ze všech skupin dirty = ponies.draw(screen) pygame.display.update(dirty) clock.tick(30) # 30 FPS pygame.quit()
Zařiďte, aby se při každém stisknutí mezerníku přidal na obrazovku nový poník, na náhodnou pozici. Bude potřeba doplnit kód ošetřování událostí pro událost takovou, že event.type == pygame.KEYDOWN and event.key == pygame.K_SPACE
.
Zařiďte, aby klávesy A, S, D vyráběly poníky tří pevně daných barev. Budete muset změnit konstruktor poníka, aby bral číslo barvy jako parametr. Kvůli tomu budete muset taky změnit původní kód na výrobu poníků a potřebný parametr doplnit jako náhodné číslo rovnou tam. Konečně, kód klávesy A je pygame.K_A
, ostatní klávesy jsou pojmenované obdobně.
Kód pro přidávání poníků kliknutím můžete smazat, na druhou stranu vám možná poslouží jako dobrá rada. Hodně se podobá tomu, co napíšete místo něj.
Zařiďte, aby poník zmizel z obrazovky, když na něj kliknete. Nejjednodušší bude použít funkci pygame.Rect.collide_point((x, y))
postupně na obdélníky odpovídající všem poníkům (je to vlastnost rect
každého z nich) a pokud funkce vrátí True
, poníka smazat jeho metodou kill()
.
Nakonec vykopírujte kód pro přidávání poníků zpět; když nekliknete na žádného poníka, měl by se nový přidat na souřadnice, kam jste klikli, s náhodnou barvou.
Napište nový skript (třeba s názvem map.py
), který bude sloužit ke kreslení mapy. Bude možné si naklikat úsečky na okno a ty se rovnou ve správném formátu zapíšou do souboru map.txt
, aby je skript s poníky načetl při příštím spuštění.
Po prvním kliknutí myší byste si měli zapamatovat začátek úsečky. Každé druhé kliknutí by pak mělo úsečku vykreslit a zapsat příslušná čtyři čísla do souboru. Formát každé řádky souboru je Ax Ay Bx By
, prostě čtyři celá čísla udávající souřadnice koncových bodů A, B.
Textový soubor jde otevřít funkcí open(název, způsob)
. Funkce navrací objekt typu file
, se kterým můžeme dále pracovat. První parametr je obyčejně název souboru, ale pokud ho udáváme s celým umístěním na disku, kvůli kompatibilitě se používají i na Windows obyčejná lomítka /
místo zpětných. Druhý parametr je řetězec udávající, co chceme se souborem dělat. Obvykle si vystačíme s "r"
pro čtení a "w+"
pro vytvoření nového souboru a zápis do něj. Pokud bychom nezacházeli s textem, nýbrž s binárními daty (třeba chtěli zapsat obrázek), musíme do řetězce přidat znak "b".
Celý obsah souboru můžeme přečíst funkcí soubor.read()
, bude to řetězec. Jako parametr můžeme funkci dát maximální počet znaků, které se mají přečíst.
Jednotlivé řádky můžeme přečíst funkcí soubor.readlines()
, výsledek je seznam řetězců. Když ale chceme každý řádek projít jen jednou, můžeme soubor rovnou použít v cyklu:
for radek in soubor: # v proměnné radek dostaneme postupně všechny řádky souboru jako řetězce
Do souboru můžeme zapisovat řádek po řádku pomocí už známé funkce print(file=soubor, cokoliv...)
s pojmenovaným parametrem file
. Zbytek volání se nemění, můžeme vypsat i víc hodnot za sebou. Když chceme zapsat libovolná data bez odřádkování, můžeme použít funkci soubor.write(řetězec)
.
Když se souborem přestaneme pracovat, musíme ho zavřít, aby se data zapsala na disk. Jde to udělat výslovně tak, že zavoláme soubor.close()
, anebo místo toho použijeme snad trochu hezčí zápis, který se o všechno postará:
with open("nazev.txt", "w+") as soubor: # tady může být nějaký kód, který bude pracovat se souborem # na tomhle místě ale bude soubor už bezpečně zavřený
Funkce str.split(oddělovač)
navrací seznam vzniklý rozdělením původního řetězce podle daných oddělovačů. Oddělovačem může být libovolný řetězec, například tečka: v takovém případě bude seznam obsahovat jednotlivé věty textu. V původním pořadí a už bez teček.
Pokud zavoláme str.split()
bez parametru, chová se trochu zvláštně: rozdělí řetězec podle všech mezerám podobných řetězců. Může to být jedna nebo víc mezer, tabulátor, nový řádek a podobně, nebo jejich směs. Je to praktické, ale zásadně odlišné od volání split(" ")
Naopak, druhým parametrem můžeme nastavit maximální počet nalezených oddělovačů. Délka seznamu str.split(oddělovač, maxsplit)
tedy bude maxsplit + 1
.
Funkce str.join(sekvence)
spojí zadané řetězce do jednoho. Mírně se liší zápisem od ostatních jazyků: řetězec před tečkou bude použitý oddělovač. Když chceme spojit seznam slov, zavoláme tedy " ".join(["nazdar", "světe"])
. Když chceme spojit řetězce bez oddělovače, použijeme prázdný řetězec ""
.