Перейти к основному содержимому

Python — Space Invaders

Разработчику Начальный уровень

О практикуме

В 1978 году Taito выпустила аркаду Space Invaders. Игрок стоит внизу экрана, стреляет вверх, а ряды пришельцев ползут в сторону; у края вся стая разворачивается и опускается на один ряд. Этот шаблон до сих пор встречается в учебных проектах и инди-играх.

Здесь соберём такую же механику на Python 3 и Pygame. Графика — цветные прямоугольники (pygame.draw, Rect), без загрузки PNG. Так проще сосредоточиться на игровом цикле, столкновениях и состоянии матча. Картинки и звуки подключите позже по инструкции из раздела Где брать графику и звук.

Что понадобится заранее

На каждом этапе вы копируете целый main.py, запускаете python main.py и проверяете одну новую механику.

Слова, которые встретятся в коде

ТерминКоротко
Игровой циклБесконечный while, на каждом кадре — события, логика, отрисовка. Подробнее в главе 312.
RectПрямоугольник с координатами и размером; им задают позицию корабля, пули и пришельца.
colliderectПроверка, пересекаются ли два Rect — основа попадания пули по врагу.
Кулдаун (fire_cd)Счётчик кадров между выстрелами, чтобы нельзя было зажать Пробел и залить экран пулями.
СтаяВсе пришельцы двигаются синхронно, как в оригинальной аркаде.
UFOЛетающая тарелка-бонус; в оригинале пролетает сверху и даёт случайное число очков.

Управление в финальной версии

КлавишаДействие
/ AКорабль влево
/ DКорабль вправо
ПробелВыстрел (с паузой между выстрелами)
RНовый матч после победы или поражения
EscВыход

Ориентир по времени — 3–5 часов на этапы 0–8, ещё около часа на модульную ревизию.

Карта этапов

ЭтапТемаЧто появится в игре
0Запуск PygameТёмное окно 640×720
1ИгрокКорабль внизу, движение по стрелкам
2ПулиВыстрел по Пробелу
3Сетка врагов5 рядов по 11 пришельцев
4Движение стаиРазворот у края и шаг вниз
5ПопаданияСчёт и ускорение стаи
6БомбыЖизни игрока
7Бонусная тарелкаПролёт UFO и случайные очки
8Конец раундаЭкран победы или поражения, клавиша R
РевизияМодулиsettings.py и пакет game/

Зависимости и папка проекта

Создайте отдельную папку и виртуальное окружение — так проще не смешать Pygame с другими проектами.

mkdir invaders && cd invaders
python -m venv .venv
# Windows: .venv\Scripts\activate
pip install pygame

Файл requirements.txt для сдачи работы или GitHub:

pygame>=2.5.0

Структура на этапах 0–8:

  • один файл main.py в корне invaders/;
  • терминал открыт в этой папке;
  • команда запуска — python main.py.

После этапа 8 можно разнести код по файлам — см. полную ревизию.


Как устроена игра

Каждый кадр (обычно 60 раз в секунду) программа повторяет одну и ту же цепочку:

Роли основных объектов:

  • Игрок — один pygame.Rect внизу экрана; сдвигается только по горизонтали.
  • Пули — список маленьких Rect, каждый кадр поднимаются вверх.
  • Пришельцы — список структур с полем rect, цветом ряда и очками за уничтожение.
  • Бомбы — падающие Rect; сбрасывает случайный пришелец из нижнего живого ряда.
  • UFO — отдельный объект; летит справа налево, за попадание даёт бонус.

Тот же приём "список объектов + цикл обновления" используется в Tetris и Ping Pong — меняется только правило движения.


Этап 0 — минимальный запуск

Цель — убедиться, что Pygame установлен, окно открывается и закрывается без ошибок.

Создайте main.py. Внутри — pygame.init(), окно, цикл while running, заливка фона, display.flip() и clock.tick(60). Выход — по крестику окна или по Esc.

Пример кода загружается. Это пример интеграции — сейчас мы запрашиваем данные из другой системы.

Самопроверка

  • Окно 640×720, тёмный фон.
  • После Esc процесс завершается, консоль не зависает.

Разбор. clock.tick(60) ограничивает частоту кадров и не даёт циклу загрузить процессор на 100%. Без tick игра может работать слишком быстро или "съедать" весь CPU.


Этап 1 — корабль игрока

Цель — нарисовать корабль и двигать его по нижней линии экрана.

Добавляется player = pygame.Rect(...) и чтение клавиш через pygame.key.get_pressed() каждый кадр (пока клавиша зажата, корабль едет). Ограничение player.x = max(8, min(...)) не выпускает прямоугольник за левый и правый край.

Пример кода загружается. Это пример интеграции — сейчас мы запрашиваем данные из другой системы.

Самопроверка

  • Корабль реагирует на / и A/D.
  • У правого и левого края корабль останавливается.

Разбор. get_pressed() возвращает состояние всех клавиш сейчас, в отличие от event.key в KEYDOWN, который срабатывает один раз на нажатие. Для непрерывного движения удобнее именно get_pressed(). Тот же приём — в Lab, Invaders lite.


Этап 2 — стрельба

Цель — по Пробелу создавать пулю над кораблём и двигать её вверх.

Пуля — ещё один Rect в списке bullets. Событие pygame.KEYDOWN с K_SPACE добавляет новую пулю. Переменная fire_cd (кулдаун) считает кадры до следующего разрешённого выстрела.

Пример кода загружается. Это пример интеграции — сейчас мы запрашиваем данные из другой системы.

Самопроверка

  • Пули летят вверх и исчезают за верхним краем.
  • При зажатом Пробеле выстрелы идут с паузой, а не сотней пуль за секунду.

Разбор. Список bullets перебирают с копией for b in bullets[:] — так безопасно удалять элементы во время цикла. Про списки и мутацию — в Python, коллекции.


Этап 3 — сетка пришельцев

Цель — расставить врагов рядами и задать разные очки по высоте.

Функция create_aliens() строит сетку 5×11. Верхний ряд приносит больше очков (массив ROW_POINTS). На этом этапе пришельцы стоят на месте — проверьте, что сетка ровная и центрирована.

Пример кода загружается. Это пример интеграции — сейчас мы запрашиваем данные из другой системы.

Самопроверка

  • Пять рядов, в каждом одиннадцать врагов.
  • Цвета рядов различаются (в коде заданы в ROW_COLORS).

Разбор. Координата y ряда считается как ALIEN_TOP + row * (высота + отступ). Так же строят сетку в Tetris и Match-3 — меняются только размер ячейки и правила заполнения.


Этап 4 — движение стаи

Цель — заставить всех пришельцев двигаться влево-вправо синхронно и опускаться у края экрана.

Переменная alien_dir хранит направление (1 или −1). Если любой живой пришелец касается края, направление меняется на противоположное (alien_dir *= -1), и весь ряд сдвигается вниз на ALIEN_DROP. Функция alien_speed() увеличивает скорость, когда врагов остаётся меньше — как в оригинальной аркаде.

Пример кода загружается. Это пример интеграции — сейчас мы запрашиваем данные из другой системы.

Самопроверка

  • Стая доходит до края, разворачивается и опускается.
  • После уничтожения части врагов (на следующих этапах) движение становится быстрее.

Разбор. Жанр fixed shooter (стрельба с фиксированной линии игрока) разобран в классификации компьютерных игр — Space Invaders там часто приводят как пример.


Этап 5 — попадания и счёт

Цель — уничтожать пришельцев пулями и показывать счёт на экране.

Для каждой пули вызывается bullet.colliderect(alien.rect). При попадании у пришельца alive = False, пуля удаляется, к score прибавляются очки ряда. Мёртвые враги не рисуются.

Пример кода загружается. Это пример интеграции — сейчас мы запрашиваем данные из другой системы.

Самопроверка

  • Счёт растёт при попадании.
  • Верхний ряд даёт больше очков, чем нижний.

Разбор. colliderect сравнивает только прямоугольники, не "прозрачные" пиксели картинки. Для PNG со сложной формой позже можно перейти на collide_mask — это описано в главе 312.


Этап 6 — бомбы и жизни

Цель — добавить ответный урон от врагов и запас жизней игрока.

Раз в несколько кадров случайный пришелец из самого нижнего живого ряда создаёт бомбу (Rect под ним). Бомба падает вниз; при пересечении с player жизнь уменьшается. При lives <= 0 включается флаг поражения.

Пример кода загружается. Это пример интеграции — сейчас мы запрашиваем данные из другой системы.

Самопроверка

  • На экране видно "Жизни: 3" (и меньше после попадания).
  • Бомбы падают только с нижнего ряда стаи, а не с верхнего.

Разбор. Счётчик bomb_cd задаёт паузу между сбросами бомб. Без него экран быстро заполнился бы падающими прямоугольниками. Жизни (lives) — целое число; при нуле матч считается проигранным.


Этап 7 — бонусная тарелка

Цель — периодически запускать UFO и давать случайный бонус за попадание.

Каждые UFO_INTERVAL_MS миллисекунд (в коде около 20 секунд) справа появляется тарелка. Очки за неё выбираются случайно (random.choice). Попадание пули начисляет бонус; на месте тарелки кратко показывается число очков.

Пример кода загружается. Это пример интеграции — сейчас мы запрашиваем данные из другой системы.

Самопроверка

  • Тарелка пролетает сверху справа налево.
  • За разные пролёты можно получить разное число очков.

Разбор. Время берётся из pygame.time.get_ticks() — миллисекунды с момента pygame.init(). Так же считают таймеры в Lab, выживание 30 секунд.


Этап 8 — победа, поражение и рестарт

Цель — завершать матч по правилам и начинать заново по R.

Условия победы:

  • живых пришельцев не осталось.

Условия поражения:

  • стая опустилась до линии игрока;
  • жизни закончились.

По клавише R сбрасываются счёт, жизни, списки пуль и бомб, заново вызывается create_aliens().

Пример кода загружается. Это пример интеграции — сейчас мы запрашиваем данные из другой системы.

Самопроверка

  • После уничтожения всех врагов — сообщение о победе.
  • Если пришельцы дошли до низа или жизни = 0 — поражение.
  • R запускает новую волну с чистого листа.

Разбор. Флаги won и game_over останавливают обновление логики, но цикл рисования продолжается — игрок видит итоговый текст. Похожая машина состояний (меню → игра → конец) есть в Lab, заготовка 5.2 и в Ping Pong, этап 13.


Полная ревизия файлов

Однофайловый main.py на этапе 8 уже длинный. Разнесение по модулям облегчает правки: константы отдельно, логика стаи отдельно, точка входа — короткая.

Целевая структура:

invaders/
main.py
settings.py
game/
__init__.py
game.py
player.py
bullets.py
alien_wave.py
ufo.py

settings.py — ширина окна, скорости, цвета, число рядов. Все "магические числа" в одном месте.

Пример кода загружается. Это пример интеграции — сейчас мы запрашиваем данные из другой системы.

main.py — только создание Game() и вызов run().

Пример кода загружается. Это пример интеграции — сейчас мы запрашиваем данные из другой системы.

game/game.py — класс Game: события, обновление, отрисовка, сброс матча.

Пример кода загружается. Это пример интеграции — сейчас мы запрашиваем данные из другой системы.

Остальные файлы пакета game/:

Пример кода загружается. Это пример интеграции — сейчас мы запрашиваем данные из другой системы.
Пример кода загружается. Это пример интеграции — сейчас мы запрашиваем данные из другой системы.
Пример кода загружается. Это пример интеграции — сейчас мы запрашиваем данные из другой системы.
Пример кода загружается. Это пример интеграции — сейчас мы запрашиваем данные из другой системы.

Скопируйте файлы в одну папку invaders/, активируйте venv, выполните из корня проекта:

python main.py

Тот же приём модулей — в Ping Pong и Racing.

Идеи для продолжения

  • Спрайты PNG — заменить draw.rect на blit после загрузки картинок. Источники — Где брать графику и звук.
  • Звуки выстрела и взрываpygame.mixer.Sound, раздел Изображения и звук.
  • Баррикады — несколько статичных Rect между игроком и стаей, как в оригинале.
  • Несколько волн — после победы снова create_aliens(), уменьшить bomb_cd для сложности.
  • Крестики-нолики с ИИ — другой жанр, но те же клики мыши по сетке — Lab §4.3.

См. также


В подборках

Статья входит в тематические подборки и блок "С чего начать?" на главной. Соседние шаги того же маршрута:

Разработка видеоигрПрактикум — о разделе, Разработка игр на Python, Pygame — мини-игры, Разработка игр — о разделе.