Pygame учебник





PyGame: учебник по программированию игр на Python

Фон и настройка

pygame – это оболочка Python для SDL library, что означает Simple DirectMedia Layer. SDL обеспечивает кроссплатформенный доступ к базовым компонентам мультимедийного оборудования вашей системы, таким как звук, видео, мышь, клавиатура и джойстик. pygame начал свою жизнь как замена остановившемуся PySDL project. Кросс-платформенный характер как SDL, так иpygame означает, что вы можете писать игры и мультимедийные программы Python для любой платформы, которая их поддерживает!

Чтобы установитьpygame на вашу платформу, используйте соответствующую команду pip:

$ pip install pygame

Вы можете проверить установку, загрузив один из примеров, поставляемых с библиотекой:

$ python3 -m pygame.examples.aliens

Если появляется окно игры, значитpygame установлен правильно! Если у вас возникнут проблемы, то Getting Started guide описывает некоторые известные проблемы и предостережения для всех платформ.

Основная программа PyGame

Когда вы запустите эту программу, вы увидите окно, которое выглядит так:

Давайте разберем этот код, раздел за разделом:

  • Lines 4 and 5 импортирует и инициализирует библиотекуpygame. Без этих строк нетpygame.
  • Line 8 устанавливает окно отображения вашей программы. Вы предоставляете либо список, либо кортеж, который определяет ширину и высоту создаваемого окна. Эта программа использует список для создания квадратного окна с 500 пикселями на каждой стороне.
  • Lines 11 and 12 настраиваетgame loop для контроля завершения программы. Об этом вы узнаете позже в этом уроке.
  • Lines 15 to 17 просматривает и обрабатываетevents в игровом цикле. Вы придете к событиям чуть позже. В этом случае обрабатывается только событиеpygame.QUIT, которое происходит, когда пользователь нажимает кнопку закрытия окна.
  • Line 20 заполняет окно сплошным цветом. screen.fill() принимает либо список, либо кортеж, определяющий значения RGB для цвета. Поскольку(255, 255, 255) предоставлен, окно заполняется белым цветом.
  • Line 23 рисует круг в окне, используя следующие параметры:
    • screen: окно, в котором нужно рисовать
    • (0, 0, 255): кортеж, содержащий значения цвета RGB
    • (250, 250): кортеж, определяющий координаты центра круга
    • 75: радиус нарисованного круга в пикселях
  • Line 26 обновляет содержимое дисплея на экране. Без этого звонка в окне ничего не появляется!
  • Line 29 выходит изpygame. Это происходит только после завершения цикла.

Это pygame версия “Hello, World”. Теперь давайте углубимся в концепции, лежащие в основе этого кода.

PyGame Concepts

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

Инициализация и Модули

Библиотекаpygame – это composed of a number of Python constructs, которая включает несколько разных modules. Эти модули обеспечивают абстрактный доступ к конкретному оборудованию в вашей системе, а также унифицированные методы для работы с этим оборудованием. Например,display обеспечивает единообразный доступ к вашему видео дисплею, аjoystick позволяет абстрактно управлять вашим джойстиком.

После импорта библиотекиpygame в приведенном выше примере первое, что вы сделали, – этоinitialize PyGame с использованиемpygame.init(). Эта функция calls the separate init() functions всех включенных модулей pygame. Поскольку эти модули являются абстракциями для конкретного оборудования, этот шаг инициализации необходим для того, чтобы вы могли работать с одним и тем же кодом в Linux, Windows и Mac.

Дисплеи и поверхности

В дополнение к модулямpygame также включает несколько Python classes, которые инкапсулируют концепции, не зависящие от оборудования. Один из них –Surface, который, по сути, определяет прямоугольную область, на которой вы можете рисовать. Объекты Surface используются во многих контекстах в pygame. Позже вы увидите, как загрузить изображение в Surface и отобразить его на экране.

В pygame все просматривается на одном созданном пользователем display, который может быть окном или полноэкранным. Отображение создается с использованием.set_mode(), который возвращает Surface, представляющий видимую часть окна. Именно этотSurface вы передаете в функции рисования, такие как pygame.draw.circle(), и содержимое этогоSurface выводится на дисплей, когда вы вызываете pygame.display.flip().

Изображения и Rects

Ваша базовая программаpygame нарисовала фигуру прямо на экранеSurface, но вы также можете работать с изображениями на диске. image module позволяет создавать изображения load и save в различных популярных форматах. Изображения загружаются в объектыSurface, которыми затем можно управлять и отображать различными способами.

Как упоминалось выше, объектыSurface представлены прямоугольниками, как и многие другие объекты вpygame, такие как изображения и окна. Прямоугольники настолько широко используются, что существует special Rect class только для их обработки. Вы будете использовать объекты и изображения Rect в своей игре, чтобы рисовать игроков и врагов и управлять столкновениями между ними.

Ладно, достаточно теории. Давайте разработаем и напишем игру!

Базовый дизайн игры

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

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

Когда он описывал программные проекты,former colleague of mine говорил: «Вы не знаете, что делаете, пока не узнаете, чего не делаете». Имея это в виду, вот некоторые вещи, которые не будут рассмотрены в этом руководстве:

  • Нет нескольких жизней
  • Нет счета
  • Нет возможности атаки игрока
  • Нет продвигающихся уровней
  • Нет боссов

Вы можете сами попробовать добавить эти и другие функции в свою программу.

Давайте начнем!

Импорт и инициализация PyGame

После импорта pygame вам также потребуется его инициализировать. Это позволяет pygame подключать свои абстракции к вашему конкретному оборудованию:

Библиотека pygame определяет многие вещи помимо модулей и классов. Он также определяет некоторые local constants для таких вещей, как нажатия клавиш, движения мыши и атрибуты отображения. Вы ссылаетесь на эти константы, используя синтаксис pygame.<CONSTANT>. Импортируя определенные константы изpygame.locals, вы можете вместо этого использовать синтаксис <CONSTANT>. Это сэкономит вам несколько нажатий клавиш и улучшит общую читабельность.

Настройка дисплея

Теперь вам нужно что-то нарисовать! Создайте screen как общий холст:

Вы создаете экран для использования, вызывая pygame.display.set_mode() и передавая кортеж или список с желаемой шириной и высотой. В этом случае размер окна составляет 800×600, что определяется константами SCREEN_WIDTH иSCREEN_HEIGHT в строках 20 и 21. Это возвращает Surface, который представляет внутренние размеры окна. Это часть окна, которой вы можете управлять, в то время как ОС контролирует границы окна и строку заголовка.

Если вы запустите эту программу сейчас, вы увидите, что окно ненадолго всплывет, а затем сразу исчезнет при выходе из программы. Не моргайте, иначе вы можете пропустить это! В следующем разделе вы сосредоточитесь на основном игровом цикле, чтобы гарантировать, что ваша программа завершает работу только при правильном вводе.

Настройка игрового цикла

В каждой игре от Pong до Fortnite используется game loop для управления игровым процессом. Игровой цикл выполняет четыре очень важных вещи:

  1. Обрабатывает пользовательский ввод
  2. Обновляет состояние всех игровых объектов
  3. Обновляет дисплей и аудио выход
  4. Поддерживает скорость игры

Каждый цикл игрового цикла называется frame, и чем быстрее вы сможете выполнять действия в каждом цикле, тем быстрее будет работать ваша игра. Кадры продолжают появляться до тех пор, пока не будет выполнено какое-либо условие для выхода из игры. В вашем дизайне есть два условия, которые могут завершить игровой цикл:

  1. Игрок сталкивается с препятствием. (Вы узнаете об обнаружении столкновений позже.)
  2. Игрок закрывает окно.

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

Обработка событий

Нажатие клавиш, движения мыши и даже движения джойстика являются одними из способов, которыми пользователь может обеспечить ввод. Результатом всего пользовательского ввода является создание event. События могут происходить в любое время и часто (но не всегда) происходят вне программы. Все события вpygame помещаются в очередь событий, к которой затем можно получить доступ и управлять ими. Работа с событиями обозначается handling них, а код для этого называется event handler.

Каждое событие в pygame имеет связанное с ним событие type. Для вашей игры типы событий, на которых вы сосредоточитесь, – это нажатия клавиш и закрытие окна. События нажатия клавиш имеют тип события KEYDOWN, а событие закрытия окна имеет тип QUIT. Различные типы событий могут также иметь другие данные, связанные с ними. Например, тип события KEYDOWN также имеет переменную с именемkey, чтобы указать, какая клавиша была нажата.

Вы получаете доступ к списку всех активных событий в очереди, вызывая pygame.event.get(). Затем вы просматриваете этот список, проверяете каждый тип события и отвечаете соответственно:

Давайте внимательнее посмотрим на этот игровой цикл:

  • Line 28 устанавливает управляющую переменную для игрового цикла. Чтобы выйти из цикла и игры, вы устанавливаете running = False. Игровой цикл начинается в строке 29.
  • Line 31 запускает обработчик событий, просматривая все события в очереди событий. Если событий нет, список пуст, и обработчик ничего не сделает.
  • Lines 35 to 38 проверяет, является ли текущий event.type событием KEYDOWN. Если это так, то программа проверяет, какая клавиша была нажата, глядя на атрибут event.key. Если ключ является ключом [.kbd .key-escape]#Esc #, обозначеннымK_ESCAPE, то он выходит из игрового цикла, устанавливая running = False.
  • Lines 41 and 42 выполняет аналогичную проверку для типа события с именем QUIT. Это событие происходит только тогда, когда пользователь нажимает кнопку закрытия окна. Пользователь также может использовать любое другое действие операционной системы, чтобы закрыть окно.

Когда вы добавите эти строки в предыдущий код и запустите его, вы увидите окно с пустым или черным экраном:

Окно не исчезнет, ​​пока вы не нажмете клавишу [.kbd .key-escape]#Esc # или иным образом не вызовете событиеQUIT, закрыв окно.

Рисование на экране

В примере программы вы рисовали на экране с помощью двух команд:

  1. screen.fill() для заливки фона
  2. pygame.draw.circle(), чтобы нарисовать круг

Теперь вы узнаете о третьем способе рисования на экране: с помощьюSurface.

Напомним, что Surface – это прямоугольный объект, на котором вы можете рисовать, как чистый лист бумаги. Объект screen – этоSurface, и вы можете создавать свои собственные объекты Surface отдельно от экрана дисплея. Давайте посмотрим, как это работает:

После заполнения экрана белым цветом в строке 45 создается новый Surface в строке 48. Этот Surface имеет ширину 50 пикселей, высоту 50 пикселей и назначен surf. На этом этапе вы относитесь к нему так же, как к screen. Итак, на линии 51 вы заполняете его черным. Вы также можете получить доступ к его базовому Rect, используя.get_rect(). Он сохраняется как rect для дальнейшего использования.

Используя.blit() и.flip()

Просто создать новый Surface недостаточно, чтобы увидеть его на экране. Для этого вам нужно blitSurface на другой Surface. Термин blit означает Block Transfer, а.blit() – это то, как вы копируете содержимое одного Surface в другой. Вы можете только.blit() от одного Surface к другому, но поскольку экран – это просто еще один Surface, это не проблема. Вот как вы рисуете surf на экране:

Вызов.blit() в строке 55 принимает два аргумента:

  1. Surface для рисования
  2. Место для его рисования на источникеSurface

Координаты (SCREEN_WIDTH/2, SCREEN_HEIGHT/2) говорят вашей программе разместить surf точно в центре экрана, но это не совсем так:

Причина, по которой изображение выглядит не по центру, заключается в том, что .blit() помещает top-left corner изsurf в указанное место. Если вы хотите, чтобы surf был центрирован, вам нужно будет выполнить некоторые вычисления, чтобы сместить его вверх и влево. Вы можете сделать это, вычтя ширину и высоту surf из ширины и высоты экрана, разделив каждую на 2, чтобы определить местонахождение центра, а затем передав эти числа в качестве аргументов в screen.blit():

Обратите внимание на вызов pygame.display.flip() после вызова blit(). Это обновляет весь экран со всем, что было нарисовано с момента последнего переворота. Без вызова .flip() ничего не отображается.

Спрайты

В вашем игровом дизайне игрок начинает слева, а справа появляются препятствия. Вы можете представить все препятствия с помощью объектов Surface, чтобы все было проще рисовать, но как узнать, где их нарисовать? Как узнать, столкнулось ли препятствие с игроком? Что происходит, когда препятствие летит за пределы экрана? Что делать, если вы хотите нарисовать фоновые изображения, которые также перемещаются? Что если вы хотите, чтобы ваши изображения были анимированными? Вы можете справиться со всеми этими и другими ситуациями с помощью sprites.

С точки зрения программирования sprite – это 2D-представление чего-либо на экране. По сути, это картина. pygame предоставляет Sprite class, который предназначен для хранения одного или нескольких графических представлений любого игрового объекта, который вы хотите отобразить на экране. Чтобы использовать его, вы создаете новый класс, расширяющий Sprite. Это позволяет использовать его встроенные методы.

игроки

Вот как вы используете объекты Sprite в текущей игре для определения игрока. Вставьте этот код после строки 18:

Сначала вы определяете Player, расширяя pygame.sprite.Sprite в строке 22. Затем.__init__() использует.super() для вызова метода.__init__() дляSprite.

Для получения дополнительной информации о том, почему это необходимо, вы можете прочитать Supercharge Your Classes With Python super().

Затем вы определяете и инициализируете .surf, чтобы удерживать изображение для отображения, которое в настоящее время представляет собой белое поле. Вы также определяете и инициализируете .rect, который вы будете использовать для рисования игрока позже. Чтобы использовать этот новый класс, вам нужно создать новый объект и изменить код для рисования. Разверните блок кода ниже, чтобы увидеть все это вместе:

Запустите этот код. Вы увидите белый прямоугольник примерно в середине экрана:

Как вы думаете, что произойдет, если вы измените строку 59 на screen.blit(player.surf, player.rect)?

Попробуйте и посмотрите:

Когда вы передаете Rect в.blit(), он использует координаты верхнего левого угла для рисования поверхности. Вы будете использовать это позже, чтобы заставить вашего игрока двигаться!

Пользовательский ввод

До сих пор вы научились настраивать pygame и рисовать объекты на экране. Теперь начинается самое интересное! Вы сделаете плеер управляемым с помощью клавиатуры.

Ранее вы видели, что pygame.event.get() возвращает список событий в очереди событий, которую вы просматриваете на предмет типов событий KEYDOWN. Ну, это не единственный способ читать нажатия клавиш . pygame также предоставляет pygame.event.get_pressed(), который возвращает dictionary, содержащий все текущие события KEYDOWN в очереди.

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

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

K_UP,K_DOWN,K_LEFT иK_RIGHT соответствуют клавишам со стрелками на клавиатуре. Если словарная запись для этого ключа –True, значит, этот ключ нажат, и вы перемещаете игрока .rect в правильном направлении. Здесь вы используете .move_ip(), что означает move in place, для перемещения текущего Rect.

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

Теперь вы можете перемещать прямоугольник игрока по экрану с помощью клавиш со стрелками:

Вы можете заметить две небольшие проблемы:

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

Чтобы игрок оставался на экране, вам нужно добавить некоторую логику, чтобы определять, собирается ли rect уйти с экрана. Для этого вы проверяете, не переместились ли координаты rect за пределы экрана. Если это так, то вы даете команду программе переместить ее обратно к краю:

Здесь вместо использования .move() вы просто меняете соответствующие координаты .top,.bottom,.left или.right напрямую. Проверьте это, и вы увидите, что прямоугольник игрока больше не может сдвинуться с экрана.

Теперь давайте добавим несколько врагов!

враги

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

Затем создайте новый класс спрайтов с именем Enemy, следуя тому же шаблону, который вы использовали для Player:

Между Enemy и Player есть четыре заметных различия:

  1. On lines 62 to 67, вы обновляете rect, чтобы он был случайным местом вдоль правого края экрана. Центр прямоугольника находится за пределами экрана. Он расположен в некотором месте между 20 и 100 пикселями от правого края и где-то между верхним и нижним краями.
  2. On line 68, вы определяете .speed как случайное число от 5 до 20. Это указывает, как быстро этот враг движется к игроку.
  3. On lines 73 to 76, вы определяете .update(). Это не требует никаких аргументов, поскольку враги двигаются автоматически. Вместо этого .update() перемещает врага в левую часть экрана на .speed, определенное при его создании.
  4. On line 74, вы проверяете, ушел ли противник за пределы экрана. Чтобы убедиться, что Enemy полностью за пределами экрана и не исчезнет просто так, пока он все еще виден, вы убедитесь, что правая сторона .rect прошла за левую часть экрана. Когда противник находится за кадром, вы вызываете .kill(), чтобы предотвратить его дальнейшую обработку.

Итак, что делает .kill()? Чтобы понять это, вы должны знать о Sprite Groups.

Спрайт группы

Еще один очень полезный класс, который предоставляет pygame, – этоSprite Group. Это объект, содержащий группу объектов Sprite. Так зачем его использовать? Разве вы не можете вместо этого просто отслеживать свои объекты Sprite в списке? Что ж, вы можете, но преимущество использования Group заключается в методах, которые он предоставляет. Эти методы помогают определить, столкнулся ли какой-либо Enemy с Player, что значительно упрощает обновление.

Давайте посмотрим, как создавать группы спрайтов. Вы создадите два разных объекта Group:

  1. Первые Group будут удерживать каждые Sprite в игре.
  2. Второй Group будет содержать только объекты Enemy.

Вот как это выглядит в коде:

Когда вы вызываете .kill(),Sprite удаляется из каждого Group, которому он принадлежит. Это также удаляет ссылки на Sprite, что позволяет сборщику мусора Python при необходимости освобождать память.

Теперь, когда у вас есть группа all_sprites, вы можете изменить способ рисования объектов. Вместо вызова .blit() только для Player, вы можете перебирать все в all_sprites:

Теперь все, что помещено в all_sprites, будет отрисовываться с каждым кадром, будь то враг или игрок.

Есть только одна проблема … У тебя нет врагов! Вы можете создать кучу врагов в начале игры, но игра быстро станет скучной, когда все они покинут экран через несколько секунд. Вместо этого давайте рассмотрим, как поддерживать постоянный приток врагов в ходе игры.

Пользовательские события

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

  1. Создайте новый Enemy.
  2. Добавьте его в all_sprites иenemies.

У вас уже есть код, который обрабатывает случайные события. Цикл обработки событий предназначен для поиска случайных событий, происходящих в каждом кадре, и надлежащего обращения с ними. К счастью, pygame не ограничивает вас использованием только тех типов событий, которые он определил. Вы можете определить свои собственные события для обработки по своему усмотрению.

Давайте посмотрим, как создать пользовательское событие, которое генерируется каждые несколько секунд. Вы можете создать собственное событие, назвав его:

pygame определяет события внутри как целые числа, поэтому вам нужно определить новое событие с уникальным целым числом. Последнее событие pygame резервов называется USEREVENT, поэтому определение ADDENEMY = pygame.USEREVENT + 1 в строке 83 гарантирует его уникальность.

Затем вам нужно регулярно вставлять это новое событие в очередь на протяжении всей игры. Вот тут и пригодится модуль time. Строка 84 запускает новое событие ADDENEMY каждые 250 миллисекунд или четыре раза в секунду. Вы вызываете .set_timer() вне игрового цикла, поскольку вам нужен только один таймер, но он будет срабатывать на протяжении всей игры.

Добавьте код для обработки вашего нового события:

Каждый раз, когда обработчик событий видит новое событие ADDENEMY в строке 115, он создает Enemy и добавляет его кenemies иall_sprites. Поскольку Enemy находится вall_sprites, он будет отрисовываться каждый кадр. Вам также необходимо вызвать enemies.update() в строке 126, который обновляет все в enemies, чтобы убедиться, что они перемещаются правильно:

Однако это не единственная причина, по которой существует группа только дляenemies.

Обнаружение столкновений

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

Здесь пригодится фреймворк вроде pygame! Написание кода обнаружения столкновений утомительно, но pygame имеет МНОГО collision detection methods, доступное для использования.

В этом руководстве вы будете использовать метод под названием .spritecollideany(), который читается как «спрайт сталкивается с любым». Этот метод принимает в качестве параметров Sprite иGroup. Он просматривает каждый объект вGroup и проверяет, пересекается ли его .rect с.rectSprite. Если да, то возвращается True. В противном случае возвращается False. Это идеально подходит для этой игры, поскольку вам нужно проверить, не сталкивается ли отдельный player с одним из Group изenemies.

Вот как это выглядит в коде:

Строка 135 проверяет, столкнулся ли player с каким-либо из объектов в enemies. Если это так, то вызывается player.kill(), чтобы удалить его из каждой группы, к которой он принадлежит. Поскольку визуализируются только объекты в all_sprites,player больше не будет отображаться. После того, как игрок был убит, вам также необходимо выйти из игры, поэтому вы устанавливаете running = False для выхода из игрового цикла в строке 138.

Теперь у вас есть основные элементы игры:

Теперь давайте немного нарядим его, сделаем его более играбельным и добавим некоторые расширенные возможности, которые помогут ему выделиться.

Спрайт Изображения

Хорошо, у вас есть игра, но давайте будем честными … Это некрасиво. Игрок и враги – это просто белые блоки на черном фоне. Когда Pong был новым, это было по последнему слову техники, но он больше не работает. Давайте заменим все эти скучные белые прямоугольники более прохладными изображениями, которые сделают игру настоящей игрой.

Ранее вы узнали, что изображения на диске могут быть загружены в Surface с некоторой помощью модуля image. Для этого урока мы сделали небольшую струю для игрока и несколько ракет для врагов. Вы можете использовать этот рисунок, нарисовать свой собственный или загрузить несколько free game art assets для использования. Вы можете нажать на ссылку ниже, чтобы загрузить искусство, используемое в этом руководстве:

Sample Code:Click here to download the source code for the PyGame sample project, используемый в этом руководстве.

Изменение конструкторов объектов

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

Давайте немного распакуем строку 31. pygame.image.load() загружает образ с диска. Вы передаете ему путь к файлу. Он возвращает Surface, а вызов .convert() оптимизирует Surface, ускоряя будущие вызовы .blit().

В строке 32 используется .set_colorkey(), чтобы указать, что цвет pygame будет отображаться как прозрачный. В этом случае вы выбираете белый, потому что это цвет фона изображения струи. Константа RLEACCEL – это необязательный параметр, который помогает быстрее отображать pygame на дисплеях без ускорения. Это добавляется к оператору импорта pygame.locals в строке 11.

Больше ничего не нужно менять. Изображение по-прежнему Surface, только теперь на нем нарисовано изображение. Вы все еще используете его таким же образом.

Вот как выглядят похожие изменения в Enemy:

Запуск программы сейчас должен показать, что это та же игра, что и у вас раньше, только теперь вы добавили красивую графику skins с изображениями. Но зачем останавливаться на том, чтобы заставить игрока и вражеских спрайтов выглядеть красиво? Давайте добавим несколько облаков, проходящих мимо, чтобы создать впечатление струи, летящей по небу.

Добавление фоновых изображений

Для фоновых облаков вы используете те же принципы, что и для Player иEnemy:

  1. Создайте класс Cloud.
  2. Добавьте к нему изображение облака.
  3. Создайте метод .update(), который перемещает cloud в левую часть экрана.
  4. Создайте настраиваемое событие и обработчик для создания новых объектов cloud через заданный интервал времени.
  5. Добавьте вновь созданные объекты cloud в новый Group с именем clouds.
  6. Обновите и нарисуйте clouds в своем игровом цикле.

Вот как выглядит Cloud:

Это все должно выглядеть очень знакомым. Это почти то же самое, что и Enemy.

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

Это говорит о том, что нужно подождать 1000 миллисекунд или одну секунду перед созданием следующего cloud.

Затем создайте новыйGroup для хранения каждого вновь созданного cloud:

Затем добавьте обработчик для нового события ADDCLOUD в обработчике событий:

Наконец, убедитесь, что clouds обновляются каждый кадр:

Строка 172 обновляет исходный screen.fill(), чтобы заполнить экран приятным небесно-голубым цветом. Вы можете изменить этот цвет на что-то другое. Может быть, вы хотите инопланетный мир с пурпурным небом, ядовитую пустошь в неоново-зеленом или поверхность Марса в красном!

Обратите внимание, что каждый новый Cloud иEnemy добавляется к all_sprites, а также кclouds иenemies. Это сделано потому, что каждая группа используется для отдельной цели:

  • Rendering выполняется с использованием all_sprites.
  • Position updates выполняется с использованием clouds иenemies.
  • Collision detection выполняется с использованием enemies.

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

Скорость игры

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

Причина этого в том, что игровой цикл обрабатывает кадры настолько быстро, насколько позволяет процессор и среда. Поскольку все спрайты перемещаются один раз за кадр, они могут перемещаться сотни раз в секунду. Количество кадров, обрабатываемых каждую секунду, называется frame rate, и правильное определение этого значения – разница между играбельной игрой и той, которую можно забыть.

Как правило, вы хотите максимально высокую частоту кадров, но для этой игры вам нужно немного замедлить ее, чтобы игра стала играбельной. К счастью, модуль time содержит Clock, который предназначен именно для этой цели.

Использование Clock для установки воспроизводимой частоты кадров требует всего двух строк кода. Первый создает новый Clock перед началом игрового цикла:

Второй вызывает .tick(), чтобы сообщить pygame, что программа достигла конца кадра:

Аргумент, переданный .tick(), устанавливает желаемую частоту кадров. Для этого .tick() вычисляет количество миллисекунд, которое должен занять каждый кадр, на основе желаемой частоты кадров. Затем он сравнивает это число с количеством миллисекунд, прошедших с момента последнего вызова .tick(). Если прошло недостаточно времени, .tick() задерживает обработку, чтобы гарантировать, что она никогда не превысит указанную частоту кадров.

Переход на меньшую частоту кадров приведет к увеличению времени в каждом кадре для вычислений, в то время как большая частота кадров обеспечивает более плавный (и, возможно, более быстрый) игровой процесс:

Поиграйте с этим номером, чтобы увидеть, что вам больше нравится!

Звуковые эффекты

Пока что вы сосредоточены на игровом процессе и визуальных аспектах вашей игры. Теперь давайте рассмотрим, как придать вашей игре слуховой вкус.  pygame предоставляет mixer для обработки всех действий, связанных со звуком. Вы будете использовать классы и методы этого модуля для обеспечения фоновой музыки и звуковых эффектов для различных действий.

Название mixer относится к тому факту, что модуль смешивает различные звуки в единое целое. Используя подмодуль music, вы можете передавать отдельные звуковые файлы в различных форматах, таких как MP3,Ogg и Mod. Вы также можете использоватьSound для удержания одного звукового эффекта для воспроизведения в форматах Ogg или uncompressed WAV. Все воспроизведение происходит в фоновом режиме, поэтому, когда вы проигрываетеSound, метод немедленно возвращается по мере воспроизведения звука.

Note: pygame documentation указывает, что поддержка MP3 ограничена, а неподдерживаемые форматы могут вызвать сбои системы. Звуки, упомянутые в этой статье, были протестированы, и мы рекомендуем тщательно протестировать любые звуки перед выпуском вашей игры.

Как и в большинстве случаев pygame, использование mixer начинается с этапа инициализации. К счастью, этим уже занимается pygame.init(). Вам нужно только вызвать pygame.mixer.init(), если вы хотите изменить значения по умолчанию:

pygame.mixer.init() принимает a number of arguments, но в большинстве случаев значения по умолчанию работают нормально. Обратите внимание: если вы хотите изменить значения по умолчанию, вам нужно вызвать pygame.mixer.init() перед вызовом pygame.init(). В противном случае значения по умолчанию будут действовать независимо от ваших изменений.

После инициализации системы вы можете настроить свои звуки и фоновую музыку:

Lines 138 and 139 загружает фоновый звуковой клип и начинает его воспроизведение. Вы можете указать звуковому клипу зацикливаться и никогда не заканчиваться, задав именованный параметр loops=-1.

Lines 143 to 145 загружает три звука, которые вы будете использовать для различных звуковых эффектов. Первые два – это повышающиеся и понижающиеся звуки, которые воспроизводятся, когда игрок движется вверх или вниз. Последний звук используется всякий раз, когда происходит столкновение. Вы также можете добавить другие звуки, такие как звук, когда создаетсяEnemy, или последний звук, когда игра заканчивается.

Итак, как вы используете звуковые эффекты? Вы хотите воспроизвести каждый звук, когда происходит определенное событие. Например, когда корабль движется вверх, вы хотите сыграть move_up_sound. Следовательно, вы добавляете вызов .play() всякий раз, когда обрабатываете это событие. В дизайне это означает добавление следующих вызовов к .update() для Player:

Для столкновения между игроком и врагом вы воспроизводите звук, когда обнаруживаются столкновения:

Здесь вы сначала останавливаете любые другие звуковые эффекты, потому что при столкновении игрок больше не движется. Затем вы проигрываете звук столкновения и продолжаете исполнение оттуда.

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

Технически эти последние несколько строк не требуются, так как программа заканчивается сразу после этого. Однако если позже вы решите добавить в игру вводный экран или экран выхода, то после завершения игры может появиться больше кода.

Это оно! Проверьте это снова, и вы должны увидеть что-то вроде этого:

Примечание к источникам

Возможно, вы заметили комментарий в строках 136-137 при загрузке фоновой музыки, в котором указан источник музыки и ссылка на лицензию Creative Commons. Это было сделано, потому что создатель этого звука требовал этого. В лицензионных требованиях указывалось, что для использования звука необходимо предоставить как надлежащую атрибуцию, так и ссылку на лицензию.

Вот некоторые источники музыки, звука и искусства, которые вы можете найти для полезного контента:

  • OpenGameArt.org: звуки, звуковые эффекты, спрайты и другие изображения
  • Kenney.nl: звуки, звуковые эффекты, спрайты и другие изображения
  • Gamer Art 2D: спрайты и другие изображения
  • CC Mixter: звуки и звуковые эффекты
  • Freesound: звуки и звуковые эффекты

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

Заключение

Из этого руководства вы узнали, чем программирование игр с помощью pygame отличается от стандартного процедурного программирования. Вы также узнали, как:

  • Реализация циклов событий
  • Рисовать предметы на экране
  • Воспроизведение звуковых эффектов и музыки
  • Обрабатывать пользовательский ввод

Для этого вы использовали подмножество модулейpygame, включая display,mixer иmusic,time,image,event и модули key. Вы также использовали несколько классовpygame, включая Rect,Surface,Sound и Sprite. Но это лишь малая часть того, на что способен pygame! Посмотрите official pygame documentation для получения полного списка доступных модулей и классов.

Вы можете найти весь код, графику и звуковые файлы для этой статьи, нажав на ссылку ниже:

Clone Repo: Click here to clone the repo you’ll use, чтобы узнать, как использовать PyGame в этом руководстве.

Ссылка

Основные модули пакета Pygame

МодульНазначение
pygame.cdromДоступ к CD-приводам и управление ими
pygame.cursorsЗагружает изображения курсора
pygame.displayДоступ к дисплею
pygame.drawРисует фигуры, линии и точки
pygame.eventУправление внешними событиями
pygame.fontИспользует системные шрифты
pygame.imageЗагружает и сохраняет изображение
pygame.joystickИспользует джойстики и аналогичные устройства
pygame.keyСчитывает нажатия клавиш с клавиатуры
pygame.mixerЗагружает и воспроизводит мелодии
pygame.mouseУправляет мышью
pygame.movieВоспроизведение видеофайлов
pygame.musicРаботает с музыкой и потоковым аудио
pygame.overlayДоступ к расширенным видеоизображениям
pygameСодержит функции Pygame высокого уровня
pygame.rectУправляет прямоугольными областями
pygame.sndarrayМанипулирует звуковыми данными
pygame.spriteУправление движущимися изображениями
pygame.surfaceУправляет изображениями и экраном
pygame.surfarrayМанипулирует данными пикселей изображения
pygame.timeмодуль pygame для управления временем и частотой кадров
pygame.transformИзменение размера и перемещение изображений

Рисование базовых элементов
модуль pygame.draw

pygame.draw.rectнарисовать прямоугольную форму
pygame.draw.polygonфигуру с любым количеством сторон
pygame.draw.circleкруг вокруг точки
pygame.draw.ellipseнарисовать круглую форму внутри прямоугольника
pygame.draw.arcнарисовать секцию эллипса
pygame.draw.lineнарисовать сегмент прямой линии
pygame.draw.linesдля рисования нескольких смежных отрезков
pygame.draw.aalineрисовать тонкую линию
pygame.draw.aalinesнарисовать связанную последовательность сглаженных линий

МЕТОДЫ РАБОТЫ С RECT

pygame.Rect.copyВозвращает новый прямоугольник, имеющий ту же позицию и размер, что и оригинал.
pygame.Rect.moveВозвращает новый прямоугольник, перемещаемый данным смещением. Аргументы x и y могут быть любым целочисленным значением, положительным или отрицательным.
pygame.Rect.move_ipТо же, что и метод Rect.move (), но работает на месте.
pygame.Rect.inflateувеличивать или уменьшать размер прямоугольника, на месте
pygame.Rect.inflate_ipувеличивать или уменьшать размер прямоугольника, на месте
pygame.Rect.clampперемещает прямоугольник внутри другого
pygame.Rect.clamp_ipперемещает прямоугольник внутри другого, на месте
pygame.Rect.clipобрезает прямоугольник внутри другого
pygame.Rect.unionсоединяет два прямоугольника в один
pygame.Rect.union_ipсоединяет два прямоугольника в один, на месте
pygame.Rect.unionallобъединение многих прямоугольников
pygame.Rect.unionall_ipобъединение многих прямоугольников, на месте
pygame.Rect.fitизменить размер и переместить прямоугольник учмиывая соотношение сторон
pygame.Rect.normalizeкорректировать отрицательные размеры
pygame.Rect.containsпроверить, находится ли один прямоугольник внутри другого
pygame.Rect.collidepointпроверить, находится ли точка внутри прямоугольника
pygame.Rect.colliderectтест, пересекаются ли два прямоугольника
pygame.Rect.collidelistпроверить, пересекается ли хоть один прямоугольник в списке
pygame.Rect.collidelistallпересекаются ли все прямоугольники в списке
pygame.Rect.collidedictпроверить, если один прямоугольник в словаре пересекается
pygame.Rect.collidedictallпересекаются ли все прямоугольники в словаре

Объект event

Модуль pygame.event для обработки очереди событий

pygame.event.pumpЕсли вы не используете другие функции событий в своей игре, вы должны вызвать pygame.event.pump (), чтобы позволить pygame обрабатывать внутренние действия
pygame.event.getполучает события из очереди
pygame.event.pollполучить одно событие из очереди
pygame.event.waitждёт одиночного события из очереди
pygame.event.peekпроверить, ждут ли очереди события определённого типа
pygame.event.clearудалить все события из очереди
pygame.event.event_nameвозвращает имя для типа события. Строка находится в стиле WordCap
pygame.event.set_blockedпроверяет, какие события не разрешены в очереди
pygame.event.set_allowedпроверяет, какие события разрешены в очереди
pygame.event.get_blockedпроверить, заблокирован ли тип события из очереди
pygame.event.set_grabпроверяет совместное использование устройств ввода с другими приложениями
pygame.event.get_grabпроверить, работает ли программа на устройствах ввода данных
pygame.event.postпоместить новое событие в очередь
pygame.event.Eventсоздать новый объект события
pygame.event.EventTypeОбъект Python, представляющий событие SDL. Экземпляры пользовательских событий создаются с вызовом функции Event. Тип EventType не может быть напрямую вызван. Экземпляры EventType поддерживают назначение и удаление атрибутов.

Pygame отслеживает все сообщения о событиях через очередь событий. Процедуры в этом модуле помогают управлять этой очередью событий. Входная очередь сильно зависит от модуля отображения (display) pygame. Если дисплей не был инициализирован и видеорежим не установлен, очередь событий не будет работать.

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

Мышь

Модуль pygame.mouse для работы с мышью

pygame.mouse.get_pressedполучить состояние кнопок мыши
pygame.mouse.get_posполучить позицию курсора мыши
pygame.mouse.get_relполучить количество движений мыши
pygame.mouse.set_posустановить позицию курсора мыши
pygame.mouse.set_visibleскрыть или показать курсор мыши
pygame.mouse.get_focusedпроверяет, принимает ли дисплей ввод мыши
pygame.mouse.set_cursorустановить изображение для курсора мыши
pygame.mouse.get_cursorполучить изображение для курсора мыши

Функции мыши можно использовать для получения текущего состояния устройства мышь. Эти функции также могут изменять курсор мыши.

Ссылка

Что такое Pygame?

Нужная ссылка

Ссылка