Rozdział 23 · Aneks

Projekt domowy D: odbijające się piłki z Pygame

Pozycja i prędkość są przechowywane w Vector2, natomiast ruch jest brany pod uwagę, dt. Prędkość mierzona jest w jednostkach na sekundę, a nie na klatkę.

Piłka jest opisywana przez klasę Myach. Pozycja i prędkość są przechowywane jako pygame.Vector2, a prędkość mierzona jest w pikselach w drugi, nie w pikselach na klatkę:

bouncing_balls.py
class Myach:
    def __init__(self, x, y, vx, vy, radius, cvet):
        self.pos = Vector2(x, y)
        self.velocity = Vector2(vx, vy)  # pikseli na sekundę
        self.radius = radius
        self.cvet = cvet
        self.otskokov = 0

    def shag(self, dt: float) -> None:
        self.pos += self.velocity * dt
        if self.pos.x - self.radius < 0 or self.pos.x + self.radius > SHIRINA:
            self.velocity.x = -self.velocity.x
            self.otskokov += 1
        # ... To samo dotyczy self.pos.y i VYSOTA
Uwzględniamy dt przy zmianie pozycji
Rozdział 20 już pokazało ten błąd: jeśli dodasz prędkość do pozycji dla każdej klatki bez uwzględnienia dt (rzeczywistego czasu, jaki upłynął od ostatniej klatki), piłka na ekranie z częstotliwością 120 klatek na sekundę odleci czterokrotnie dalej w tej samej sekundzie, niż przy 30 klatkach na sekundę. Mnożenie przez dt wyraża prędkość w jednostkach na sekundę i znacznie zmniejsza zależność od FPS. Swobodny, jednorodny ruch pokrywa się w różnych FPS w ramach dokładności numerycznej, ale dyskretne wykrywanie kolizji może nadal zależeć od wielkości kroku czasowego.

Okno i pętla renderowania są tworzone tylko wewnątrz main(). Import pliku bouncing_balls.py nie otwiera okna pygame, ale Więc, Myach.shag() można sprawdzić testem bez wyświetlacza:

bouncing_balls.py
def main() -> None:
    pygame.init()
    screen = pygame.display.set_mode((SHIRINA, VYSOTA))
    clock = pygame.time.Clock()
    myachi = sozdat_myachi()
    while rabotaet:
        dt = clock.tick(FPS) / 1000
        for myach in myachi:
            myach.shag(dt)
        narisovat_kadr(screen, myachi)

Pełny kod: projects/pygame/bouncing-balls-oop/bouncing_balls.py

Quest

★★ Zadanie samodzielne
Uproszczona kolizja czołowa

Dla uproszczenia edukacyjnego rozważ jedynie frontalne zderzenie identycznych piłek: środki i prędkości leżą na tej samej linii, kule się zbliżają. W tym ograniczonym przypadku wymień cały velocity. Nie nazywaj tej techniki ogólnym wzorem 2D-kolizja.

Dlaczego ograniczenie jest konieczne
Przy dowolnym 2D-zderzeniu zmieniają się normalne składowe prędkości wzdłuż linii środków, a styczne pozostają; potrzebna jest również kontrola zbliżenia i korekcja przenikania. Wymiana pełnych wektorów jest poprawna tylko w sformułowanym modelu czołowym o jednakowych masach.
GitHubPraktyka GitHub

Issue «Add bouncing balls project», gałąź feat/bouncing-balls, FPS Test Niezależności Ruchu (30/60/120 klatek daje ten sam wynik w jednym i w tym samym momencie), Pull Request.

Praktyka: klasa Myach i kilka piłek naraz
Pygame otwiera natywne okno Python. Wykonaj ćwiczenie lokalnie w VS Code, PyCharm lub Jupyter
Praktyka kursuje lokalnie
Otwórz praktykę →