ПРИМЕЧАНИЕ
В некоторых классических играх выводимая информация снабжается текстовыми метками: «Уро- вень», «Рекорд» и т . д . Мы эти метки опустили, потому что смысл каждого числа понятен каждому, кто сыграл в Alien Invasion . Если вы включите эти метки, добавьте их в строки непосредственно перед вызовами font .render() в Scoreboard .
Подсчет очков 303
Рис. 14.5. Текущий уровень выводится под текущим счетом
Вывод количества кораблей
Остается вывести количество кораблей, оставшихся у игрока, но на этот раз инфор- мация будет выводиться в графическом виде. Как во многих классических аркад- ных играх, в левом верхнем углу экрана программа рисует несколько изображений корабля. Каждый корабль обозначает одну оставшуюся попытку.
Для начала нужно сделать так, чтобы класс
Ship наследовал от
Sprite
, — это не- обходимо для создания группы кораблей:
ship.py
import pygame from pygame.sprite import Sprite
class Ship(Sprite):
def __init__(self, ai_settings, screen):
"""Инициализирует корабль и задает его начальную позицию."""
super(Ship, self).__init__()
Здесь мы импортируем
Sprite
, объявляем о наследовании
Ship от
Sprite
и вы- зываем super()
в начале
__init__()
.
Далее необходимо изменить
Scoreboard и создать группу кораблей для вывода на экран. Команды import и метод
__init__()
выглядят так:
scoreboard.py
import pygame.font from pygame.sprite import Group
304 Глава 14 • Ведение счета from ship import Ship class Scoreboard():
"""Класс для вывода игровой информации."""
def __init__(self, ai_settings, screen, stats):
self.prep_level()
self.prep_ships()
Так как мы собираемся создать группу кораблей, программа импортирует классы
Group и
Ship
. Метод 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_settings, self.screen)
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.pydef show_score(self):
self.screen.blit(self.level_image, self.level_rect)
# Вывод кораблей.
self.ships.draw(self.screen)
При выводе кораблей на экран мы вызываем метод draw()
для группы, а Pygame рисует каждый отдельный корабль.
Чтобы игрок видел, сколько попыток у него в начале игры, мы вызываем prep_
ships()
при запуске новой игры. Это происходит в функции check_play_button()
из файла game_functions .py
:
game_functions.pydef check_play_button(ai_settings, screen, stats, sb, play_button, ship,
Подсчет очков 305
aliens, bullets, mouse_x, mouse_y):
"""Запускает новую игру при нажатии кнопки Play."""
button_clicked = play_button.rect.collidepoint(mouse_x, mouse_y)
if button_clicked and not stats.game_active:
# Сброс изображений счетов и уровня.
sb.prep_score()
sb.prep_high_score()
sb.prep_level()
sb.prep_ships()
Метод prep_ships()
также вызывается при столкновении пришельца с кораблем, чтобы изображение обновлялось при потере корабля:
game_functions.py
def update_aliens(ai_settings, screen, stats, sb, ship, aliens, bullets):
# Проверка коллизий "пришелец-корабль".
if pygame.sprite.spritecollideany(ship, aliens):
ship_hit(ai_settings, screen, stats, sb, ship, aliens, bullets)
# Проверяет, добрались ли пришельцы до нижнего края экрана.
check_aliens_bottom(ai_settings, screen, stats, sb, ship, aliens, bullets)
def ship_hit(ai_settings, screen, stats, sb, ship, aliens, bullets):
"""Обрабатывает столкновение корабля с пришельцем."""
if stats.ships_left > 0:
# Уменьшение ships_left.
stats.ships_left -= 1
# Обновление игровой информации.
sb.prep_ships()
# Очистка списков пришельцев и пуль.
Сначала параметр sb добавляется в определение update_aliens()
. Затем про- грамма передает sb функциям ship_hit()
и check_aliens_bottom()
, чтобы эти функции имели доступ к объекту
Scoreboard
.
Затем определение ship_hit()
изменяется с включением sb
. Метод prep_ships()
вызывается после уменьшения значения ships_left
, так что при каждой потере корабля выводится правильное количество изображений.
Вызов ship_hit()
также включен в check_aliens_bottom()
, так что эту функцию тоже нужно обновить:
game_functions.py
def check_aliens_bottom(ai_settings, screen, stats, sb, ship, aliens,
bullets):
"""Проверяет, добрались ли пришельцы до нижнего края экрана."""
306 Глава 14 • Ведение счета screen_rect = screen.get_rect()
for alien in aliens.sprites():
if alien.rect.bottom >= screen_rect.bottom:
# Происходит то же, что при столкновении с кораблем.
ship_hit(ai_settings, screen, stats, sb, ship, aliens, bullets)
break
Так как check_aliens_bottom()
теперь получает параметр sb
, мы добавляем аргу- мент sb в вызов ship_hit()
Остается добавить sb в вызов update_aliens()
в файле alien_invasion .py
:
alien_invasion.py
# Запуск основного цикла игры.
while True:
if stats.game_active:
ship.update()
gf.update_bullets(ai_settings, screen, stats, sb, ship, aliens,
bullets)
gf.update_aliens(ai_settings, screen, stats, sb, ship, aliens,
bullets)
На рис. 14.6 показана полная игровая информация на экране, с количеством остав- шихся кораблей в левой верхней части экрана.
Рис. 14.6. Полная игровая информация в Alien Invasion
Итоги
307УПРАЖНЕНИЯ14-4 . Исторический рекорд: в текущей версии рекорд сбрасывается каждый раз, когда игрок закрывает и перезапускает Alien Invasion . Чтобы этого не происходило, запишите рекорд в файл перед вызовом sys .exit() и загрузите его при инициализации значения в GameStats .
14-5 . Рефакторинг: найдите функции и методы, которые решают более одной задачи, и про- ведите рефакторинг, улучшающий структуру и эффективность кода . Например, переме- стите часть кода функции check_bullet_alien_collisions(), которая запускает новый уровень при уничтожении флота, в функцию start_new_level() . Также переместите четыре метода, вызываемых в методе __init__() класса Scoreboard, в метод prep_images() для сокращения длины __init__() . Метод prep_images() также может оказать помощь check_play_button() или start_game(), если вы уже провели рефакторинг check_play_button() .
ПРИМЕЧАНИЕПрежде чем
браться за рефакторинг проекта, обратитесь к приложению Г . В нем расска- зано, как восстановить рабочее состояние проекта, если в ходе рефакторинга были до- пущены ошибки .
14-6 . Расширение Alien Invasion: подумайте над возможными расширениями Alien Invasion .
Например, пришельцы тоже могут стрелять по кораблю, или же вы можете добавить укры- тия, за которыми может скрываться корабль (укрытия могут разрушаться пулями с обеих сторон) . Или добавьте звуковые эффекты (например, взрывы или звуки выстрелов) сред- ствами модуля pygame .mixer .
Итоги
В этой главе вы узнали, как создать кнопку для запуска новой игры, как обнару- живать события мыши и скрывать указатель мыши в активных играх. Полученные знания помогут вам создать другие кнопки в играх, например кнопку для вывода инструкций по игре. Также вы научились изменять скорость по ходу игры, созда- вать прогрессивную систему подсчета очков и выводить информацию в текстовом и графическом виде.
Проект 2
Визуализация данных
15Генерирование данных
Под
визуализацией данных понимается исследование данных через их визуальное представление. Визуализация тесно связана с
анализом данных (data mining), ис- пользующим программный код для изучения закономерностей и связей в наборе данных. Набором данных может быть как маленький список чисел, помещающийся в одной строке кода, так и массивом из многих гигабайт.
Качественное представление данных не сводится к красивой картинке. Если для набора данных подобрано простое, визуально привлекательное представление, его смысл становится очевидным для зрителя.
Люди замечают в наборе данных закономерности, о которых они и не подозревали.
К счастью, для визуализации сложных данных не нужен суперкомпьютер. Бла- годаря эффективности Python вы сможете быстро исследовать наборы данных из миллионов отдельных
элементов данных (точек данных) на обычном ноутбуке.
Элементы данных даже не обязаны быть числовыми. Приемы, о которых вы узнали в первой части книги, позволят вам проанализировать даже нечисловые данные.
Python используется для обработки данных в генетике, исследовании климата, по- литическом и экономическом анализе и множестве других областей. Специалисты по обработке данных написали на Python впечатляющий инструментарий визуа- лизации и анализа, и многие из этих разработок также доступны и для вас. Один из самых популярных инструментов такого рода — matplotlib
, математическая библиотека построения диаграмм. С помощью matplotlib можно строить простые диаграммы, графики, диаграммы разброса данных и т. д. После этого будет создан более интересный набор данных, основанный на концепции
случайного блужда-ния — визуализации, генерируемой на базе серии случайных решений.
Также в этом проекте будет использоваться пакет Pygal, ориентированный на создание визуализаций, хорошо работающих с цифровыми устройствами.
С помощью Pygal можно выделять и изменять размеры элементов в ходе взаимо- действия пользователя с визуализацией; кроме того, размер визуализации легко изменяется под крошечные «умные часы» или гигантский монитор. Мы используем
Pygal для исследования закономерностей различных бросков кубиков.
Установка matplotlib
Сначала необходимо установить библиотеку matplotlib
, которая будет исполь- зоваться в исходном наборе визуализаций. Если вы еще не использовали про-
310 Глава 15 • Генерирование данных грамму pip
, обращайтесь к разделу «Установка пакетов Python с использованием pip» (с. 227).
В Linux
Если вы используете версию Python, входящую в поставку системы, вы сможете использовать менеджер пакетов своей системы для установки matplotlib всего одной командой:
$
sudo apt-get install python3-matplotlibЕсли вы используете Python 2.7, команда выглядит так:
$
sudo apt-get install python-matplotlibЕсли у вас установлена более новая версия Python, вам придется установить еще несколько библиотек, от которых зависит matplotlib
:
$
sudo apt-get install python3.5-dev python3.5-tk tk-dev$
sudo apt-get install libfreetype6-dev g++Затем программа pip используется для установки matplotlib
:
$
pip install --user matplotlibВ OS X
Компания Apple включает matplotlib в свою стандартную установку Python. Что- бы проверить,
установлена ли библиотека в вашей системе, откройте терминальный сеанс и попробуйте импортировать matplotlib
. Если библиотека matplotlib еще не установлена в системе и вы использовали Homebrew для установки Python, установите ее следующей командой:
$
pip install --user matplotlibПРИМЕЧАНИЕВозможно, при установке пакетов вам придется использовать команду pip3 вместо pip . Если же эта команда не работает, попробуйте исключить флаг --user .
В Windows
В системе Windows сначала необходимо установить Visual Studio. Откройте стра- ницу
https://dev.windows.com/, щелкните на ссылке
Скачать средства
(
Downloads
) и найдите Visual Studio Community — бесплатный набор средств разработчика для
Windows. Загрузите и запустите программу установки.
Затем вам понадобится программа установки для matplotlib
. Обратитесь по адресу
https://pypi.python.org/pypi/matplotlib/ и найдите файл с расширени- ем
.whl
, соответствующий используемой версии Python. Например, если вы используете 32-разрядную версию Python 3.5, загрузите файл matplotlib-1 .4 .3- cp35-none-win32 .whl
Построение простого графика 311
ПРИМЕЧАНИЕ
Если вы не нашли файл, соответствующий используемой версии Python, просмотрите возможные варианты по адресу http://www .lfd .uci .edu/gohlke/pythonlibs/#matplotlib . На этом сайте новые па- кеты появляются немного ранее, чем на официальном сайте matplotlib .
Скопируйте файл
.whl в каталог проекта, откройте окно командной строки и перейдите в каталог проекта. Используйте pip для установки matplotlib
:
> cd python_work
python_work> python -m pip install --user matplotlib-1.4.3-cp35-none-win32.whl
Тестирование matplotlib
После того как необходимые пакеты будут установлены, протестируйте свою уста- новку. Для этого откройте сеанс командной строки командой python или python3
и импортируйте matplotlib
:
$ python3
>>> import matplotlib
>>>
Если на экране не появились сообщения об ошибках, значит, библиотека matplotlib установлена в вашей системе и вы можете переходить к следующему разделу.
ПРИМЕЧАНИЕ
Если у вас возникнут проблемы с установкой, поищите информацию в приложении В . Если ничего не помогает, обратитесь за помощью . Скорее всего, опытный программист Python быстро найдет причины возникших проблем с минимумом информации от вас .
Галерея matplotlib
Чтобы получить представления о визуализациях, которые можно строить в matplotlib
, посетите галерею на сайте http://matplotlib.org/. Щелкая на визуали- зации в галерее, вы сможете просмотреть код, использованный для ее построения.
Построение простого графика
Начнем с построения простого линейного графика с использованием matplotlib
, а затем настроим его для более содержательной визуализации данных. В качестве данных для графика будет использоваться последовательность квадратов 1, 4, 9,
16 и 25.
Передайте matplotlib числа так, как показано ниже, а matplotlib сделает все остальное:
mpl_squares.py
import matplotlib.pyplot as plt squares = [1, 4, 9, 16, 25]
plt.plot(squares)
plt.show()
312 Глава 15 • Генерирование данных
Сначала импортируйте модуль pyplot с псевдонимом plt
, чтобы вам не приходи- лось многократно вводить имя pyplot
. (Это сокращение часто встречается в приме- рах на сайте, поэтому мы поступим так же.) Модуль pyplot содержит ряд функций для построения диаграмм и графиков.
Мы создаем список для хранения квадратов и передаем его функции 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]
plt.plot(squares, linewidth=5)
# Назначение заголовка диаграммы и меток осей.
plt.title("Square Numbers", fontsize=24)
plt.xlabel("Value", fontsize=14)
Построение простого графика 313
plt.ylabel("Square of Value", fontsize=14)
# Назначение размера шрифта делений на осях.
plt.tick_params(axis='both', labelsize=14)
plt.show()
Параметр linewidth
управляет толщиной линии, которая строится вызо- вом plot()
. Функция title()
назначает заголовок диаграммы. Параметры fontsize
, неоднократно встречающиеся в коде, управляют размером текста диаграммы.
Функции xlabel()
и ylabel()
позволяют назначить метки (заголовки) каж- дой из осей , а функция tick_params()
определяет оформление делений на осях . Аргументы, использованные в данном примере, относятся к делениям на обоих осях (
axes='both'
) и устанавливают для меток делений размер шрифта
14 (
labelsize=14
).
Как видно из рис. 15.2, график выглядит гораздо лучше. Текст надписей стал круп- нее, а линия графика — толще.
Рис. 15.2. График выглядит гораздо лучше
Корректировка графика
Теперь, когда текст на графике стал нормально читаться, мы видим, что данные помечены неправильно. Обратите внимание: для точки 4,0 в конце графика указан квадрат 25! Давайте исправим эту ошибку.
Если plot()
передает числовую последовательность, функция считает, что первый элемент данных соответствует координате x со значением 0, но в нашем примере первая точка соответствует значению 1. Чтобы переопределить значение по умол- чанию, передайте plot()
как входные значения, так и квадраты:
314 Глава 15 • Генерирование данных
mpl_squares.pyimport matplotlib.pyplot as plt input_values = [1, 2, 3, 4, 5]
squares = [1, 4, 9, 16, 25]
plt.plot(input_values, squares, linewidth=5)
# Назначение заголовка диаграммы и меток осей.
Теперь plot()
правильно строит график, потому что мы предоставили оба набора значений, и функции не нужно предполагать, как был сгенерирован выходной на- бор чисел. На рис. 15.3 изображен правильный график.