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

27 апреля, 2020

Подписывайся на наш канал в Telegram, чтобы ежедневно совершенствоваться в Python. Там выходят задачи, полезные советы и сливы платных курсов - перейти

Оглавление

  • Фон и настройка
  • Основная программа PyGame
  • PyGame Concepts
    • Инициализация и Модули
    • Дисплеи и поверхности
    • Изображения и Rects
  • Базовый дизайн игры
    • Импорт и инициализация PyGame
    • Настройка дисплея
    • Настройка игрового цикла
    • Обработка событий
    • Рисование на экране
    • Использование .blit () и .flip ()
  • Спрайты
    • Игроки
    • Пользовательский ввод
    • Враги
  • Спрайт группы
  • Пользовательские события
  • Обнаружение столкновения
  • Спрайт Изображения
    • Изменение конструкторов объектов
    • Добавление фоновых изображений
  • Скорость игры
  • Звуковые эффекты
  • Примечание к источникам
  • Вывод

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

К концу этой статьи вы сможете:

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

Этот учебник предполагает, что у вас есть базовые знания по написанию программ на Python , включая пользовательские функции, импорт , циклы и условные выражения . Вы также должны знать, как открывать файлы на вашей платформе. Базовое понимание объектно-ориентированный Python является полезным. pygame работает с большинством версий Python, но Python 3.6 рекомендуется и используется в этой статье.

Вы можете получить весь код из этой статьи по этой ссылке:

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

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

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

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

$ pip install pygame

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

$ python3 -m pygame.examples.aliens

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

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

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

# Simple pygame program
 
# Import and initialize the pygame library
import pygame
pygame.init()
 
# Set up the drawing window
screen = pygame.display.set_mode([500, 500])
 
Run until the user asks to quit
running = True
while running:
 
    # Did the user click the window close button?
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
 
    # Fill the background with white
    screen.fill((255, 255, 255))
 
    # Draw a solid blue circle in the center
    pygame.draw.circle(screen, (0, 0, 255), (250, 250), 75)
 
    # Flip the display
    pygame.display.flip()
 
# Done! Time to quit.
pygame.quit()

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

Простая программа Pygame

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

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

Это версия pygame «Привет, мир». Теперь давайте углубимся в концепции этого кода.

PyGame Concepts

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

# Import the pygame module
import pygame
 
# Import pygame.locals for easier access to key coordinates
# Updated to conform to flake8 and black standards
from pygame.locals import (
    K_UP,
    K_DOWN,
    K_LEFT,
    K_RIGHT,
    K_ESCAPE,
    KEYDOWN,
    QUIT,
)
 
# Initialize pygame
pygame.init()

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

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

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

# Import the pygame module
import pygame

# Import pygame.locals for easier access to key coordinates
# Updated to conform to flake8 and black standards
from pygame.locals import (
    K_UP,
    K_DOWN,
    K_LEFT,
    K_RIGHT,
    K_ESCAPE,
    KEYDOWN,
    QUIT,
)

# Initialize pygame
pygame.init()

# Define constants for the screen width and height
SCREEN_WIDTH = 800
SCREEN_HEIGHT = 600

# Create the screen object
# The size is determined by the constant SCREEN_WIDTH and SCREEN_HEIGHT
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))

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

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

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

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

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

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

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

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

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

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

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

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

# Variable to keep the main loop running
running = True

# Main loop
while running:
    # Look at every event in the queue
    for event in pygame.event.get():
        # Did the user hit a key?
        if event.type == KEYDOWN:
            # Was it the Escape key? If so, stop the loop.
            if event.key == K_ESCAPE:
                running = False

        # Did the user click the window close button? If so, stop the loop.
        elif event.type == QUIT:
            running = False

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

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

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

Пустое, но постоянное окно пигмея

Окно не исчезнет, ​​пока вы не нажмете клавишу Esc или не запустите QUITсобытие, закрыв окно.

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

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

  1. screen.fill() заполнить фон
  2. pygame.draw.circle() нарисовать круг

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

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

# Fill the screen with white
screen.fill((255, 255, 255))

# Create a surface and pass in a tuple containing its length and width
surf = pygame.Surface((50, 50))

# Give the surface a color to separate it from the background
surf.fill((0, 0, 0))
rect = surf.get_rect()

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

Использование .blit() и .flip()

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

# This line says "Draw surf onto the screen at the center"
screen.blit(surf, (SCREEN_WIDTH/2, SCREEN_HEIGHT/2))
pygame.display.flip()

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

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

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

Разбивая поверхность на экран

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

# Put the center of surf at the center of the display
surf_center = (
    (SCREEN_WIDTH-surf.get_width())/2,
    (SCREEN_HEIGHT-surf.get_height())/2
)

# Draw surf at the new coordinates
screen.blit(surf, surf_center)
pygame.display.flip()

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

Спрайты

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

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

Игроки

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

# Define a Player object by extending pygame.sprite.Sprite
# The surface drawn on the screen is now an attribute of 'player'
class Player(pygame.sprite.Sprite):
    def __init__(self):
        super(Player, self).__init__()
        self.surf = pygame.Surface((75, 25))
        self.surf.fill((255, 255, 255))
        self.rect = self.surf.get_rect()

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

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

# Import the pygame module
import pygame

# Import pygame.locals for easier access to key coordinates
# Updated to conform to flake8 and black standards
from pygame.locals import (
    K_UP,
    K_DOWN,
    K_LEFT,
    K_RIGHT,
    K_ESCAPE,
    KEYDOWN,
    QUIT,
)

# Define constants for the screen width and height
SCREEN_WIDTH = 800
SCREEN_HEIGHT = 600

# Define a player object by extending pygame.sprite.Sprite
# The surface drawn on the screen is now an attribute of 'player'
class Player(pygame.sprite.Sprite):
    def __init__(self):
        super(Player, self).__init__()
        self.surf = pygame.Surface((75, 25))
        self.surf.fill((255, 255, 255))
        self.rect = self.surf.get_rect()

# Initialize pygame
pygame.init()

# Create the screen object
# The size is determined by the constant SCREEN_WIDTH and SCREEN_HEIGHT
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))

# Instantiate player. Right now, this is just a rectangle.
player = Player()

# Variable to keep the main loop running
running = True

# Main loop
while running:
    # for loop through the event queue
    for event in pygame.event.get():
        # Check for KEYDOWN event
        if event.type == KEYDOWN:
            # If the Esc key is pressed, then exit the main loop
            if event.key == K_ESCAPE:
                running = False
        # Check for QUIT event. If QUIT, then set running to false.
        elif event.type == QUIT:
            running = False

    # Fill the screen with black
    screen.fill((0, 0, 0))

    # Draw the player on the screen
    screen.blit(player.surf, (SCREEN_WIDTH/2, SCREEN_HEIGHT/2))

    # Update the display
    pygame.display.flip()

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

Нарисован основной спрайт игрока

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

# Fill the screen with black
screen.fill((0, 0, 0))

# Draw the player on the screen
screen.blit(player.surf, player.rect)

# Update the display
pygame.display.flip()

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

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

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

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

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

# Get the set of keys pressed and check for user input
pressed_keys = pygame.key.get_pressed()

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

# Move the sprite based on user keypresses
def update(self, pressed_keys):
    if pressed_keys[K_UP]:
        self.rect.move_ip(0, -5)
    if pressed_keys[K_DOWN]:
        self.rect.move_ip(0, 5)
    if pressed_keys[K_LEFT]:
        self.rect.move_ip(-5, 0)
    if pressed_keys[K_RIGHT]:
        self.rect.move_ip(5, 0)

K_UPK_DOWNK_LEFT и K_RIGHTсоответствуют клавиши со стрелками на клавиатуре. Если словарная запись для этой клавиши True, то эта клавиша нажата, и вы перемещаете игрока .rect в правильном направлении. Здесь вы используете .move_ip(), что означает движение на месте,чтобы переместить текущий Rect.

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

# Main loop
while running:
    # for loop through the event queue
    for event in pygame.event.get():
        # Check for KEYDOWN event
        if event.type == KEYDOWN:
            # If the Esc key is pressed, then exit the main loop
            if event.key == K_ESCAPE:
                running = False
        # Check for QUIT event. If QUIT, then set running to false.
        elif event.type == QUIT:
            running = False

    # Get all the keys currently pressed
    pressed_keys = pygame.key.get_pressed()

    # Update the player sprite based on user keypresses
    player.update(pressed_keys)

    # Fill the screen with black
    screen.fill((0, 0, 0))

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

Клавиши, перемещающие спрайт в Pygame

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

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

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

# Move the sprite based on user keypresses
def update(self, pressed_keys):
    if pressed_keys[K_UP]:
        self.rect.move_ip(0, -5)
    if pressed_keys[K_DOWN]:
        self.rect.move_ip(0, 5)
    if pressed_keys[K_LEFT]:
        self.rect.move_ip(-5, 0)
    if pressed_keys[K_RIGHT]:
        self.rect.move_ip(5, 0)

    # Keep player on the screen
    if self.rect.left < 0:
        self.rect.left = 0
    if self.rect.right > SCREEN_WIDTH:
        self.rect.right = SCREEN_WIDTH
    if self.rect.top <= 0:
        self.rect.top = 0
    if self.rect.bottom >= SCREEN_HEIGHT:
        self.rect.bottom = SCREEN_HEIGHT

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

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

Враги

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

# Import random for random numbers
import random

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

# Define the enemy object by extending pygame.sprite.Sprite
# The surface you draw on the screen is now an attribute of 'enemy'
class Enemy(pygame.sprite.Sprite):
    def __init__(self):
        super(Enemy, self).__init__()
        self.surf = pygame.Surface((20, 10))
        self.surf.fill((255, 255, 255))
        self.rect = self.surf.get_rect(
            center=(
                random.randint(SCREEN_WIDTH + 20, SCREEN_WIDTH + 100),
                random.randint(0, SCREEN_HEIGHT),
            )
        )
        self.speed = random.randint(5, 20)

    # Move the sprite based on speed
    # Remove the sprite when it passes the left edge of the screen
    def update(self):
        self.rect.move_ip(-self.speed, 0)
        if self.rect.right < 0:
            self.kill()

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

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

Итак, что же .kill() делает? Чтобы понять это, вы должны знать о группах спрайтов.

Группы спрайтов

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

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

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

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

# Create the 'player'
player = Player()

# Create groups to hold enemy sprites and all sprites
# - enemies is used for collision detection and position updates
# - all_sprites is used for rendering
enemies = pygame.sprite.Group()
all_sprites = pygame.sprite.Group()
all_sprites.add(player)

# Variable to keep the main loop running
running = True

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

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

# Fill the screen with black
screen.fill((0, 0, 0))

# Draw all sprites
for entity in all_sprites:
    screen.blit(entity.surf, entity.rect)

# Flip everything to the display
pygame.display.flip()

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

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

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

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

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

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

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

# Create the screen object
# The size is determined by the constant SCREEN_WIDTH and SCREEN_HEIGHT
screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))

# Create a custom event for adding a new enemy
ADDENEMY = pygame.USEREVENT + 1
pygame.time.set_timer(ADDENEMY, 250)

# Instantiate player. Right now, this is just a rectangle.
player = Player()

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

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

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

# Main loop
while running:
    # Look at every event in the queue
    for event in pygame.event.get():
        # Did the user hit a key?
        if event.type == KEYDOWN:
            # Was it the Escape key? If so, stop the loop.
            if event.key == K_ESCAPE:
                running = False

        # Did the user click the window close button? If so, stop the loop.
        elif event.type == QUIT:
            running = False

        # Add a new enemy?
        elif event.type == ADDENEMY:
            # Create the new enemy and add it to sprite groups
            new_enemy = Enemy()
            enemies.add(new_enemy)
            all_sprites.add(new_enemy)

    # Get the set of keys pressed and check for user input
    pressed_keys = pygame.key.get_pressed()
    player.update(pressed_keys)

    # Update enemy position
    enemies.update()

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

Враги пролетают в пигаме

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

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

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

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

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

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

# Draw all sprites
for entity in all_sprites:
    screen.blit(entity.surf, entity.rect)

# Check if any enemies have collided with the player
if pygame.sprite.spritecollideany(player, enemies):
    # If so, then remove the player and stop the loop
    player.kill()
    running = False

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

На данный момент у вас есть основные элементы игры на месте:

Окно пигмея

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

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

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

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

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

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

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

# Import pygame.locals for easier access to key coordinates
# Updated to conform to flake8 and black standards
# from pygame.locals import *
from pygame.locals import (
    RLEACCEL,
    K_UP,
    K_DOWN,
    K_LEFT,
    K_RIGHT,
    K_ESCAPE,
    KEYDOWN,
    QUIT,
)

# Define constants for the screen width and height
SCREEN_WIDTH = 800
SCREEN_HEIGHT = 600


# Define the Player object by extending pygame.sprite.Sprite
# Instead of a surface, use an image for a better-looking sprite
class Player(pygame.sprite.Sprite):
    def __init__(self):
        super(Player, self).__init__()
        self.surf = pygame.image.load("jet.png").convert()
        self.surf.set_colorkey((255, 255, 255), RLEACCEL)
        self.rect = self.surf.get_rect()

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

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

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

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

# Define the enemy object by extending pygame.sprite.Sprite
# Instead of a surface, use an image for a better-looking sprite
class Enemy(pygame.sprite.Sprite):
    def __init__(self):
        super(Enemy, self).__init__()
        self.surf = pygame.image.load("missile.png").convert()
        self.surf.set_colorkey((255, 255, 255), RLEACCEL)
        # The starting position is randomly generated, as is the speed
        self.rect = self.surf.get_rect(
            center=(
                random.randint(SCREEN_WIDTH + 20, SCREEN_WIDTH + 100),
                random.randint(0, SCREEN_HEIGHT),
            )
        )
        self.speed = random.randint(5, 20)

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

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

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

  • Создайте класс Cloud.
  • Добавьте к нему изображение облака.
  • Создайте метод .update(), который перемещает облако в левую часть экрана.
  • Создайте настраиваемое событие и обработчик для создания новых облачных объектов через заданный интервал времени.
  • Добавьте вновь созданные облачные объекты в новую группу, называемую облаками.
  • Обновите и нарисуйте облака в своем игровом цикле.

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

# Define the cloud object by extending pygame.sprite.Sprite
# Use an image for a better-looking sprite
class Cloud(pygame.sprite.Sprite):
    def __init__(self):
        super(Cloud, self).__init__()
        self.surf = pygame.image.load("cloud.png").convert()
        self.surf.set_colorkey((0, 0, 0), RLEACCEL)
        # The starting position is randomly generated
        self.rect = self.surf.get_rect(
            center=(
                random.randint(SCREEN_WIDTH + 20, SCREEN_WIDTH + 100),
                random.randint(0, SCREEN_HEIGHT),
            )
        )

    # Move the cloud based on a constant speed
    # Remove the cloud when it passes the left edge of the screen
    def update(self):
        self.rect.move_ip(-5, 0)
        if self.rect.right < 0:
            self.kill()

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

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

# Create custom events for adding a new enemy and a cloud
ADDENEMY = pygame.USEREVENT + 1
pygame.time.set_timer(ADDENEMY, 250)
ADDCLOUD = pygame.USEREVENT + 2
pygame.time.set_timer(ADDCLOUD, 1000)

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

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

# Create groups to hold enemy sprites, cloud sprites, and all sprites
# - enemies is used for collision detection and position updates
# - clouds is used for position updates
# - all_sprites is used for rendering
enemies = pygame.sprite.Group()
clouds = pygame.sprite.Group()
all_sprites = pygame.sprite.Group()
all_sprites.add(player)

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

# Main loop
while running:
    # Look at every event in the queue
    for event in pygame.event.get():
        # Did the user hit a key?
        if event.type == KEYDOWN:
            # Was it the Escape key? If so, then stop the loop.
            if event.key == K_ESCAPE:
                running = False

        # Did the user click the window close button? If so, stop the loop.
        elif event.type == QUIT:
            running = False

        # Add a new enemy?
        elif event.type == ADDENEMY:
            # Create the new enemy and add it to sprite groups
            new_enemy = Enemy()
            enemies.add(new_enemy)
            all_sprites.add(new_enemy)

        # Add a new cloud?
        elif event.type == ADDCLOUD:
            # Create the new cloud and add it to sprite groups
            new_cloud = Cloud()
            clouds.add(new_cloud)
            all_sprites.add(new_cloud)

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

# Update the position of enemies and clouds
enemies.update()
clouds.update()

# Fill the screen with sky blue
screen.fill((135, 206, 250))

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

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

  • Рендеринг делается с использованием all_sprites.
  • Обновление позиции осуществляется с помощью clouds и enemies.
  • Обнаружение столкновения делается с помощью enemies.

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

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

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

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

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

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

# Setup the clock for a decent framerate
clock = pygame.time.Clock()

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

# Flip everything to the display
pygame.display.flip()

# Ensure program maintains a rate of 30 frames per second
clock.tick(30)

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

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

Установка частоты кадров в Pygame

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

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

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

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

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

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

# Setup for sounds. Defaults are good.
pygame.mixer.init()

# Initialize pygame
pygame.init()

# Set up the clock for a decent framerate
clock = pygame.time.Clock()

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

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

# Load and play background music
# Sound source: http://ccmixter.org/files/Apoxode/59262
# License: https://creativecommons.org/licenses/by/3.0/
pygame.mixer.music.load("Apoxode_-_Electric_1.mp3")
pygame.mixer.music.play(loops=-1)

# Load all sound files
# Sound sources: Jon Fincher
move_up_sound = pygame.mixer.Sound("Rising_putter.ogg")
move_down_sound = pygame.mixer.Sound("Falling_putter.ogg")
collision_sound = pygame.mixer.Sound("Collision.ogg")

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

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

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

# Define the Player object by extending pygame.sprite.Sprite
# Instead of a surface, use an image for a better-looking sprite
class Player(pygame.sprite.Sprite):
    def __init__(self):
        super(Player, self).__init__()
        self.surf = pygame.image.load("jet.png").convert()
        self.surf.set_colorkey((255, 255, 255), RLEACCEL)
        self.rect = self.surf.get_rect()

    # Move the sprite based on keypresses
    def update(self, pressed_keys):
        if pressed_keys[K_UP]:
            self.rect.move_ip(0, -5)
            move_up_sound.play()
        if pressed_keys[K_DOWN]:
            self.rect.move_ip(0, 5)
            move_down_sound.play()

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

# Check if any enemies have collided with the player
if pygame.sprite.spritecollideany(player, enemies):
    # If so, then remove the player
    player.kill()

    # Stop any moving sounds and play the collision sound
    move_up_sound.stop()
    move_down_sound.stop()
    collision_sound.play()

    # Stop the loop
    running = False

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

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

# All done! Stop and quit the mixer.
pygame.mixer.music.stop()
pygame.mixer.quit()

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

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

Окно пигмея

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

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

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

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

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

Вывод

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

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

Для этого вы использовали подмножество модулей pygame, включая дисплей, микшер и музыку, время, изображение, событие и ключевые модули. Вы также использовали несколько классов pygame, включая Rect, Surface, Sound и Sprite. Но это лишь малая часть того, что может сделать Pygame! Ознакомьтесь с официальной документацией pygame для получения полного списка доступных модулей и классов.

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

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


Совершенствуй знания каждый день у нас в Телеграм-каналах

Вопросы, реклама — VK | Telegram