• Nie Znaleziono Wyników

Sprite'y i wykrywanie kolizji

N/A
N/A
Protected

Academic year: 2022

Share "Sprite'y i wykrywanie kolizji"

Copied!
14
0
0

Pełen tekst

(1)

Sprite'y i wykrywanie kolizji

W tej części będziemy kontynuować używanie animacji Pygame. Zobaczymy rzeczy zwane sprite'ami, które pomagają nam śledzić wiele obrazów poruszających się na ekranie. Zobaczymy również, jak wykryć, kiedy dwa obrazy nakładają się na siebie lub uderzają o siebie, na przykład kiedy piłka uderza w wiosło lub statek kosmiczny uderza w asteroidę.

Duszki

W ostatniej części zobaczyłeś, że prosta animacja nie jest wcale taka prosta. Jeśli masz dużo obrazów i przenosisz je dookoła, śledzenie tego, co jest „pod” każdym obrazem, może być bardzo trudne, dzięki czemu możesz odświeżyć obraz po przesunięciu. W naszym pierwszym przykładzie z piłką plażową tło było tylko białe, więc było łatwiej. Ale możesz sobie wyobrazić, że z grafiką w tle będzie się to komplikować. Na szczęście Pygame zapewnia dodatkową pomoc. Poszczególne obrazy lub części obrazu, które się poruszają, nazywane są duszkami, a Pygame ma specjalny moduł do obsługi duszków, sprite. Pozwala to łatwiej przenosić obiekty graficzne. W ostatniej części podskoczyliśmy na plaży. A co, jeśli chcemy, aby cała masa piłek plażowych podskakiwała? Moglibyśmy napisać kod, aby zarządzać każdą piłką indywidualnie, ale zamiast tego użyjemy modułu sprite. Pygame, aby ułatwić sobie pracę.

Sprite oznacza grupę pikseli, które są przenoszone i wyświetlane jako pojedyncza jednostka, rodzaj obiektu graficznego.

„Termin„ sprite ”jest zapisem ze starszych komputerów i maszyn do gier. Te starsze pudełka nie były w stanie szybko rysować i usuwać normalnej grafiki, aby mogły działać jako gry. Maszyny te miały specjalny sprzęt do obsługi obiektów podobnych do gry, które musiały być Obiekty te nazywano

„duszkami” i miały specjalne ograniczenia, ale można je było szybko rysować i aktualizować… W dzisiejszych czasach komputery stały się na ogół wystarczająco szybkie, aby obsługiwać obiekty sprite bez dedykowanego sprzętu.

Co jest sprite?

Pomyśl o duszku jako małej grafice - rodzaju obiektu graficznego, który będzie poruszał się po ekranie i wchodził w interakcje z innymi obiektami graficznymi. Większość duszków ma kilka podstawowych właściwości:

■ Obraz - grafika wyświetlana dla ikonki

■ rect - prostokątny obszar zawierający ikonkę

Obraz może być taki, który narysujesz za pomocą funkcji rysowania Pygame lub takiego, który otrzymujesz z pliku obrazu.

Klasa sprite

Moduł sprite Pygame zapewnia klasę sprite bazową o nazwie Sprite. (Pamiętaj, kiedy rozmawialiśmy o obiektach i klasach kilka rozdziałów temu?). Zwykle nie używasz bezpośrednio klasy bazowej, ale zamiast tego utwórz własną podklasę opartą na pygame.sprite.Sprite. Zróbmy to w przykładzie i wywołajmy klasę

class MyBallClass(pygame.sprite.Sprite):

def __init__(self, image_file, location):

pygame.sprite.Sprite.__init__(self) <- Inicjuje sprite’a

self.image = pygame.image.load(image_file)<- Ładuje do niego plik obrazu

(2)

self.rect = self.image.get_rect() <- Pobiera prostokąt określający granice obrazu self.rect.left, self.rect.top = location <- Ustawia początkową lokalizację piłki

Ostatniemu wierszowi tego kodu warto przyjrzeć się bliżej. lokalizacja to lokalizacja [x, y], która jest listą z dwoma elementami. Ponieważ mamy listę z dwoma elementami po jednej stronie znaku = (x i y), możemy przypisać dwie rzeczy po drugiej stronie. Tutaj przypisaliśmy lewy i górny atrybut prostokąta prostego. Teraz, gdy zdefiniowaliśmy MyBallClass, musimy utworzyć kilka jego wystąpień.

(Pamiętaj, że definicja klasy to tylko plan; teraz musimy zbudować kilka domów.) Nadal potrzebujemy tego samego kodu, którego użyliśmy w ostatnim rozdziale, aby utworzyć okno Pygame. Będziemy także tworzyć kilka kulek na ekranie, ułożonych w rzędy i kolumny. Zrobimy to za pomocą zagnieżdżonej pętli:

img_file = "beach_ball.png"

balls = []

for row in range (0, 3):

for column in range (0, 3):

location = [column * 180 + 10, row * 180 + 10] <-Zmienia lokalizację za każdym razem w pętli ball = MyBallClass(img_file, location) <- Tworzy piłkę w tym miejscu

balls.append(ball) <- Zbieraj piłki z listy

Musimy również wymieszać piłki na powierzchni wyświetlacza for ball in balls:

screen.blit(ball.image, ball.rect) pygame.display.flip()

Łącząc wszystko, nasz program jest pokazany na poniższej liście.

Listing 17.1 Używanie duszków do umieszczania wielu obrazów kul na ekranie import sys, pygame

class MyBallClass(pygame.sprite.Sprite):

def __init__(self, image_file, location):

pygame.sprite.Sprite.__init__(self) self.image = pygame.image.load(image_file)

self.rect = self.image.get_rect() self.rect.left, self.rect.top = location size = width, height = 640, 480

screen = pygame.display.set_mode(size) screen.fill([255, 255, 255])

img_file = "beach_ball.png"

(3)

balls = []

for row in range (0, 3):

for column in range (0, 3):

location = [column * 180 + 10, row * 180 + 10]

ball = MyBallClass(img_file, location)

balls.append(ball) <- Dodaje piłki do listy for ball in balls:

screen.blit(ball.image, ball.rect) pygame.display.flip()

running = True while running:

for event in pygame.event.get():

if event.type == pygame.QUIT:

running = False pygame.quit()

Jeśli to uruchomisz, powinieneś zobaczyć dziewięć piłek plażowych w oknie Pygame, w ten sposób:

Za chwilę zaczniemy je przenosić. Czy zauważyłeś małą zmianę w wierszach 10 i 11, która określa rozmiar okna Pygame? Zastąpiliśmy

screen = pygame.display.set_mode ([640,480])

(4)

z

size = width, height = 640, 480

screen = pygame.display.set_mode(size)

Ten kod nie tylko ustawia rozmiar okna - jak poprzednio - ale także definiuje dwie zmienne, szerokość i wysokość, które możemy wykorzystać później. Dobrą rzeczą jest to, że zdefiniowaliśmy listę, zwaną size, z dwoma elementami w niej, a także zdefiniowaliśmy dwie zmienne całkowite, szerokość i wysokość, wszystko w jednej instrukcji. Nie używaliśmy również nawiasów kwadratowych wokół naszej listy, a Python ma z tym coś wspólnego. Chciałem ci tylko pokazać, że czasami w Pythonie można robić różne rzeczy. Jeden niekoniecznie jest lepszy od drugiego (o ile oba działają). Nawet jeśli musisz przestrzegać składni Pythona (zasady języka), wciąż jest miejsce na wolność wypowiedzi. Jeśli poprosiłeś 10 programistów o napisanie tego samego programu, prawdopodobnie nie otrzymasz dwóch identycznych fragmentów kodu.

Metoda move()

Ponieważ tworzymy kule jako instancje MyBallClass, sensowne jest ich przenoszenie za pomocą metody klasowej. Stwórzmy więc nową metodę klasy o nazwie move ():

def move(self):

self.rect = self.rect.move(self.speed) if self.rect.left < 0 or self.rect.right > width:

self.speed[0] = -self.speed[0]

if self.rect.top < 0 or self.rect.bottom > height:

self.speed[1] = -self.speed[1]

Sprite'y (właściwie ich prostokąty) mają wbudowaną metodę o nazwie move(). Ta metoda wymaga parametru o nazwie prędkość, aby powiedzieć, jak daleko (czyli jak szybko) przesunąć obiekt. Ponieważ mamy do czynienia z grafiką dwuwymiarową, prędkość to lista dwóch liczb, jedna dla prędkości x, a druga dla prędkości y. Sprawdzamy również, czy piłka uderza w krawędzie okna, abyśmy mogli „odbić”

kule wokół ekranu. Zmieńmy definicję MyBallClass, aby dodać właściwość speed i metodę move ():

class MyBallClass(pygame.sprite.Sprite):

def __init__(self, image_file, location, speed):<- Dodaje argument prędkości pygame.sprite.Sprite.__init__(self)

self.image = pygame.image.load(image_file) self.rect = self.image.get_rect()

self.rect.left, self.rect.top = location

self.speed = speed <- Dodaje tę linię, aby utworzyć atrybut prędkości dla piłki

def move(self): |

self.rect = self.rect.move(self.speed) |

(5)

if self.rect.left < 0 or self.rect.right > width:

self.speed[0] = -self.speed[0]

if self.rect.top < 0 or self.rect.bottom > height:

self.speed[1] = -self.speed[1]

Zwróć uwagę na zmianę w linii 2 (def __init __ (self, image_file, location, speed) :) oraz dodanie linii 7 (self.speed = speed), a także nową metodę move () w liniach od 9 do 15. Teraz kiedy tworzymy każde wystąpienie piłki, musimy powiedzieć jej prędkość, a także plik obrazu i lokalizację:

speed = [2, 2]

ball = MyBallClass(img_file, location, speed)

Poprzedni kod utworzy wszystkie kule z tą samą prędkością (w tym samym kierunku), ale fajnie byłoby zobaczyć, jak kule poruszają się trochę losowo. Użyjmy funkcji random.choice (), aby ustawić prędkość, tak jak poniżej:

from random import *

speed = [choice([-2, 2]), choice([-2, 2])

Spowoduje to wybranie -2 lub 2 zarówno dla prędkości x, jak i y. Oto kompletny program.

Listing 17.2 Program do przenoszenia kulek za pomocą ikonek import sys, pygame

from random import *

class MyBallClass(pygame.sprite.Sprite):

def __init__(self, image_file, location, speed):

pygame.sprite.Sprite.__init__(self)

self.image = pygame.image.load(image_file) self.rect = self.image.get_rect()

self.rect.left, self.rect.top = location self.speed = speed

def move(self):

self.rect = self.rect.move(self.speed) if self.rect.left < 0 or self.rect.right > width:

self.speed[0] = -self.speed[0]

if self.rect.top < 0 or self.rect.bottom > height:

self.speed[1] = -self.speed[1]

size = width, height = 640, 480

(6)

screen = pygame.display.set_mode(size) screen.fill([255, 255, 255])

img_file = "beach_ball.png"

balls = [] <-Tworzy listę do śledzenia piłki for row in range (0, 3):

for column in range (0, 3):

location = [column * 180 + 10, row * 180 + 10]

speed = [choice([-2, 2]), choice([-2, 2])]

ball = MyBallClass(img_file, location, speed)

balls.append(ball) <- Dodaje każdą piłkę do listy w jej postaci running = True

while running:

for event in pygame.event.get():

if event.type == pygame.QUIT:

running = False

pygame.time.delay(20) screen.fill([255, 255, 255]) for ball in balls:

ball.move()

screen.blit(ball.image, ball.rect) pygame.display.flip()

pygame.quit()

Ten program używa listy do śledzenia wszystkich piłek. W linii 32 (balls.append (ball)) każda piłka jest dodawana do listy w jej postaci. Kod w ostatnich pięciu wierszach powoduje ponowne narysowanie ekranu. Tutaj trochę oszukujemy i zamiast „wymazywać” (malując) każdą piłkę oddzielnie, po prostu wypełniamy okno bielą, a następnie przerysowujemy wszystkie piłki. Możesz eksperymentować z tym kodem, mając więcej (lub mniej) piłek, zmieniając ich prędkość, zmieniając sposób poruszania się i

„odbijania”, i tak dalej. Zauważysz, że kule poruszają się i odbijają od boków okna, ale nie odbijają się od siebie - jeszcze!

Whaw! Wykrywanie kolizji

W większości gier komputerowych musisz wiedzieć, kiedy jeden sprite uderza w innego. Na przykład, możesz potrzebować wiedzieć, kiedy kula do kręgli uderzy w kręgle lub kiedy pocisk trafi w statek kosmiczny. Być może myślisz, że jeśli znasz położenie i rozmiar każdego duszka, możesz napisać kod, aby sprawdzić, czy są one ustawione względem rozmiaru i pozycji każdego innego duszka, aby

(7)

zobaczyć, gdzie się nakładają. Ale ludzie, którzy napisali Pygame, już to dla nas zrobili. Pygame ma wbudowaną funkcję wykrywania kolizji.

Wykrywanie kolizji oznacza po prostu wiedzę, kiedy dwa sprite'y dotykają się lub nakładają się. Kiedy dwie rzeczy, które się poruszają, wpadają na siebie, nazywa się to kolizją

Pygame ma także sposób grupowania duszków. Na przykład w grze w kręgle wszystkie kręgle mogą znajdować się w jednej grupie, a piłka będzie w grupie własnej. Grupy i wykrywanie kolizji idą w parze.

W przykładzie gry w kręgle chciałbyś wykryć, kiedy piłka uderzy w któryś z kręgli, więc będziesz szukał kolizji między duszkiem piłki i wszelkie duchy w grupie kręgli. Możesz także wykryć kolizje w grupie (jak uderzenia o siebie szpilki). Przejdźmy do przykładu. Zaczniemy od naszych odbijających się piłek plażowych, ale aby łatwiej było zobaczyć, co się dzieje, zaczniemy od zaledwie czterech piłek zamiast dziewięciu. I zamiast tworzyć listę piłek, tak jak w poprzednim przykładzie, użyjemy klasy grupy Pygame. Będziemy też trochę wyczyścić kod, umieszczając część, która animuje kule (kilka ostatnich linii na listingu 17.2) w funkcji, którą nazwiemy animate(). Funkcja animate() będzie również miała kod do wykrywania kolizji. Kiedy zderzą się dwie kule, sprawimy, że będą odwrócone. Następny listing pokazuje kod.

Listing 17.3 Wykrywanie kolizji za pomocą grupy ikonek zamiast listy import sys, pygame

from random import *

class MyBallClass(pygame.sprite.Sprite):

def __init__(self, image_file, location, speed):

pygame.sprite.Sprite.__init__(self)

self.image = pygame.image.load(image_file)

self.rect = self.image.get_rect() self.rect.left, self.rect.top = location

self.speed = speed def move(self):

self.rect = self.rect.move(self.speed) if self.rect.left < 0 or self.rect.right > width:

self.speed[0] = -self.speed[0]

if self.rect.top < 0 or self.rect.bottom > height:

self.speed[1] = -self.speed[1]

def animate(group):

screen.fill([255,255,255]) for ball in group:

group.remove(ball) <-Usuwa sprite z grupy

(8)

if pygame.sprite.spritecollide(ball, group, False): <-Sprawdza kolizje między spritem a grupą ball.speed[0] = -ball.speed[0]

ball.speed[1] = -ball.speed[1]

group.add(ball) <- Dodaje piłkę do grupy ball.move()

screen.blit(ball.image, ball.rect) pygame.display.flip()

pygame.time.delay(20)

size = width, height = 640, 480 <-Główny program zaczyna się tutaj screen = pygame.display.set_mode(size)

screen.fill([255, 255, 255]) img_file = "beach_ball.png"

group = pygame.sprite.Group() <-Tworzy grupę ikonek for row in range (0, 2):

for column in range (0, 2):

location = [column * 180 + 10, row * 180 + 10]

speed = [choice([-2, 2]), choice([-2, 2])]

ball = MyBallClass(img_file, location, speed)

group.add(ball) <-Dodaje każdą piłkę do grupy running = True

while running:

for event in pygame.event.get():

if event.type == pygame.QUIT:

running = False

animate(group) <-Wywołuje funkcję animate (), przekazując do niej grupę pygame.quit()

Najbardziej interesującą nowością jest sposób działania wykrywania kolizji. Moduł sprite Pygame ma funkcję o nazwie spritecollide(), który szuka kolizji pomiędzy pojedynczym duszkiem i dowolnym duszkiem w grupie. Jeśli sprawdzasz kolizje między ikonkami w tej samej grupie, musisz to zrobić w trzech krokach:

1 Usuń ikonkę z grupy.

2 Sprawdź, czy nie ma kolizji między ikonką a resztą grupy.

(9)

3 Dodaj sprite do grupy.

Dzieje się tak w pętli for w liniach od 21 do 29 (w środkowej części funkcji animate()). Jeśli najpierw nie usuniemy ikonki z grupy, spritecollide() wykryje kolizję między ikonką a sobą, ponieważ znajduje się w grupie. Na początku może to wydawać się dziwne, ale ma to sens, jeśli zastanowisz się przez chwilę.

Uruchom program i zobacz, jak to wygląda. Czy zauważyłeś jakieś dziwne zachowanie? Zauważyłem dwie rzeczy:

■ Gdy piłki zderzają się, robią „jąkanie” lub podwójne uderzenie.

■ Czasami piłka utknie wzdłuż krawędzi okna i trzęsie się przez chwilę.

Dlaczego to się dzieje? Cóż, ma to związek ze sposobem, w jaki napisaliśmy funkcję animate(). Zauważ, że poruszamy jedną kulą, a następnie sprawdzamy jej kolizje, a następnie przesuwamy kolejną kulę, a następnie sprawdzamy jej kolizje i tak dalej. Najpierw powinniśmy wykonać wszystkie ruchy, a następnie wykonać wszystkie kontrole kolizji. Więc chcemy wziąć linię 28, ball.move () i umieścić ją we własnej pętli, tak jak poniżej:

def animate(group):

screen.fill([255,255,255]) for ball in group:

ball.move() <-Najpierw przenosimy wszystkie piłki for ball in group:

group.remove(ball)

if pygame.sprite.spritecollide(ball, group, False):

ball.speed[0] = -ball.speed[0]

ball.speed[1] = -ball.speed[1]

group.add(ball)

screen.blit(ball.image, ball.rect) pygame.display.flip()

pygame.time.delay(20)

Spróbuj tego i sprawdź, czy działa trochę lepiej. Możesz bawić się kodem, zmieniać rzeczy takie jak prędkość (liczba time.delay ()), liczba piłek, oryginalna lokalizacja piłek, losowość i tak dalej, aby zobaczyć, co dzieje się z piłkami.

Kolizja prostokątów a kolizja perfekcyjna pikseli

Jedna rzecz, którą zauważysz, to fakt, że kulki nie zawsze dotykają się całkowicie, gdy „zderzają się”.

Spritecollide () nie używa okrągłego kształtu kuli do wykrywania kolizji. Wykorzystuje rect piłkę, prostokąt wokół piłki. Jeśli chcesz to zobaczyć, narysuj prostokąt wokół obrazu piłki i użyj tego nowego obrazu zamiast zwykłego obrazu piłki plażowej. Zrobiłem dla ciebie jeden, więc możesz spróbować:

img_file = "b_ball_rect.png"

Powinien wyglądać mniej więcej tak:

(10)

Jeśli chcesz, aby kule odbijały się od siebie tylko wtedy, gdy okrągłe części kulek (a nie krawędzie prostokątów) rzeczywiście się dotknęły, musisz użyć czegoś, co nazywa się wykrywaniem kolizji doskonałych w pikselach. Funkcja spritecollide () tego nie robi, ale zamiast tego używa prostszego wykrywania kolizji. Oto różnica. Przy prostym wykrywaniu kolizji dwie kule zderzają się, gdy jakakolwiek część ich prostokątów styka się ze sobą. Dzięki doskonałej detekcji kolizji w pikselach dwie kule zderzają się tylko wtedy, gdy same piłki dotkną, tak:

Perfekcyjne wykrywanie kolizji pikseli jest bardziej realistyczne. (Nie widziałeś żadnych niewidzialnych prostokątów wokół prawdziwych piłek plażowych, prawda?) Ale jest to bardziej skomplikowane w programie. Dla większości rzeczy, które zrobisz w Pygame, poprawne wykrywanie kolizji jest wystarczająco dobre. Doskonałe wykrywanie kolizji w pikselach wymaga więcej kodu i sprawi, że Twoje gry będą działać wolniej, więc używaj ich tylko wtedy, gdy naprawdę tego potrzebujesz. Dostępnych jest kilka modułów doskonałych dla pikseli (co najmniej dwa na stronie Pygame podczas ostatniego sprawdzenia). Niektóre wyszukiwania w sieci znajdą je, jeśli chcesz wypróbować wykrywanie kolizji w sposób idealny dla pikseli

Liczenie czasu

Do tej pory używaliśmy time.delay() do kontrolowania szybkości działania naszej animacji. Ale to nie jest najlepszy sposób, ponieważ kiedy używasz time.delay(), tak naprawdę nie wiesz, jak długo każda pętla będzie. Kod w pętli wymaga trochę czasu (nieznany czas) i wtedy opóźnienie zajmuje więcej czasu (znany czas). Część czasu jest znana, ale część jest nieznana. Jeśli chcesz wiedzieć, jak często działa

(11)

pętla, musisz znać całkowity czas każdej pętli, czyli czas kodu + czas opóźnienia. Aby obliczyć czas animacji, wygodnie jest użyć milisekund, lub tysięcznych części sekundy. Skrót to ms, więc 25 milisekund wynosi 25 ms.

W naszym przykładzie załóżmy, że czas kodu wynosi 15 ms. Oznacza to, że uruchomienie kodu w pętli while trwa 15 ms, nie wliczając w to time.delay(). Znamy czas opóźnienia, ponieważ ustawiamy go na 20 ms za pomocą time.delay(20). Całkowity czas pętli wynosi 20 ms + 15 ms = 35 ms, a w ciągu jednej sekundy jest 1000 ms. Jeśli każda pętla zajmuje 35 ms, otrzymujemy 1000 ms / 35 ms = 28,57. Oznacza to, że otrzymamy około 29 pętli na sekundę. W grafice komputerowej każdy krok animacji nazywany jest ramką, a programiści gier mówią o szybkości klatek i klatkach na sekundę, kiedy omawiają szybkość aktualizacji swojej grafiki. W naszym przykładzie szybkość klatek wynosiłaby około 29 klatek na sekundę lub 29 klatek na sekundę. Problem polega na tym, że tak naprawdę nie można kontrolować części „czasu kodu” równania. Jeśli dodasz lub usuniesz kod, czas się zmieni. Nawet z tym samym kodem, jeśli jest inny numer sprite'ów (na przykład, gdy obiekty gry pojawiają się i znikają), czas potrzebny na ich narysowanie się zmieni. Również ten sam kod będzie działać szybciej lub wolniej na różnych komputerach. Zamiast 15 ms czas kodu może wynosić 10 ms lub 20 ms. Byłoby dobrze, gdyby istniał bardziej przewidywalny sposób kontrolowania liczby klatek na sekundę. Na szczęście moduł czasu Pygame daje nam narzędzia do tego, z klasą o nazwie Zegar.

Sterowanie częstotliwością klatek za pomocą pygame.time.Clock ()

Zamiast dodawać opóźnienie do każdej pętli, pygame.time.Clock () kontroluje, jak często działa każda pętla. To jak zegar, który trzyma startując, mówiąc „Zacznij teraz od następnej pętli! Rozpocznij teraz kolejną pętlę! … ”

Zanim zaczniesz używać zegara Pygame, musisz utworzyć instancję obiektu Clock. To działa tak samo jak tworzenie wystąpienie dowolnej innej klasy:

clock = pygame.time.Clock ()

Następnie, w ciele głównej pętli, mówisz zegarowi, jak często powinien on „zaznaczyć”, czyli jak często pętla powinna działać:

clock.tick (60)

Liczba, którą przekazujesz clock.tick () nie jest liczbą milisekund. Zamiast tego to liczba uruchomień pętli na sekundę. Więc ta pętla powinna działać 60 razy na sekundę. Mówię „powinien działać”, ponieważ pętla może działać tylko tak szybko, jak komputer może ją uruchomić. Przy 60 pętlach (lub klatkach) na sekundę, to 1000/60 = 16,66 ms (około 17 ms) na pętlę. Jeśli uruchomienie kodu w pętli trwa dłużej niż 17 ms, nie zostanie to wykonane przez zegar wskazujący, że ma rozpocząć następną pętlę. Zasadniczo oznacza to ograniczenie liczby klatek na sekundę, które może uruchomić Twoja grafika. Ten limit zależy od złożoności grafiki, rozmiaru okna i szybkości komputera, na którym działa program. W przypadku niektórych programów jeden komputer może działać z szybkością 90 klatek na sekundę, podczas gdy starszy, wolniejszy komputer łączy się z szybkością 10 klatek na sekundę. W przypadku dość złożonej grafiki większość współczesnych komputerów nie będzie miała problemów z uruchomieniem programów Pygame z prędkością od 20 do 30 fps. Jeśli więc chcesz, aby gry działały z taką samą prędkością na większości komputerów, wybierz częstotliwość odświeżania od 20 do 30 klatek na sekundę lub mniej. Jest to wystarczająco szybkie, aby uzyskać płynny ruch. Od teraz użyjemy clock.tick (30) do przykładów w tej książce.

Sprawdzanie liczby klatek na sekundę

(12)

Jeśli chcesz wiedzieć, jak szybko program może działać, możesz sprawdzić częstotliwość klatek za pomocą funkcji o nazwie clock.get_fps(). Oczywiście, jeśli ustawisz wskaźnik sławy na 30, zawsze osiągniesz 30 klatek na sekundę (zakładając, że komputer może działać tak szybko). Aby zobaczyć jak najszybciej dany program może uruchomić na konkretnej maszynie, ustaw clock.tick bardzo szybko (np. 200 fps), a następnie uruchom program i sprawdź rzeczywistą szybkość klatek za pomocą clock.get_fps (). (Wkrótce pojawi się przykład.)

Skalowanie szybkości klatek

Jeśli chcesz mieć pewność, że Twoja animacja działa z taką samą prędkością na każdej maszynie, możesz skorzystać z sztuczki clock.tick() i clock.get_fps(). Ponieważ znasz szybkość, z jaką chcesz biegać i prędkość, na której aktualnie się poruszasz, możesz dostosować lub skalować szybkość animacji w zależności od prędkości maszyny.

Załóżmy na przykład, że masz clock.tick(30), co oznacza, że próbujesz uruchomić z prędkością 30 fps.

Jeśli używasz clock.get_fps() i odkrywasz, że osiągasz tylko 20 fps, wiesz, że obiekty na ekranie poruszają się wolniej niż chcesz. Ponieważ uzyskasz mniej klatek na sekundę, musisz przesuwać obiekty dalej w każdej klatce, aby wyglądały, jakby poruszały się z właściwą prędkością. Prawdopodobnie będziesz mieć zmienną (lub atrybut) nazywaną prędkością dla poruszających się obiektów, która mówi im, jak daleko się poruszać w każdej klatce. Wystarczy zwiększyć prędkość, aby nadrobić wolniejszą maszynę. Ile to zwiększyć? Zwiększasz go o stosunek pożądanych fps / rzeczywistych fps. Jeśli bieżąca prędkość obiektu wynosi 10 dla żądanego 30 klatek na sekundę, a program działa z prędkością 20 klatek na sekundę,

object_speed = current_speed * (pożądane fps / rzeczywiste fps) object_speed = 10 * (30/20)

object_speed = 15

Zamiast przesuwać 10 pikseli na klatkę, przesuwałbyś obiekt o 15 pikseli na klatkę, aby zrekompensować wolniejszą częstotliwość klatek. Oto lista programów do piłki plażowej wykorzystujących rzeczy omówione w ostatnich kilku sekcjach: Zegar i get_fps()

Listing 17.4 Korzystanie z zegara i get_fps () w programie piłki plażowej import sys, pygame

from random import *

class MyBallClass(pygame.sprite.Sprite):

def __init__(self, image_file, location, speed):

pygame.sprite.Sprite.__init__(self)

self.image = pygame.image.load(image_file) self.rect = self.image.get_rect()

self.rect.left, self.rect.top = location self.speed = speed

def move(self):

(13)

self.rect = self.rect.move(self.speed) if self.rect.left < 0 or self.rect.right > width:

self.speed[0] = -self.speed[0]

if self.rect.top < 0 or self.rect.bottom > height:

self.speed[1] = -self.speed[1]

def animate(group):

screen.fill([255,255,255]) for ball in group:

ball.move() for ball in group:

group.remove(ball)

if pygame.sprite.spritecollide(ball, group, False):

ball.speed[0] = -ball.speed[0]

ball.speed[1] = -ball.speed[1]

group.add(ball)

screen.blit(ball.image, ball.rect)

pygame.display.flip() <-time.delay () został usunięty size = width, height = 640, 480

screen = pygame.display.set_mode(size) screen.fill([255, 255, 255])

img_file = "beach_ball.png"

clock = pygame.time.Clock() <-Tworzy instancję zegara

group = pygame.sprite.Group()

for row in range (0, 2):

for column in range (0, 2):

location = [column * 180 + 10, row * 180 + 10]

speed = [choice([-4, 4]), choice([-4, 4])]

ball = MyBallClass(img_file, location, speed) group.add(ball) #add the ball to the group running = True

while running: <-Tutaj rozpoczyna się główna pętla while

(14)

for event in pygame.event.get():

if event.type == pygame.QUIT:

running = False

frame_rate = clock.get_fps() <-Sprawdza szybkość klatek print "frame rate = ", frame_rate

animate(group)

clock.tick(30) <- clock.tick steruje teraz szybkością klatek (ograniczoną prędkością komputera) pygame.quit()

Obejmuje to podstawy Pygame i sprite'ów. W następnym rozdziale zrobimy prawdziwą grę przy użyciu Pygame, a zobaczysz inne rzeczy, które możesz zrobić, takie jak dodawanie tekstu (do wyników gry) oraz wprowadzanie danych za pomocą myszy i klawiatury.

Czego się nauczyłeś?

■ Sprites w Pygame i jak ich używać do obsługi wielu ruchomych obrazów

■ Grupy ikonek

■ Wykrywanie kolizji

■ pygame.clock i liczba klatek na sekundę

Sprawdź swoją wiedzę

1 Co to jest wykrywanie kolizji?

2 Co to jest wykrywanie kolizji w trybie pikselowym i czym różni się od wykrywania bezpośredniego kolizji?

3 Jakie są dwa sposoby śledzenia wielu obiektów sprite razem?

4 Jakie są dwa sposoby kontrolowania szybkości animacji w kodzie?

5 Dlaczego używanie pygame.clock jest bardziej dokładne niż użycie pygame.time.delay ()?

6 W jaki sposób możesz sprawdzić, na jakiej szybkości odtwarzania działa Twój program?

Wypróbuj to

Jeśli wpisałeś wszystkie przykłady kodu w tym rozdziale, wypróbowałeś wystarczająco dużo. Jeśli nie, wróć i zrób to. Obiecuję, że się czegoś nauczysz!

Cytaty

Powiązane dokumenty

[r]

Skoro tu mowa o możliwości odtwarzania, to ma to zarazem znaczyć, że przy „automatycznym ” rozumieniu nie może natu ­ ralnie być mowy o jakimś (psychologicznym)

Dane są dodatnio (prawostronnie) asymetryczne wtedy i tylko wtedy gdy ich funkcja symetrii jest niemalejąca.. Wykres dowolnej funkcji symetrii leży w pewnym

Dla kontrolowania rzędów zer i biegunów funkcji wymiernych wygodnie jest haszować je jako współczynniki grupy abelowej wolnej generowanych przez punkty krzywej E

[r]

Zaªó»my, »e X interpretuje grup¦.. Zaªó»my, »e X

[r]

Utrata zwi¸ azk´ ow fazowych (tzw. koherencji) zredukowanego opera- tora stanu w wyniku ewolucji uk ladu rozszerzonego jest nazywana dekoherencj¸