Мэтиз. Изучаем Python. Crash course2 n d e d i t i o na h a n d s o n, p r o j e c t b a s e d i n t r o d u c t i o n t o p r o g r a m m i n g
Скачать 6.2 Mb.
|
scoreboard.py def show_score(self): """Выводит текущий счет, рекорд и число оставшихся кораблей.""" self.screen.blit(self.score_image, self.score_rect) self.screen.blit(self.high_score_image, self.high_score_rect) self.screen.blit(self.level_image, self.level_rect) Подсчет очков 313 Добавленная строка выводит на экран изображение, представляющее уровень. Увеличение stats.level и обновление изображения уровня выполняются в _check_ bullet_alien_collisions() : alien_invasion.py def _check_bullet_alien_collisions(self): if not self.aliens: # Уничтожить существующие снаряды и создать новый флот. self.bullets.empty() self.create_fleet() self.settings.increase_speed() # Увеличение уровня. self.stats.level += 1 self.sb.prep_level() Если все пришельцы уничтожены, программа увеличивает значение stats.level и вызывает prep_level() для обновления уровня. Чтобы убедиться в том, что изображения текущего счета и уровня правильно об- новляются в начале новой игры, мы вызываем prep_level() при нажатии кнопки Play : alien_invasion.py def _check_play_button(self, mouse_pos): if button_clicked and not stats.game_active: self.sb.prep_score() sb.prep_level() Метод prep_level() вызывается сразу же после вызова prep_score() Теперь количество пройденных уровней отображается на экране (рис. 14.5). ПРИМЕЧАНИЕ В некоторых классических играх выводимая информация снабжается текстовыми метками: «Уровень», «Рекорд» и т . д . Мы эти метки опустили, потому что смысл каждого числа понятен каждому, кто сыграл в Alien Invasion . Если вы включи- ли эти метки, добавьте их в строки непосредственно перед вызовами font.render() в Scoreboard . Вывод количества кораблей Остается вывести количество кораблей, оставшихся у игрока, но на этот раз инфор- мация будет выводиться в графическом виде. Как во многих классических аркад- ных играх, в левом верхнем углу экрана программа рисует несколько изображений корабля. Каждый корабль обозначает одну оставшуюся попытку. 314 Глава 14 • Ведение счета Рис. 14.5. Текущий уровень выводится под текущим счетом Для начала нужно сделать так, чтобы класс Ship наследовал от Sprite — это не- обходимо для создания группы кораблей: ship.py import pygame from pygame.sprite import Sprite ❶ class Ship(Sprite): # Класс для управления кораблем. def __init__(self, ai_game): """Инициализирует корабль и задает его начальную позицию.""" ❷ super().__init__() Здесь мы импортируем Sprite , объявляем о наследовании Ship от Sprite и вы- зываем super() в начале __init__() . Далее необходимо изменить Scoreboard и создать группу кораблей для вывода на экран. Команды import выглядят так: scoreboard.py import pygame.font from pygame.sprite import Group from ship import Ship Подсчет очков 315 Так как мы собираемся создать группу кораблей, программа импортирует классы Group и Ship Метод __init__() выглядит так: scoreboard.py def __init__(self, ai_game): """Инициализирует атрибуты подсчета очков.""" self.ai_game = ai_game self.screen = ai_game.screen self.prep_level() self.prep_ships() Экземпляр игры присваивается атрибуту, так как он понадобится нам для создания кораблей. Метод prep_ships() будет вызываться после prep_level() . Он выглядит так: scoreboard.py def prep_ships(self): """Сообщает количество оставшихся кораблей.""" ❶ self.ships = Group() ❷ for ship_number in range(self.stats.ships_left): ship = Ship(self.ai_game) ❸ ship.rect.x = 10 + ship_number * ship.rect.width ❹ ship.rect.y = 10 ❺ self.ships.add(ship) Метод prep_ships() создает пустую группу self.ships для хранения экземпляров кораблей . В ходе заполнения этой группы цикл выполняется по одному разу для каждого корабля, оставшегося у игрока . В цикле создается новый корабль, а координата x этого корабля задается так, чтобы корабли размещались рядом друг с другом, разделенные интервалами величиной 10 пикселов . Координата y за- дается так, чтобы корабли были смещены на 10 пикселов от верхнего края экрана и были выровнены по изображению текущего счета . Наконец, каждый корабль добавляется в группу ships . Следующим шагом становится вывод кораблей на экран: scoreboard.py def show_score(self): """Выводит очки, уровень и количество кораблей на экран.""" self.screen.blit(self.score_image, self.score_rect) self.screen.blit(self.high_score_image, self.high_score_rect) self.screen.blit(self.level_image, self.level_rect) self.ships.draw(self.screen) При выводе кораблей на экран мы вызываем метод draw() для группы, а Pygame рисует каждый отдельный корабль. 316 Глава 14 • Ведение счета Чтобы игрок видел, сколько попыток у него в начале игры, мы вызываем prep_ ships() при запуске новой игры. Это происходит в функции _check_play_button() в AlienInvasion : alien_invasion.py def _check_play_button(self, mouse_pos): if button_clicked and not self.stats.game_active: self.sb.prep_score() self.sb.prep_level() self.sb.prep_ships() Метод prep_ships() также вызывается при столкновении пришельца с кораблем, чтобы изображение обновлялось при потере корабля: alien_invasion.py def _ship_hit(self): """Обрабатывает столкновение корабля с пришельцем.""" if self.stats.ships_left > 0: # Уменьшение ships_left и обновление панели счета self.stats.ships_left -= 1 self.sb.prep_ships() Метод prep_ships() вызывается после уменьшения значения ships_left , так что при каждой потере корабля выводится правильное количество изображений. На рис. 14.6 показана полная игровая информация на экране, с количеством остав- шихся кораблей в левой верхней части экрана. УПРАЖНЕНИЯ 14.5. Исторический рекорд: в текущей версии рекорд сбрасывается каждый раз, когда игрок закрывает и перезапускает Alien Invasion. Чтобы этого не происходило, запишите рекорд в файл перед вызовом sys.exit() и загрузите его при инициализации значения в GameStats. 14.6. Рефакторинг: найдите функции и методы, которые решают более одной задачи, и проведите рефакторинг, улучшающий структуру и эффективность кода. Например, пере- местите часть кода функции _check_bullet_alien_collisions() , которая запускает новый уровень при уничтожении флота, в функцию start_new_level() . Также переместите четыре метода, вызываемых в методе __init__() класса Scoreboard, в метод prep_images() для со- кращения длины __init__() . Метод prep_images() также может оказать помощь _check_ play_button() или start_game() , если вы уже провели рефакторинг _check_play_button() ПРИМЕЧАНИЕ Прежде чем браться за рефакторинг проекта, обратитесь к приложе- нию Г . В нем рассказано, как восстановить рабочее состояние проекта, если в ходе рефакторинга были допущены ошибки . Итоги 317 Рис. 14.6. Полная игровая информация в Alien Invasion 14.7. Расширение Alien Invasion: подумайте над возможными расширениями Alien Invasion. Например, пришельцы тоже могут стрелять по кораблю, или же вы можете до- бавить укрытия, за которыми может скрываться корабль (укрытия могут разрушаться сна- рядами с обеих сторон). Или добавьте звуковые эффекты (например, взрывы или звуки выстрелов) средствами модуля pygame.mixer 14.8. Боковая стрельба, финальная версия: продолжайте разрабатывать приложение с бо- ковой стрельбой, используя все, чему вы научились в этом проекте. Добавьте кнопку Play , обеспечьте ускорение игры в нужных местах и разработайте систему начисления очков. Не забывайте проводить рефакторинг в процессе работы и ищите возможности настройки игры за рамками того, что было показано в этой главе. Итоги В этой главе вы узнали, как создать кнопку для запуска новой игры, как обнару- живать события мыши и скрывать указатель мыши в активных играх. Полученные знания помогут вам создать другие кнопки в играх, например кнопку для вывода инструкций по игре. Также вы научились изменять скорость по ходу игры, созда- вать прогрессивную систему подсчета очков и выводить информацию в текстовом и графическом виде. Проект 2 Визуализация данных 15 Генерирование данных Под визуализацией данных понимается исследование данных через их визуальное представление. Визуализация тесно связана с анализом данных (data mining), ис- пользующим программный код для изучения закономерностей и связей в наборе данных. Набором данных может быть как маленький список чисел, помещающийся в одной строке кода, так и массив из многих гигабайтов. Качественное представление данных не сводится к красивой картинке. Если для набора данных подобрано простое, визуально привлекательное представление, его смысл становится очевидным для зрителя. Люди замечают в наборе данных закономерности, о которых они и не подозревали. К счастью, для визуализации сложных данных не нужен суперкомпьютер. Благо- даря эффективности Python вы сможете быстро исследовать наборы данных из миллионов отдельных элементов данных (точек данных) на обычном ноутбуке. Элементы данных даже не обязаны быть числовыми. Приемы, о которых вы узнали в части I книги, позволят вам проанализировать даже нечисловые данные. Python используется для обработки данных в генетике, исследовании климата, по- литическом и экономическом анализе и множестве других областей. Специалисты по обработке данных написали на Python впечатляющий инструментарий визуа- лизации и анализа, и многие из этих разработок также доступны и для вас. Один из самых популярных инструментов такого рода — matplotlib , математическая библиотека построения диаграмм. С помощью matplotlib можно строить простые диаграммы, графики, диаграммы разброса данных и т. д. После этого будет создан более интересный набор данных, основанный на концепции случайного блужда- ния — визуализации, генерируемой на основе серии случайных решений. Также в этом проекте будет использоваться пакет Plotly, ориентированный на создание визуализаций, хорошо работающих с цифровыми устройствами. Plotly генерирует визуализации, автоматически масштабируемые по размерам экранов различных цифровых устройств. Визуализации также могут включать различные интерактивные возможности, например выделение различных аспектов данных набора данных при наведении указателя мыши на разные части визуализации. Мы используем Plotly для исследования закономерностей различных бросков кубиков. 320 Глава 15 • Генерирование данных Установка matplotlib Чтобы использовать библиотеку Matplotlib для исходных визуализаций, необхо- димо установить ее при помощи pip — модуля для загрузки и установки пакетов Python. Введите следующую команду в приглашении терминала: $ python -m pip install --user matplotlib Эта команда приказывает Python запустить модуль pip и добавить пакет matplotlib к установке Python текущего пользователя. Если вы используете для запуска про- грамм или запуска терминального сеанса другую команду вместо python (например, python3 ), ваша команда будет выглядеть так: $ python3 -m pip install --user matplotlib ПРИМЕЧАНИЕ Если команда не работает в macOS, попробуйте запустить ее без фла- га --user Чтобы получить представление о визуализациях, которые можно построить сред- ствами Matplotlib, посетите галерею по адресу https://matplotlib .org/gallery/ . Щелкая на визуализации в галерее, вы сможете просмотреть код, использованный для ее построения. Построение простого графика Начнем с построения простого линейного графика с использованием Matplotlib, а затем настроим его для более содержательной визуализации данных. В качестве данных для графика будет использоваться последовательность квадратов 1, 4, 9, 16 и 25. Передайте Matplotlib числа так, как показано ниже, а Matplotlib сделает все осталь- ное: mpl_squares.py import matplotlib.pyplot as plt squares = [1, 4, 9, 16, 25] ❶ fig, ax = plt.subplots() ax.plot(squares) plt.show() Сначала импортируйте модуль pyplot с псевдонимом plt , чтобы вам не приходи- лось многократно вводить имя pyplot . (Это сокращение часто встречается в приме- рах на сайте, поэтому мы поступим так же.) Модуль pyplot содержит ряд функций для построения диаграмм и графиков. Построение простого графика 321 Мы создаем список squares для хранения данных, которые будут наноситься на график. Затем используется еще одно общепринятое соглашение Matplotlib — вызов функции subplots() . Эта функция позволяет сгенерировать одну или несколько поддиаграмм на одной диаграмме. Переменная fig представляет весь рисунок или набор генерируемых диаграмм. Переменная ax представляет одну диаграмму на рисунке; эта переменная будет использоваться чаще всего в нашем примере. Затем вызывается функция plot() , которая пытается построить осмысленное графическое представление для заданных чисел. Вызов plt.show() открывает окно просмотра Matplotlib и выводит график (рис. 15.1). В окне просмотра можно изменять масштаб и перемещаться по построенному графику, а кнопка с диском позволяет сохранить любое изображение по вашему выбору. Рис. 15.1. Пример простейшего графика в Matplotlib Изменение типа надписей и толщины графика Хотя из графика на рис. 15.1 видно, что числовая последовательность возрастает, текст надписей слишком мелкий, а линия слишком тонкая. К счастью, Matplotlib позволяет настроить практически каждый аспект визуализации. Мы используем эти возможности настройки для того, чтобы сделать график более выразительным: mpl_squares.py import matplotlib.pyplot as plt squares = [1, 4, 9, 16, 25] 322 Глава 15 • Генерирование данных fig, ax = plt.subplots() ❶ ax.plot(squares, linewidth=3) # Назначение заголовка диаграммы и меток осей. ❷ ax.set_title("Square Numbers", fontsize=24) ❸ ax.set_xlabel("Value", fontsize=14) ax .set_ylabel("Square of Value", fontsize=14) # Назначение размера шрифта делений на осях. ❹ ax.tick_params(axis='both', labelsize=14) plt.show() Параметр linewidth управляет толщиной линии, которая строится вызовом plot() . Метод set_title() назначает заголовок диаграммы. Параметры fontsize , неоднократно встречающиеся в коде, управляют размером текста различных эле- ментов диаграммы. Методы xlabel() и ylabel() позволяют назначить метки (заголовки) каждой из осей , а функция tick_params() определяет оформление делений на осях . Аргументы, использованные в данном примере, относятся к делениям на обе- их осях ( axis='both' ) и устанавливают для меток делений размер шрифта 14 ( labelsize=14 ). Как видно из рис. 15.2, график выглядит гораздо лучше. Текст надписей стал круп- нее, а линия графика толще. Часто стоит поэкспериментировать с этими значения- ми, чтобы получить представление о том, какой вариант оформления будет лучше смотреться на полученной диаграмме. Рис. 15.2. График выглядит гораздо лучше Построение простого графика 323 Корректировка графика Теперь, когда текст на графике стал нормально читаться, мы видим, что данные помечены неправильно. Обратите внимание: для точки 4,0 в конце графика указан квадрат 25! Давайте исправим эту проблему. Если plot() передается числовая последовательность, функция считает, что первый элемент данных соответствует координате x со значением 0, но в нашем примере первая точка соответствует значению 1. Чтобы переопределить значение по умол- чанию, передайте plot() как входные значения, так и квадраты: mpl_squares.py import matplotlib.pyplot as plt input_values = [1, 2, 3, 4, 5] squares = [1, 4, 9, 16, 25] fig, ax = plt.subplots() ax.plot(input_values, squares, linewidth=3) # Назначение заголовка диаграммы и меток осей. Теперь plot() правильно строит график, потому что мы предоставили оба набора значений и функции не нужно предполагать, как был сгенерирован выходной набор чисел. На рис. 15.3 изображен правильный график. При вызове plot() можно передавать многочисленные аргументы, а также ис- пользовать различные функции для настройки графиков. Знакомство с этими Рис. 15.3. График с правильными данными 324 Глава 15 • Генерирование данных функциями продолжится позднее, когда мы начнем работать с более интересными наборами данных в этой главе. Встроенные стили В Matplotlib существует целый ряд заранее определенных стилей оформления с хорошей подборкой настроек для цвета фона, линий сетки, толщины линий, шрифтов, размера шрифтов и т. д.; готовые настройки позволят вам создавать привлекательные диаграммы без возни с настройкой. Чтобы узнать, какие стили доступны в вашей системе, выполните следующие команды в терминальном сеансе: >>> import matplotlib.pyplot as plt >>> plt.style.available ['seaborn-dark', 'seaborn-darkgrid', 'seaborn-ticks', 'fivethirtyeight', Чтобы использовать эти стили, добавьте одну строку кода перед построением диа- граммы: mpl_squares.py import matplotlib.pyplot as plt input_values = [1, 2, 3, 4, 5] squares = [1, 4, 9, 16, 25] plt.style.use('seaborn') fig, ax = plt.subplots() Этот код строит график, изображенный на рис. 15.4. Существует множество разно- образных стилей; поэкспериментируйте и найдите те, которые вам больше нравятся. Рис. 15.4. Встроенный стиль seaborn Построение простого графика 325 Нанесение и оформление отдельных точек функцией scatter() Иногда бывает полезно нанести на график отдельные точки, основанные на некото- рых характеристиках, и определить их оформление. Например, на графике малые и большие значения могут отображаться разными цветами. Возможны и другие варианты: например, сначала нанести множество точек с одним типом оформления, а затем выделить отдельные точки набора, перерисовав их с другим оформлением. Для нанесения на диаграмму отдельной точки используется функция scatter() Передайте scatter() координаты (x, y) нужной точки, и функция нанесет эти значения на диаграмму: |