$ sudo teach IT
Модуль 7 · Финал · Урок 7.4

Финал и куда дальше

Вы увидите «Комету» целиком, от нажатия клавиши до картинки на экране, и выберете, что строить дальше.

Обзор~20 минутСредний

Игра готова: у неё есть меню, пауза, уровни, звук, рекорд на диске, и вы уже умеете собрать её в один файл для друзей. Осталось последнее — отойти на шаг назад и увидеть «Комету» целиком, как видит её автор, который помнит, что за что отвечает. Тогда следующую идею вы будете вставлять в нужное место, а не искать наугад.

Что получилось: «Комета» по файлам

Представьте небольшую студию. В main.py сидит режиссёр: он включает свет, следит за временем и решает, какую сцену показывать. В sprites.py работают актёры: у каждого свой вид и своё движение. В logic.py лежит свод правил, который не знает ни про сцену, ни про зрителей. В settings.py — доска с цифрами, которые можно крутить, не лезя в сценарий.

Это и есть главный приём курса: логика отдельно от отрисовки. Поэтому logic.py проверяется без окна, а задачи в этом курсе проверяли именно её.

ФайлЗа что отвечаетГлавные имена
main.pyокно, игровой цикл, экраны и состоянияhandle_event, update_play, draw_play, draw_menu, draw_pause, draw_gameover, draw_frame, new_game, load_record, save_record
sprites.pyвсё, что можно нарисовать и подвигатьPlayer, Meteor, Crystal, Laser, Explosion, load_image, load_frames, load_sound
logic.pyправила игры на чистом Python, без pygamestep_ship, SpawnTimer, Cooldown, next_state, handle_key, level_for_score, parse_record, resource_dir
settings.pyвсе числа и цвета в одном местеWIDTH, HEIGHT, FPS, PLAYER_SPEED, INVULNERABLE_TIME, SCORE_PER_LEVEL
check_logic.pyбыстрые проверки logic.py через assertзапуск без окна, за секунду
make_assets.py и assets/скрипт рисует картинки и звуки, папка их хранитship.png, meteor.png, shot.wav, music.wav
record.jsonрекорд между запусками{"record": 1234}

Управление в готовой игре

Запомните и клавиши: каждая из них проходит через handle_key или get_pressed().

  • Меню: стрелки вверх и вниз выбирают пункт, Enter подтверждает, Esc закрывает игру.
  • Полёт: стрелки или WASD двигают корабль, Пробел стреляет, P или Esc ставят паузу.
  • Пауза: P или Esc продолжают игру, Q возвращает в меню.
  • Игра окончена: Enter начинает заново, Q ведёт в меню, Esc закрывает игру.
  • Везде: M включает и выключает звук, F1 показывает и прячет рамки столкновений.

Путь одного кадра

Игра 60 раз в секунду проходит один и тот же путь. Посмотрим его целиком, по именам из вашего main.py:

  1. dt = clock.tick(FPS) / 1000 — узнаём, сколько секунд прошло с прошлого кадра. Все скорости и таймеры дальше считаются от этого числа.
  2. handle_event(event) — для каждого события из очереди: нажатая клавиша превращается в имя через pygame.key.name, handle_key(state, name) отвечает, что это значит в текущем состоянии, а next_state меняет состояние.
  3. update_play(dt) — только если state == "play". Таймеры invulnerable, Cooldown, SpawnTimer; рождение метеоров, кристаллов и выстрелов; all_sprites.update(dt, keys) сдвигает всех; затем столкновения, очки, жизни, смена уровня. Если жизни кончились, next_state(state, "die"), и рекорд уходит в record.json.
  4. draw_frame() — заливка BG_COLOR, звёзды, затем draw_play, draw_menu, draw_pause или draw_gameover по состоянию.
  5. pygame.display.flip() — готовая картинка выводится на экран, и всё начинается заново.

Проверим схему на примере. Вы нажали P посреди игры. Событие KEYDOWN попадает в handle_event, имя клавиши "p", handle_key("play", "p") отвечает "pause", next_state переводит игру в "pause". На следующем кадре условие state == "play" ложно, и update_play не зовётся: метеоры замерли. А draw_frame рисует и мир, и поверх него draw_pause. Вся пауза — это один if в цикле и одна запись в таблице переходов.

С файлами то же самое. Картинки и звуки ищутся через ASSETS, который строится от logic.resource_dir, и поэтому игра находит их и рядом с исходниками, и внутри сборки PyInstaller. Рекорд лежит по пути RECORD_PATH, рядом с программой.

Идеи развития: где что менять

У каждой хорошей идеи есть своё место в проекте. Если сразу понять, где она будет жить, работа превращается из «не знаю, с чего начать» в список маленьких шагов.

ИдеяПравило в logic.pyКартинка и объектыПодключение в main.py
Бонус-щитсколько секунд защиты и как она складывается с invulnerableкласс Shield в sprites.py по образцу Crystal; картинка в make_assets.pyгруппа shields, таймер появления, при подборе — invulnerable растёт
Боссзапас здоровья и урон: функция, которая возвращает новое здоровьекласс Boss с большим radiusпоявляется при определённом level, выстрелы отнимают здоровье
Второе оружиеперезарядка и смещения выстреловновый класс по образцу Laserсвоя клавиша и свой Cooldown в update_play
Таблица рекордовparse_record читает список, новая функция вставляет результат и обрезает до пяти—record.json хранит список; экран "gameover" показывает строки

Заметьте: у каждой идеи первая колонка про правила. Это не случайно. Сначала вы договариваетесь с игрой о числах и условиях, проверяете их без окна, и лишь потом рисуете.

Покажем это на самом маленьком шаге щита. Допустим, подобранный щит даёт пять секунд защиты, а если защита уже есть, берётся большее значение: бонус не должен её укорачивать.

Python · фрагмент (пример идеи, в проект пока не вставляем)
SHIELD_TIME = 5.0


def pick_shield(invulnerable, shield_time):
    return max(invulnerable, shield_time)


assert pick_shield(0.5, SHIELD_TIME) == 5.0
assert pick_shield(8.0, SHIELD_TIME) == 8.0

Разбор по строкам

  • SHIELD_TIME = 5.0 — число живёт отдельно, как в settings.py: захотите семь секунд, поменяете в одном месте.
  • def pick_shield(invulnerable, shield_time) — правило в виде функции. Ни спрайтов, ни окна, только числа.
  • return max(invulnerable, shield_time) — берём большее из двух: оставшуюся неуязвимость или время щита.
  • assert ... == 5.0 — проверка: при почти закончившейся неуязвимости щит даёт полные пять секунд.
  • assert ... == 8.0 — проверка: щит не отнимает лишнего у того, у кого защиты больше.

Маленькими шагами

Работайте так: правило с проверкой, затем объект, затем подключение, и после каждого шага запускайте игру. Если что-то сломалось, виноват последний шаг, а не вся неделя работы.

Чему вы научились

Вы начинали с чёрного окна, а закончили игрой, у которой есть состояния, звук и сохранение. Важнее другое: почти всё, что вы освоили, не принадлежит ни Pygame, ни Python. Это общий язык игровых движков, просто в каждом он называется по-своему.

В «Комете»Что это такоеКак встретите в Unity
while running и clock.tickигровой цикл: кадр за кадромметод Update(), который движок зовёт каждый кадр
dtвремя кадра в секундах, чтобы скорость не зависела от FPSTime.deltaTime
Sprite и Groupобъекты игры и наборы объектовигровые объекты (GameObject) на сцене
Rect и круги столкновенийформа, по которой проверяются касанияколлайдеры (Collider)
state и next_stateсостояния игры и переходы между нимисцены и конечные автоматы
assets/картинки и звуки отдельно от кодапапка Assets проекта

Поэтому переходить на другой движок вам будет проще, чем кажется. Незнакомыми будут кнопки и названия, а вопросы останутся прежними: что происходит каждый кадр, как зависит скорость от времени, кто с кем сталкивается и в каком состоянии сейчас игра.

Есть и вторая, менее заметная победа: привычка разделять правила и картинку. Задача в конце урока как раз об этом. Весь мир — обычный словарь, один кадр — одна функция, окна нет, а проверить можно за мгновение.

Куда дальше

Дальше две дороги, и они не исключают друг друга.

  • Python. Если во время курса вы чувствовали, что классы, словари и файлы вам даются с усилием, пройдите бесплатный курс по Python: https://sudoteach.com/course/python. Там основы разобраны подробнее, включая объектно-ориентированное программирование. Хорошо подтянутый Python сделает следующую игру вдвое быстрее.
  • Unity. Если хочется 3D и большие проекты, переходите на курс по Unity: https://sudoteach.com/course/unity. Игры там пишутся на C#, а идеи те же, что в таблице выше: Update, время кадра, коллайдеры.

Что делать прямо сейчас

Не откладывайте. Выберите одну идею из таблицы, разбейте её на три шага и сделайте первый сегодня.

Практика: проверьте и выберите шаг

Нового кода в игре сегодня нет. Ваша задача — пройти проект как игрок и как автор, а затем выбрать следующий шаг.

  1. Откройте терминал в папке kometa/ с включённым виртуальным окружением и запустите проверки логики: python check_logic.py. Должно появиться «Все проверки пройдены».
  2. Запустите игру: python main.py. Пройдите весь круг: меню, игра, пауза клавишей P, продолжение, потеря всех жизней, «Игра окончена», Enter для новой партии.
  3. Закройте игру и откройте снова. Рекорд в меню должен остаться на месте — значит, файл record.json работает.
  4. Выберите идею из таблицы и запишите на листке три шага: правило, объект, подключение.
  5. Соберите игру в dist/ и покажите её хотя бы одному человеку. Лучше посмотреть, как друг играет молча, чем объяснять ему управление.
Терминал
python check_logic.py
python main.py
Проверьте себя
  • Могу ли я назвать пять шагов одного кадра по именам из своего main.py?
  • Я знаю, в каком файле лежит число 1.5 (время неуязвимости), а в каком — таблица клавиш KEY_ACTIONS?
  • Проверки из check_logic.py проходят без запуска окна?
  • Рекорд сохраняется между запусками, а игра с чистой папкой (без record.json) не падает?
  • У меня записаны три шага для одной следующей идеи?

Частые ошибки (как советы)

  • Переписывать всё с нуля. Новый проект кажется чище, но через неделю он станет таким же. Вместо этого доработайте старый маленькими шагами: рабочая игра на каждом шаге.
  • Бросить, не показав. Игра, в которую никто не играл, не покажет своих слабых мест. Отдайте её друзьям сегодня, даже если она «ещё сырая».
  • Делать три идеи сразу. Босс, щит и ракеты в один вечер приводят к ошибке, у которой не видно виновника. Одна идея, один запуск.
  • Начинать с картинки. Красивый босс без правил про его здоровье придётся переделывать. Сначала правило и проверка, потом внешний вид.
  • Сдаться после первой ошибки. Читайте последнюю строку сообщения, ищите имя файла и номер строки: так вы исправите её быстрее, чем кажется.

Что важно запомнить

  • «Комета» держится на разделении: logic.py — правила, sprites.py — объекты, main.py — цикл и экраны, settings.py — числа.
  • Один кадр: dt, события, обновление мира (только в "play"), отрисовка, flip.
  • Новая идея начинается с правила и проверки в check_logic.py, а не с картинки.
  • Игровой цикл, dt, объекты, коллайдеры и состояния есть в любом движке, включая Unity.
  • Развивайте проект маленькими шагами и показывайте его людям.

Вы дошли до конца курса — а значит, умеете довести проект до готового файла. Следующий курс (Python или Unity) начнёт с того места, где остановилась «Комета».