Скорострельность и интервал между выстрелами
Удержание пробела стреляет с ограниченной частотой — в секундах, а не в кадрах.
Раздел 21.4 стрелял по событию KEYDOWN — оно происходит ровно
один раз на одно физическое нажатие пробела, поэтому даже долгое удержание клавиши там давало
только один выстрел. Финальной версии игры нужна стрельба очередями, пока пробел зажат, а для
этого требуется другой источник данных — pygame.key.get_pressed(),
которая на каждом обновлении сообщает, зажата ли клавиша прямо сейчас. Без ограничений это
означало бы выстрел на каждом обновлении игры, то есть скорострельность, зависящую от FPS: 60
выстрелов в секунду при 60 FPS и 120 — при 120 FPS. Поэтому стрельба через
get_pressed() всегда сопровождается минимальным интервалом между
выстрелами (cooldown, в профессиональной речи — кулдаун), заданным в секундах.
FIRE_INTERVAL = 0.20 # секунд между выстрелами
self.fire_cooldown = max(0.0, self.fire_cooldown - dt)
if keys[pygame.K_SPACE] and self.fire_cooldown <= 0.0:
self._spawn_bullet()
self.fire_cooldown = FIRE_INTERVAL
fire_cooldown -= dt счётчик уменьшался на единицу каждый кадр (kadrov_do_vystrela -= 1), скорострельность зависела бы от FPS точно так же, как нескорректированное движение из раздела 20.16: на 120 FPS корабль стрелял бы вдвое чаще, чем на 60 FPS, при одном и том же коде.Почему здесь не используется while, как в таймере появления врагов
Раздел 21.14 покажет финальный таймер появления врагов: он продвигает время через
+= interval, сохраняя остаток, а while
там нужен на случай, если один обрабатываемый шаг способен охватить сразу несколько интервалов.
Интервал между выстрелами устроен иначе:
| Таймер появления врагов | Интервал между выстрелами игрока | |
|---|---|---|
| Источник события | Автономная симуляция (сама игра решает, когда) | Ввод игрока (человек решает, когда) |
| Долгий обрабатываемый шаг | Если шаг способен охватить несколько интервалов, таймер может обработать несколько событий подряд | не должен выстрелить сразу несколько пуль одним кадром |
| Приём в коде | while: продолжать, пока накоплено время | if: не больше одного выстрела за кадр |
MAX_DT не позволяет одному обработанному шагу стать длиннее минимального интервала появления врагов (подробности — в разделе 21.14) — поэтому в этой конкретной игре ни пауза отладчика, ни зависание ОС не приводят к тому, что игра «наверстывает» всё накопленное реальное время залпом из нескольких врагов сразу.dt оказался большим.