Предыдущие части: Подсчёт очков, Главное меню, Геймплей, Есть ли у него душа, Организация ввода, View, Визуализация поля, Загрузка уровня, INI-файл, Пишем Питона на Питоне!
Код для этой части находится в ветке modals на github. Вы можете смотреть там все файлы онлайн и также скачать зип-архив всей ветки.
Вводя в игру жизни, мы должны построить целую инфраструктуру. Жизни могут заканчиваться, а после этого наступает Game Over. То есть появляются новые события и новая логика переходов между экранами.
Начнём с разработки модальных окон. Это диалоговые окна, которые располагаются поверх всего и блокируют всё остальное. Вы с ними наверняка часто сталкивались:
Для нас модальные окна будут ещё одним видом представления. В них будут размещаться сообщения и простые меню:
У нас уже есть механизм переключения контроллеров и представлений. В начале игры работает контроллер MainMenuController с представлением MainMenuView. После выбора пункта "Play" загружается GameController с представлением GameView. Если во время игры нажать клавишу Esc, то нужно показать модальное окно с меню игры. Проблем с этим нет, надо просто загрузить новый контроллер и новое представление.
Но когда мы закроем модальное окно, то должны вернуться в игру и продолжить с того места, где остановились. А этого места уже нет, потому что и контроллер, и представление были заменены.
Поэтому для модальных окон порядок показа должен быть другой. Вместо замены контроллера мы должны сохранять его в стеке (контроллер хранит внутри себя ссылки на модель и представление, поэтому они сохранятся тоже). Когда отработает контроллер модального окна, из стека возвращается предыдущий контроллер, и игру можно продолжать.
Добавим в класс Game режим MODE_PAUSE и методы push_mode() и pop_mode(). В них содержится простейшая работа со стеком (свойство controller_stack). Также добавим метод reset_mode(), который переходит в другое состояние, при этом очищая стек (например, из модального окна сразу в главное меню).
Теперь уберём из главного цикла в main.py обработку нажатия Esc (это был выход из игры) и передадим её в контроллер GameController. Он будет вызывать game.push_mode(Game.MODE_PAUSE) при нажатии Esc.
Сделаем для режима паузы представление PauseView. Но так как модальные окна будут разные, а стиль у них один и тот же, то сначала сделаем общий класс ModalView.
В его задачи будет входить: загрузка тени от окна (свойство img_shadow) и фона окна (свойство img_back), центровка и вывод их на экран. Все остальные элементы будут дорисовываться классами-потомками.
Класс PauseView, отнаследованный от ModalView, дополнительно отрисует заголовок окна ("Pause"), подсказку на закрытие ("Esc: x") и меню из трёх пунктов:
Для меню будет использоваться та же модель, что и в главном меню (MainMenuModel). И так как они идентичны, придадим классу MainMenuModel универсальности, переименовав его в просто MenuModel, и будем использовать повторно.
Сделаем для режима паузы контроллер PauseController. Он практически не отличается от MainMenuController: с помощью нажатия стрелок вверх и вниз в нём перебираются пункты меню. И так как код почти идентичный, мы можем отнаследовать их от общего класса, но пока не будем отвлекаться. При выборе пункта "Main menu" контроллер переключит игру в режим главного меню, а при выборе пункта "Continue" достанет из стека предыдущий контроллер, и игра продолжится. При выборе пункта "Sound" пока ничего не будет происходить.
И вот, наконец, во время игры мы можем вызвать модальное окно:
Теперь, имея необходимую инфраструктуру, займёмся подсчётом жизней. Так же как очки, будем хранить жизни в классе Game (свойство lives). Первоначально их будет 3. Добавим в GameController логику, которая уменьшает жизни на 1 при столкновении. Но главное, что при достижении нуля нужно показать другое модальное окно – "Game Over", и вернуться после этого в главное меню.
Чтобы сделать это окно, создадим представление GameOverView, отнаследованное от ModalView, и контроллер GameOverController, чьей задачей будет обслуживать один-единственный пункт меню: возврат в главное меню. Модель меню будет представлена универсальным классом MenuModel. Всё это делается абсолютно аналогично предыдущему модальному окну, так что объяснять особо нечего. Также получится много повторяющегося кода, но это задача на попозже.
Вывод набранных очков и длины в этом окне тоже сделаем позже, когда займёмся таблицей рекордов.
Ну а пока осталось только сделать отображение количества жизней во время игры. Символом жизни будет свернувшийся питончик – он же используется как указатель в меню. Кроме него, нужно сделать такой же, но тёмного цвета, чтобы обозначить потраченную жизнь.
Эти символы будут выводиться в верхней полоске в GameView следующим образом:
- Вывести тёмный символ 3 - lives раз подряд
- Вывести светлый символ lives раз подряд
Всё, теперь можно не просто играть, но и переживать за свою жизнь.
В проекте были сделаны и прочие доделки типа перемещения и переименования файлов для большей стройности.
В следующем выпуске будем делать таблицу рекордов. Или прикручивать звук? Да. Его.
Напоминаю: все вопросы вы можете написать в комментариях.
Читайте также:
- ООП в Python: особенности реализации
- Что такое стек