Глава 21 · Проект: космический шутер

Точное движение с Vector2

Rect хранит только целые числа — позиция живёт отдельно, как Vector2 с плавающей точкой.

pygame.Rect удобен для столкновений и отрисовки, но хранит только целые числа. Если на каждом кадре прибавлять к его координате маленькое дробное значение, оно молча теряется при округлении — движение на очень низкой скорости может вообще не сдвинуться с места, хотя код выглядит правильно.

Vector2 (float)
position += velocity * dt
не теряет дробную часть
round()
округление только в момент отрисовки
Rect (int)
используется для столкновений и blit()
Позиция всегда живёт как Vector2 с плавающей точкой; Rect каждый кадр пересобирается ИЗ неё, а не обновляется напрямую.
fragment_player_move.py
class Player(pygame.sprite.Sprite):
    def __init__(self, image, center):
        super().__init__()
        self.image = image
        self.rect = self.image.get_rect()
        self.position = pygame.Vector2(center)
        self.rect.center = (round(self.position.x), round(self.position.y))

    def move(self, direction, dt, playfield):
        if direction.length_squared() > 0:
            direction = direction.normalize()
        self.position += direction * self.speed * dt
        self.rect.center = (round(self.position.x), round(self.position.y))
direction.normalize() — то же диагональное правило, что и в разделе 20.16
Вектор направления (1, 1) (вправо-вниз одновременно) без нормализации по длине больше единицы — √2 ≈ 1.41. Если не привести его к единичной длине, корабль по диагонали двигался бы на 41% быстрее, чем строго по одной оси.
direction.length_squared() > 0, а не > 0 сразу для длины
У этих двух строк разные задачи. Проверка > 0 — то, что не даёт вызвать normalize() у нулевого вектора (когда игрок вообще не нажимает клавиши движения): нормализация делит вектор на его длину, а длина нулевого вектора равна нулю. А length_squared() (квадрат длины) вместо length() нужен только затем, чтобы не извлекать лишний квадратный корень, когда достаточно знать, ненулевой ли вектор — обычная микрооптимизация в реальном коде на Vector2.

Игровое поле не отпускает корабль

Каждое движение сразу ограничивается границами игрового поля (раздел 21.12) — так же, как max(0, min(...)) в разделе 21.3, но теперь применяется и по X, и по Y, потому что корабль двигается во всех четырёх направлениях.

Практика: Vector2, нормализация и clamp
Проверяется прямо в браузере — установка Pygame не требуется
Практика выполняется локально
Открыть практику →