Ügyességi játékok programozása
Pythonnal és Pygame-mel

Chapter 13: Ismerkedés a grafikus hátteret kezelő elemekkel(sprite-okkal)

Videó: Ismerkedés a sprite-okkal

A játékunknak szüksége van arra, hogy kezelni tudjuk az ütköző tárgyakat. Labdák pattognak, lézerágyúk találnak el idegeneket, vagy a kedvenc karakterünk gyűjt be érmét. Minden ilyen példa, egy-egy ütközés érzékelést igényelt.

A Pygame könyvtár támogatja a sprite-okat. A sprite egy kétdimenziós kép, ami egy nagyobb grafikus jelenet része. Tipikusan, a sprite egyfajta objektum a képernyőn, ami kapcsolatba lép egy autóval, békával vagy egy kis vízvezetékszerelővel.

fig.sprite

Eredetileg a videójáték konzoloknak beépített támogatásuk volt a hardverben a sprite-okhoz. Most már ez a különleges hardvertámogatás nem szükséges, mégis használjuk a sprite terminust.

13.1 Alapvető sprite-ok és ütközések

Nézzük meg a példa programunkat, ami sprite-okat használ. A példán azt láthatjuk, hogyan készítsünk egy fekete téglákból álló képernyőt, és hogyan gyűjtsük össze őket egy vörös téglával, amit az egérrel irányítunk. 13.1. A program számolja a pontokat, aszerint, mennyi téglát gyűjtöttünk be. A kódja ennek a példa programnak megtalálható itt:
ProgramArcadeGames.com/python_examples/f.php?file=sprite_collect_blocks.py

fig.example_sprite_game1
Figure 13.1: Példa sprite-os játék

Az első néhány programsorunk ugyanúgy kezdődik, mint más játékaink eddig:

import pygame
import random

# Definiáljuk a színeket
BLACK = (  0,   0,   0)
WHITE = (255, 255, 255)
RED   = (255,   0,   0)

A Pygame könyvtár fontos lesz a sprite kezelés szempontjából már az első sorban. A véleten (random) könyvtár felelős a téglák véletlenszerű elhelyezéséért, a második sortól hívtuk meg. A színek megadás az 5-7 sorokban történik, semmi új nincs ebben a példában.

class Block(pygame.sprite.Sprite):
    """
    Ez az osztály jeleníti meg a labdát.
    Ez a sprite osztályból lesz leegyszerűsítve.
    """

A kilencedik sor azzal kezdi, hogy definiálja a Block(tégla) osztályt. Figyeld meg, hogy a 9-s sorban, ez az osztály a gyermeke a kbd>Sprite osztálynak. A pygame.sprite. specifikálja a könyvtárat és a csomagot, de ezekről majd a 15-dik fejezetben ejtünk szót. Az összes alapértelmezett beállítása a Sprite osztálynak most már a Block osztály része is.

    def __init__(self, color, width, height):
        """ Konstruktor. Megadja a tégla színét, és
	x,y pozícióját . """

        # Meghívjuk a szülő osztályt(Sprite) a konstruktorhoz
        super().__init__()

A konstruktor a Block osztályhoz, a 15-s sorban a self paraméterrel rendelkezik, mint bármely más konstruktor. Vannak még paraméterei, amik megadják a színt, magasságot és szélességet.

Fontos, hogy meghívjuk a szülő osztályát a konstruktornak a Sprite-ban, mivel így tudjuk a sprite-okat kezdő értékekkel ellátni. Ezt a 20-dik sorban tesszük meg.

        # Létrehozunk egy tégla képet, és kitöltjük színnel.
        # Ez lehet egy kép, amit lemezről töltünk be.
        self.image = pygame.Surface([width, height])
        self.image.fill(color)

A 24 és 25-s sorban olyan képet alkotunk, ami mindig megjelenik majd a képernyőn. A 24-s sor egy üres képet alkot. A 25-s tölti ki feketével. Ha a program számára fontos, hogy valami más legyen ott, ne csak egy fekete négyzet, akkor ez a pár sor kód kell neked:

Íme a példánk kódja:

    def __init__(self, color, width, height):
        """
	Ellipszis konstruktor. Megadja az ellipszis színét
	és méretét.
        """
        # Meghívjuk a szülő osztályt (Sprite) konstruktornak
        super().__init__()

        #Megadjuk a háttér színét és áttetszővé állítjuk
        self.image = pygame.Surface([width, height])
        self.image.fill(white)
        self.image.set_colorkey(white)

        # Kirajzoljuk az ellipszist
        pygame.draw.ellipse(self.image, color, [0, 0, width, height])

Ha a fenti kódot behelyettesítjük, akkor minden ellipszis alakot ölt majd. A 29-s sor kirajzolja az ellipszist, és a 26-s sor fehérré és áttetszővé teszi. Ugyanaz az elképzelés, mint a Tizenkettedik Fejezetben volt. Fehér hátteret készítünk, és a képünk fehér részét áttetszővé tesszük.

    def __init__(self):
        """ Grafikus sprite konstruktor """

        # Meghívjuk a szülő osztályt (Sprite) a konstruktornak
        super().__init__()

        # betöltjük a képet
        self.image = pygame.image.load("player.png").convert()

        # áttetszővé tesszük a színt
        self.image.set_colorkey(white)

Ha ehelyett egy bit-térképes grafikát szeretnénk, akkor másoljuk át a fenti kódot, ami a grafikát tölti be (22-s sor) és állítsuk be fehérnek az áttetsző színt (25-s sor). Ezesetben, a sprite-unk dimenziói automatikusan a grafikai dimenziói lesznek, és így nem kell tovább foglalkoznunk vele. Nézd meg a 15-s sorban, ahogy már nincsenek meg ezek a paraméterek.

Van még egy fontos sor, ami szükséges a konstruktorunknal, nem számít, milyen sprite-t szeretnénk:

        # adjuk meg a téglalapot, aminek a dimenziója a kép
        # Frissítsük ennek a tárgynak a pozícióját, a megadott értékekkel.
        # az értékeke a rect.x és rect.y
        self.rect = self.image.get_rect()

A rect változó egy attribútum, ami egy példánya a Rect osztálynak, amit a Pygame biztosít számunkra. A téglalap ábrázolja a sprite dimenzióit. Ez a téglalap osztály rendelkezik az x és y attribútumokkal. A Pygame oda fogja rajzolni a téglalapot, ahol az x és y vannak. Szóval, ahhoz, hogy mozgatni tudjuk ezt a sprite-ot, a programozónak szüksége van a mySpriteRef.rect.x és a mySpriteRef.rect.y értékekre, ahol a mySpriteRef az a változó, ami a sprite-ra mutat.

Készen vagyunk a Tégla osztállyal. Ideje tovább menni az alap beállítások felé.

# Pygame alap beállítása
pygame.init()

# A képernyő szélessége és magassága
screen_width = 700
screen_height = 400
screen = pygame.display.set_mode([screen_width, screen_height])

A fenti kód alapértékekkel látja el a Pygame-t és egy, a játék számára készült ablakot hoz létre. Semmi új nincs még itt, más programjainkhoz képest.

#Sprite-ok listája. Minden tégla a programban a
#listához fog adódni
# A listát egy "Group/csoport" nevű osztály kezeli
block_list = pygame.sprite.Group()

# Ez itt az összes sprite listája.
# Minden tégla és a játékos téglája is itt van.
all_sprites_list = pygame.sprite.Group()

A legnagyobb előny a sprite-okkal való munkának az, hogy csoportban lehet velük dolgozni. Rajzolhatunk és mozgathatunk minden sprite-ot egyetlen parancs kiadásával, ha csoportban vannak. Sőt, ellenőrízhetjük csoportosan a pattogásukat is.

A fenti kód két listát hozott létre. Az all_sprites_list változó minden sprite-t magába foglal a játékban. Ez a lista lesz használva arra, hogy kirajzoljuk az összes sprite-t. A block_list változó tartalmazza az összes objektumot, amit a játékos pattogtathat. Ebben a példában ez fogja magába foglalni az összes objektumot, még a játékost is. De, nekünk nincs szükségünk a játékosra ebben a listában, mivel ha ellenőrízzük, hogy a játékos ütközött e valakivel a block_list-ban, akkor azt találjuk majd, hogy igen, mivel ő maga is a lista része.

for i in range(50):
    # Ez egy téglát jelent
    block = Block(BLACK, 20, 15)

    # Tegyük egy véletlen helyre a téglát
    block.rect.x = random.randrange(screen_width)
    block.rect.y = random.randrange(screen_height)

    # Adjuk a téglát egy lista objektumhoz
    block_list.add(block)
    all_sprites_list.add(block)

A ciklus elkezdődik a 49-s sorban és 50 fekete sprite téglát ad majd a képernyőhöz. Az 51-s sor egy új téglát fog létrehozni, beállítja a színt, szélességet, magasságot. Az 54-55-s sor megadja a koordinátákat, ahol majd az objektum megjelenik. Az 58-s sor pedig a téglát a listához adja, amiben a többi tégla is van, és amikkel a játékos összeütközhet. Az 59-s sor hozzáadja az összes téglát tartalmazó listához. Ez egy elég egyszerű kód, amit már megírtál a 13-s laborban.

# létrehozzuk a Vörös színű játékos tégláját
player = Block(RED, 20, 15)
all_sprites_list.add(player)

a 61-63 sorok a játékosnak adnak értékeket a játék elején. A 62-s sor egy vörös téglát hoz létre, ami végig a játékban a játékost fogja jelenteni. Ez a tégla hozzá lesz adva az all_sprites_list a 63-s s sorban, így kirajzolható lesz, de nem lesz hozzáadva a block_list-hoz.

# Ismételd amíg a játékos rá nem kattint a bezár gombra.
done = False

# Kezelnünk kell azt is, hogy a képernyő milyen gyakran frissüljön.
clock = pygame.time.Clock()

score = 0

# -------- Főprogram ciklusa  -----------
while not done:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            done = True

    # letöröljük  a képernyőt
    screen.fill(WHITE)

A fenti kód egy standard program ciklus, amivel először az ötödik fejezet- ben találkoztunk. A 71-s sor fogja a score változót 0-ra állítani kezdetben.

    #Adjuk meg a jelenlegi egér pozíciót. ez vissza fog adni egy pozíciót,
    # ami két szám lesz egy listában.
    pos = pygame.mouse.get_pos()

    # Nyerjük ki az x és y értékét a listából,
    # hasonlóan ahhoz, ahogyan egy szövegből kinyertük a betűket.
    # A játékos objektumát jelenítsük meg az egér helyén.
    player.rect.x = pos[0]
    player.rect.y = pos[1]

A 84-s sor megadja az egér pozícióját, hasonlóan más Pygame programokhoz, amikről már beszéltünk. A fontos dolog ebben a 89-90-s sor, ahol a téglatest magába foglalja a sprite-t, ami majd egy új helyre mozgatja azt. Emlékezz rá, hogy a "rect"-t a 31-s sorban hoztuk létre, és ez a kód nem működik nélküle.

    # Figyeld meg, ha a játékos téglája ütközik e valamivel.
    blocks_hit_list = pygame.sprite.spritecollide(player, block_list, True)

Ez a kódsor veszi a player sprite referenciáját és összehasonlítja minden sprite-tal a block_list-ban. A kód ezután visszaadja a sprite-ok listáját, amik fedik egymást. Ha nincsenek egymást fedő sprite-ok, akkor egy üres lista fog visszajönni. A True (igaz) boolean változó letörli az ütköző sprite-okat a listáról. Ha ezt False (hamis)-ra állítjuk, akkor a sprite-ok nem fognak törlődni.

    # Ellenőrízd a pattogás listáját:
    for block in blocks_hit_list:
        score +=1
        print(score)

Ez a ciklus minden sprite-ot a pattogós listában visszaad a 93-s sorban. Ha ezek a sprite-ok a listában vannak, akkor meg kell növelni a pontot minden pattogás esetén. Azután a pontszámot kiírjuk a a képernyőre. Jegyezd meg, hogy a print a 98-s sorban nem fogja kiírni a pontszámot a fő ablakba, hanem csak csak a konzol ablakba. A 14-s labor mutatja meg, hogyan kell megjeleníteni a pontszámot a fő ablakban.

    # rajzold ki az összes sprite-ot
    all_sprites_list.draw(screen)

A Group (csoport) osztálynak az all_sprites_list tagja, és így van egy eljárása, amit draw(rajz)-nak hívnak. Ez az eljárás ciklus ismételni fog minden sprite-ot a listában és meghívja ahhoz a draw eljárást. Ez azt jelenti, hogy csak egy sornyi kód kell, hogy a program all_sprites_list-ből az összes sprite-t megjelenítsük.

    # Csak 60 frame per másodperc
    clock.tick(60)

    # Gyerünk tovább, és frissítsük a képernyőt amivel rajzoltuk.
    pygame.display.flip()

pygame.quit()

A 103-109 sorok felugranak a képernyőre és meghívják a quit eljárást, amikor a főciklusnak vége.

13.2 Sprite-ok mozgatása

Videó: Sprite-ok mozgatása

Az eddigi példákban csak a játékos sprite-ja mozgott. Hogyan képes egy program az összes sprite-ot mozgásra bírni? Ez is könnyű, mindössze két lépést igényel.

Az első lépésben egy új eljárást fogunk létrehozni a Block osztályban. Ezt az új eljárást nevezzük update (frissítés)-nek. A frissítés függvény automatikusan meg lesz hívva, amikor az update-t futtatjuk/meghívjuk a program során.

Tegyük ezt a sprite-ba:

    def update(self):
        """ Minden frame-t meghívunk. """

        # Egy pixellel lejjeb mozog a tégla
        self.rect.y += 1

Tegyük ezt a főprogram ciklusba:

    #Meghívjuk az update() eljárást minden téglára, a block_list-ban
    block_list.update()

A kód nem tökéletes, mivel a tégla ki fog esni a képernyőről és nem fog többet megjelenni. Ezt a kódot még ki kell egészíteni az update függvénnyel, így a tégla megjelenik majd a tetején.

    def update(self):
        # mozgassuk lejjebb egy pixellel a téglát
        self.rect.y += 1
        if self.rect.y > screen_height:
			self.rect.y = random.randrange(-100, -10)
			self.rect.x = random.randrange(0, screen_width)

Ha a program reseteli a téglát, amit a képernyő tetejére gyűjtöttünk, akkor a sprite-ot meg tudjuk változtatni az alábbi kóddal:

    def reset_pos(self):
        """ Kinullázzuk  a pozíciót a képernyő tetejére, véletlen x helyre
	Meghívjuk az update() vagy a főciklust, ha ütközés vam .
        """
        self.rect.y = random.randrange(-300, -20)
        self.rect.x = random.randrange(0, screen_width)

    def update(self):
        """ minden frame-t meghívunk """

        # mozgassunk egy pixellel lejjeb egy téglát
        self.rect.y += 1

        # Ha egy tégla túl lent van, akkor reseteljük ki a képernyő tetejére
        if self.rect.y > 410:
            self.reset_pos()

Ahelyett, hogy elpusztítanánk a téglát, amikor egy ütközés történik, a program meghívhat egy függvényt, amit reset_pos -nak nevezünk el, és a tégla a képernyő tetejére mozog, begyűjtésre várván.

    # Lássuk, hogy  ajátékos téglája ütközik e.
    blocks_hit_list = pygame.sprite.spritecollide(player, block_list, False)

    # Ellenőrízzük az ütközések listáját.
    for block in blocks_hit_list:
        score += 1
        print(score)

        # reseteljük a téglát a képernyő tetejére, hogy újra leessen.
        block.reset_pos()

A teljes példakódunk itt lesz:
ProgramArcadeGames.com/python_examples/f.php?file=moving_sprites.py

Ha inkább ugráló kódokat látnál, akkor ezt nézd meg:
ProgramArcadeGames.com/python_examples/f.php?file=moving_sprites_bounce.py

Ha köröket szeretnél mozgatni:
ProgramArcadeGames.com/python_examples/f.php?file=sprite_circle_movement.py

13.3 A játék osztály

Lapozzunk vissza a Kilencedik Fejezethez, ahol megismerkedtünk a függvénnyel. A fejezet végén beszéltünk egy lehetőségről, a main függvényről. Ahogy a programunk egyre nagyobb lesz, ez a technika fog minket kihúzni a bajból, és így nem kell annyi sor kódot írnunk. A programunk nem túl nagy még. Habár, tudom, hogy némely emberek szeretnek aprólékosan tervezni már a kezdettől.

Nos, azoknak az embereknek, abban a táborban, itt van még egy technika, amivel lehetőség nyílik a kód szervezettebbé tételére. (Ha nem tartozol ahhoz a táborhoz, akkor át is ugorhatod ezt a részt, és majd visszakanyarodsz, amikor a programot nagyobbá kezd válni. ) Nézd meg a videót, hogy képet kapj arról, ahogyan a program működik.
ProgramArcadeGames.com/python_examples/f.php?file=game_class_example.py

13.4 További példák

Van még másféle példa is arra, ahogyan tudjuk kezelni a sprite-okat. Egypárról van itt videó, amiből kiderül, hogy működik a kód.

13.4.1 Dolgok lelövése

fig.bullets
Figure 13.2: dolgok lelövése

Érdekelnek a lövöldözős játékok? Valami olyasmi, mint a klasszikussá vált Space Invaders? Ez a példa be fogja mutatni, hogyan készíts sprite-okat, amik lövedékeket jelenítenek meg:
ProgramArcadeGames.com/python_examples/f.php?file=bullets.py

13.4.2 Falak

Jobban kedveled a kaland játékokat? És persze nem szeretnéd, hogy a játékos csak úgy bárhova elkószálhasson. Ez itt megmutatja, hogyan adhatsz falakat, amik gátolják a játékos mozgását:
ProgramArcadeGames.com/python_examples/f.php?file=move_with_walls_example.py

fig.walls
Figure 13.3: Falak, amikre felmászhatunk

Egy pillanat! Egy szoba nem elég egy kalandhoz? Szeretnéd, ha a játékos mozoghatna képernyőről, képernyőre? Megtehetjük. Nézd csak meg ezt a példát, ahol a játékos egy több-szobás labirintusban mászkálhat:
ProgramArcadeGames.com/python_examples/f.php?file=maze_runner.py

fig.maze_runner
Figure 13.4: Többszobás labirintus

13.4.3 Platformok

Szeretnél platform játékot, mint amilyen a Donkey Kong? Ehhez szükség lesz egy ahhoz hasonló dologra, mint a falak, csak gravitációt kell hozzá adnunk. :
ProgramArcadeGames.com/python_examples/f.php?file=platform_jumper.py

fig.platform_jumper
Figure 13.5: Platformokon ugrálás

A jó platformjáték oldaltól oldalig mozog. Ez egy oldalra scrollozó platformjáték: :
ProgramArcadeGames.com/python_examples/f.php?file=platform_scroller.py

fig.platform_scroller
Figure 13.6: Oldalra scrollozó platform játék

Még a jobb platformjátékokban is vannak platformok, amik mozognak. Nézzük meg egy példán át ezeket:
ProgramArcadeGames.com/python_examples/f.php?file=platform_moving.py

fig.platform_moving
Figure 13.7: Mozgó platformok

13.4.4 Kígyó/százlábú

Meg szoktam kérni a diákokat, hogy készítsenek kígyó, vagy százlábú játékot. Van egy több részből álló kígyód, amit irányítsz. Ehhez minden szegmenst egy listában kell tárolni. Ehhez két új parancsot kell megtanulni, de maga a koncepció, ahogy az egész működik, nem nehéz.

Irányítsd a kígyódat, vagy százlábúdat a képernyőn körbe:
ProgramArcadeGames.com/python_examples/f.php?file=snake.py

fig.snake
Figure 13.8: Kígyü

13.4.5 sprite-lapok használata

Ez egy példa arra, ahogyan sprite-lapokat használhatsz, hogy megtámogasd a platform játékot ezzel. Ez támogatja a több szintet és a mozgó platformokat is. A játék több fájlra tördelhető. ProgramArcadeGames.com/python_examples/en/sprite_sheets

fig.sprite_sheet
Figure 13.9: Sprite lap platform játék

13.4.6 Kvízkérdések

Click here for a multiple-choice quiz.

13.4.7 Feladatlap

There is no worksheet for this chapter.

13.4.8 Labor feladat

Click here for the chapter lab.


You are not logged in. Log in here and track your progress.