https://www.pygame.org/docs/ref/sprite.html
https://www.geeksforgeeks.org/pygame-creating-sprites/
https://coderslegacy.com/python/python-pygame-tutorial/
https://coderslegacy.com/python/pygame-sprite-collision-detection/
Moduł 'sprite' zawiera klasę Sprite i klasę Group, która zawiera sprites. W module są jeszcze inne specjalistyczne klasy oraz funkcje do wykrywania kolizji sprites. Użycie tych klas jest opcjonalne, ale są one przydatne w typowych grach.
Wykrywanie kolizji ciał poprzez sprawdzanie nakrywających się pikseli na ogół jest zbyt powolne, jak na potrzeby gier wideo. Lepiej jest stworzyć obiekty Rect trochę mniejsze od poruszających się ciał, a następnie użyć obiekty Rect do wykrycia kolizji.
Klasa Sprite jest pomyślana jako klasa bazowa dla różnych rodzajów obiektów w grze. Ważne jest nadpisanie atrybutów 'image' oraz 'rect'. Przydaje się nadpisanie metody update() do sterowania zachowaniem sprite, domyślna metoda nie robi nic.
class Block(pygame.sprite.Sprite): def __init__(self, color, width, height): #pygame.sprite.Sprite.__init__(self) # działa konstruktor z klasy bazowej super().__init__() self.image = pygame.Surface([width, height]) # return Surface # Można załadować obrazek z dysku. self.image.fill(color) # nie mamy obrazka, to będzie kolor self.rect = self.image.get_rect() # return Rect
# Metody instancji klasy Sprite. sprite.add(*groups) # dodanie sprite do podanych grup sprite.remove(*groups) # usunięcie sprite z podanych grup sprite.kill() # usunięcie sprite ze wszystkich grup; sprite żyje dalej sprite.alive() # return bool; czy sprite należy do jakiejś grupy sprite.groups() # return group_list; lista grup, do których należy sprite
Klasa Group jest zaprojektowana do przechowywania sprites, a w grze można tworzyć specjalizowane klasy potomne. Dodawanie i usuwanie sprites z grupy jest wydajne, podobnie jak sprawdzenie, czy sprite nateży do grupy. Jeden sprite może należeć jednocześnie do wielu grup. Lepiej korzystać z grup niż dodawać nowe atrubuty do klasy Sprite, bo to ułatwia wyszukiwanie w grze.
Metody Group.draw() i Group.clear() wymagają, aby każdy sprite miał atrybuty 'image' oraz 'rect'.
Metoda Group.update() wywołuje Sprite.update() dla wszystkich sprites należących do grupy.
Wykrywanie kolizji sprites wymaga, aby sprites posiadały atrybut 'rect'.
group = pygame.sprite.Group() group = pygame.sprite.Group(*sprites)
# Standardowe operacje Pythona. sprite in group # test przynależności len(group) # liczebność grupy bool(group) # True, jeżeli len(group) != 0 for sprite in group: pass # iteracja po grupie # Nie można iterować po grupie i jednocześnie jej modyfikować!
# Metody instancji klasy Group. group.sprites() # return sprite_list group2 = group1.copy() group.add(*sprites) # dodawanie podanych sprites do grupy group1.add(group2) # wstawiamy do group1 sprites należące do group2 group1.add(group2, group3, sprite4, sprite5) # można łączyć group.remove(*sprites) # usuwanie podanych sprites z grupy group1.remove(group2) # usuwamy z group1 sprites należące do group2 group1.remove(group2, group3, sprite4, sprite5) # można łączyć group.empty() # usuwanie wszystkich sprites z tej grupy group.has(*sprites) # return bool; True, jeżeli wszystkie sprites należą group1.has(group2, group3, sprite4, sprite5) # można łączyć # Równoważny (prawie) kod: # all(sprite in group for sprite in sprites) # Wg dokumentacji dla add(), remove(), has(): # Each sprite argument can also be a iterator containing Sprites. group.update(*arguments, **keywords) # Równoważny kod: # for sprite in group: # sprite.update(*arguments, **keywords) group.draw(Surface) # blit the Sprite images # Równoważny (prawie) kod: # for sprite in group: # Surface.blit(sprite.image, sprite.rect) group.clear(Surface_dest, background) # draw a background over the Sprites; # Zamazuje pozycje sprites używając 'background'. # Korzysta z pozycji (Rect) z ostatniego wywołania Group.draw(). # 'background' to jest zwykle Surface o taki rozmiarze jak Surface_dest. # Może też być 'callback function' typu clear_callback(surface, rect).
# Wykrywanie kolizji sprite1 i sprite2. sprite1.rect.colliderect(sprite2.rect) # return bool pygame.sprite.collide_rect(sprite1, sprite2) # return bool # wewnętrznie używane są atrybuty 'rect'.
# Wykrywanie kolizji sprite z grupą innych sprites. # Zwraca None, jeżeli nie ma kolizji. # Zwraca pewien sprite2 z group, z którym jest przecięcie. # Ta funkcja jest szybsza od spritecollide(). pygame.sprite.spritecollideany(sprite, group) # return sprite2 lub None
# W wynikowej liście są wszystkie przecinane sprites z group. # Jeżeli dokill=True, to te sprites są usuwane z group. # Wewnętrznie do znalezienia przecięć używane są atrybuty 'rect'. pygame.sprite.spritecollide(sprite, group, dokill) # return sprite_list
# Wykrywanie kolizji między grupami sprites. # W wynikowym słowniku sprites z group1 są kluczami, a wartościami # są listy przecinanych sprites z group2. # Jeżeli dokill1=True, to sprites są usuwane z group1. # Jeżeli dokill2=True, to sprites są usuwane z group2. pygame.sprite.groupcollide(group1, group2) # return dict pygame.sprite.groupcollide(group1, group2, dokill1, dokill2) # return dict
# sprite1.py import sys import pygame # COLORS black = (0, 0, 0) gray = (128, 128, 128) white = (255, 255, 255) red = (255, 0, 0) green = (0, 255, 0) blue = (0, 0, 255) yellow = (255, 255, 0) class Block(pygame.sprite.Sprite): def __init__(self, color, width, height): super().__init__() self.image = pygame.Surface([width, height]) self.image.fill(color) self.rect = self.image.get_rect() # INITIALIZE THE GAME pygame.init() # to zawsze na starcie size = (width, height) = (500, 500) screen = pygame.display.set_mode(size) # display Surface pygame.display.set_caption('Creating Sprite') # CLOCK FPS = 60 # frames per second setting clock = pygame.time.Clock() sprite_group = pygame.sprite.Group() sprite_red = Block(red, 50, 100) sprite_red.rect.topleft = (200, 300) sprite_group.add(sprite_red) sprite_blue = Block(blue, 100, 100) sprite_blue.rect.topleft = (100, 100) sprite_group.add(sprite_blue) sprite_green = Block(green, 100, 50) sprite_green.rect.topleft = (150, 100) sprite_group.add(sprite_green) # MAIN GAME LOOP while True: for event in pygame.event.get(): if event.type == pygame.QUIT: # QUIT Event, pygame.locals.QUIT pygame.quit() # deactivates the Pygame library sys.exit(0) elif event.type == pygame.KEYDOWN: if event.key == pygame.K_t: print("test kolizji sprite_red i sprite_blue") print(pygame.sprite.collide_rect(sprite_red, sprite_blue)) print("test kolizji sprite_blue i sprite_green") print(sprite_blue.rect.colliderect(sprite_green.rect)) elif event.type == pygame.MOUSEBUTTONDOWN: for sprite in sprite_group: # iteracja po grupie if sprite.rect.collidepoint(event.pos): print("trafiony {}".format(event.pos)) sprite_group.update() # wywołuje update() dla sprites screen.fill(black) sprite_group.draw(screen) pygame.display.flip() clock.tick(FPS)