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

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

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

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

  • Нарисуйте предметы на вашем экране

  • Воспроизведение звуковых эффектов и музыки

  • Обрабатывать пользовательский ввод

  • Реализация циклов событий

  • Опишите, чем игровое программирование отличается от стандартного процедурного программирования на Python

В этом учебнике предполагается, что у вас естьbasic understanding of writing Python programs, включая определяемые пользователем функции,imports,loops иconditionals. Вы также должны быть знакомы сhow to open files на своей платформе. Также полезно использоватьbasic understanding of object-oriented Python. pygame работает с большинством версий Python, но Python 3.6 рекомендуется и используется в этой статье.

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

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

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

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

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

 1 # Simple pygame program
 2
 3 # Import and initialize the pygame library
 4 import pygame
 5 pygame.init()
 6
 7 # Set up the drawing window
 8 screen = pygame.display.set_mode([500, 500])
 9
10 # Run until the user asks to quit
11 running = True
12 while running:
13
14     # Did the user click the window close button?
15     for event in pygame.event.get():
16         if event.type == pygame.QUIT:
17             running = False
18
19     # Fill the background with white
20     screen.fill((255, 255, 255))
21
22     # Draw a solid blue circle in the center
23     pygame.draw.circle(screen, (0, 0, 255), (250, 250), 75)
24
25     # Flip the display
26     pygame.display.flip()
27
28 # Done! Time to quit.
29 pygame.quit()

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

A simple pygame program

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

  • 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 также включает несколько Pythonclasses, которые инкапсулируют концепции, не зависящие от оборудования. Один из них -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 подключать свои абстракции к вашему конкретному оборудованию:

 1 # Import the pygame module
 2 import pygame
 3
 4 # Import pygame.locals for easier access to key coordinates
 5 # Updated to conform to flake8 and black standards
 6 from pygame.locals import (
 7     K_UP,
 8     K_DOWN,
 9     K_LEFT,
10     K_RIGHT,
11     K_ESCAPE,
12     KEYDOWN,
13     QUIT,
14 )
15
16 # Initialize pygame
17 pygame.init()

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

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

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

 1 # Import the pygame module
 2 import pygame
 3
 4 # Import pygame.locals for easier access to key coordinates
 5 # Updated to conform to flake8 and black standards
 6 from pygame.locals import (
 7     K_UP,
 8     K_DOWN,
 9     K_LEFT,
10     K_RIGHT,
11     K_ESCAPE,
12     KEYDOWN,
13     QUIT,
14 )
15
16 # Initialize pygame
17 pygame.init()
18
19 # Define constants for the screen width and height
20 SCREEN_WIDTH = 800
21 SCREEN_HEIGHT = 600
22
23 # Create the screen object
24 # The size is determined by the constant SCREEN_WIDTH and SCREEN_HEIGHT
25 screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))

Вы создаете экран для использования, вызываяpygame.display.set_mode() и передавая кортеж или список с желаемой шириной и высотой. В этом случае размер окна составляет 800x600, что определяется константами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(). Затем вы просматриваете этот список, проверяете каждый тип события и отвечаете соответственно:

27 # Variable to keep the main loop running
28 running = True
29
30 # Main loop
31 while running:
32     # Look at every event in the queue
33     for event in pygame.event.get():
34         # Did the user hit a key?
35         if event.type == KEYDOWN:
36             # Was it the Escape key? If so, stop the loop.
37             if event.key == K_ESCAPE:
38                 running = False
39
40         # Did the user click the window close button? If so, stop the loop.
41         elif event.type == QUIT:
42             running = False

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

  • 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. Это событие происходит только тогда, когда пользователь нажимает кнопку закрытия окна. Пользователь также может использовать любое другое действие операционной системы, чтобы закрыть окно.

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

An empty

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

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

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

  1. screen.fill() для заливки фона

  2. pygame.draw.circle(), чтобы нарисовать круг

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

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

44 # Fill the screen with white
45 screen.fill((255, 255, 255))
46
47 # Create a surface and pass in a tuple containing its length and width
48 surf = pygame.Surface((50, 50))
49
50 # Give the surface a color to separate it from the background
51 surf.fill((0, 0, 0))
52 rect = surf.get_rect()

После заполнения экрана белым цветом в строке 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 на экране:

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

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

  1. Surface для рисования

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

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

Blitting a surface onto the screen

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

54 # Put the center of surf at the center of the display
55 surf_center = (
56     (SCREEN_WIDTH-surf.get_width())/2,
57     (SCREEN_HEIGHT-surf.get_height())/2
58 )
59
60 # Draw surf at the new coordinates
61 screen.blit(surf, surf_center)
62 pygame.display.flip()

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

Спрайты

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

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

игроки

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

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

Сначала вы определяетеPlayer, расширяяpygame.sprite.Sprite в строке 22. Затем.__init__() использует.super() для вызова метода.__init__() дляSprite. Для получения дополнительной информации о том, почему это необходимо, вы можете прочитатьSupercharge Your Classes With Python super().

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

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

Basic player sprite being drawn

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

55 # Fill the screen with black
56 screen.fill((0, 0, 0))
57
58 # Draw the player on the screen
59 screen.blit(player.surf, player.rect)
60
61 # Update the display
62 pygame.display.flip()

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

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

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

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

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

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

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

29 # Move the sprite based on user keypresses
30 def update(self, pressed_keys):
31     if pressed_keys[K_UP]:
32         self.rect.move_ip(0, -5)
33     if pressed_keys[K_DOWN]:
34         self.rect.move_ip(0, 5)
35     if pressed_keys[K_LEFT]:
36         self.rect.move_ip(-5, 0)
37     if pressed_keys[K_RIGHT]:
38         self.rect.move_ip(5, 0)

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

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

52 # Main loop
53 while running:
54     # for loop through the event queue
55     for event in pygame.event.get():
56         # Check for KEYDOWN event
57         if event.type == KEYDOWN:
58             # If the Esc key is pressed, then exit the main loop
59             if event.key == K_ESCAPE:
60                 running = False
61         # Check for QUIT event. If QUIT, then set running to false.
62         elif event.type == QUIT:
63             running = False
64
65     # Get all the keys currently pressed
66     pressed_keys = pygame.key.get_pressed()
67
68     # Update the player sprite based on user keypresses
69     player.update(pressed_keys)
70
71     # Fill the screen with black
72     screen.fill((0, 0, 0))

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

Keypresses moving a sprite in pygame

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

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

  2. Прямоугольник игрока может сдвинуться с экрана. Давайте решим это сейчас.

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

25 # Move the sprite based on user keypresses
26 def update(self, pressed_keys):
27     if pressed_keys[K_UP]:
28         self.rect.move_ip(0, -5)
29     if pressed_keys[K_DOWN]:
30         self.rect.move_ip(0, 5)
31     if pressed_keys[K_LEFT]:
32         self.rect.move_ip(-5, 0)
33     if pressed_keys[K_RIGHT]:
34         self.rect.move_ip(5, 0)
35
36     # Keep player on the screen
37     if self.rect.left < 0:
38         self.rect.left = 0
39     if self.rect.right > SCREEN_WIDTH:
40         self.rect.right = SCREEN_WIDTH
41     if self.rect.top <= 0:
42         self.rect.top = 0
43     if self.rect.bottom >= SCREEN_HEIGHT:
44         self.rect.bottom = SCREEN_HEIGHT

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

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

враги

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

 4 # Import random for random numbers
 5 import random

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

55 # Define the enemy object by extending pygame.sprite.Sprite
56 # The surface you draw on the screen is now an attribute of 'enemy'
57 class Enemy(pygame.sprite.Sprite):
58     def __init__(self):
59         super(Enemy, self).__init__()
60         self.surf = pygame.Surface((20, 10))
61         self.surf.fill((255, 255, 255))
62         self.rect = self.surf.get_rect(
63             center=(
64                 random.randint(SCREEN_WIDTH + 20, SCREEN_WIDTH + 100),
65                 random.randint(0, SCREEN_HEIGHT),
66             )
67         )
68         self.speed = random.randint(5, 20)
69
70     # Move the sprite based on speed
71     # Remove the sprite when it passes the left edge of the screen
72     def update(self):
73         self.rect.move_ip(-self.speed, 0)
74         if self.rect.right < 0:
75             self.kill()

Между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.

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

82 # Create the 'player'
83 player = Player()
84
85 # Create groups to hold enemy sprites and all sprites
86 # - enemies is used for collision detection and position updates
87 # - all_sprites is used for rendering
88 enemies = pygame.sprite.Group()
89 all_sprites = pygame.sprite.Group()
90 all_sprites.add(player)
91
92 # Variable to keep the main loop running
93 running = True

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

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

117 # Fill the screen with black
118 screen.fill((0, 0, 0))
119
120 # Draw all sprites
121 for entity in all_sprites:
122     screen.blit(entity.surf, entity.rect)
123
124 # Flip everything to the display
125 pygame.display.flip()

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

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

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

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

  1. Создайте новыйEnemy.

  2. Добавьте его вall_sprites иenemies.

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

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

78 # Create the screen object
79 # The size is determined by the constant SCREEN_WIDTH and SCREEN_HEIGHT
80 screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT))
81
82 # Create a custom event for adding a new enemy
83 ADDENEMY = pygame.USEREVENT + 1
84 pygame.time.set_timer(ADDENEMY, 250)
85
86 # Instantiate player. Right now, this is just a rectangle.
87 player = Player()

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

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

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

100 # Main loop
101 while running:
102     # Look at every event in the queue
103     for event in pygame.event.get():
104         # Did the user hit a key?
105         if event.type == KEYDOWN:
106             # Was it the Escape key? If so, stop the loop.
107             if event.key == K_ESCAPE:
108                 running = False
109
110         # Did the user click the window close button? If so, stop the loop.
111         elif event.type == QUIT:
112             running = False
113
114         # Add a new enemy?
115         elif event.type == ADDENEMY:
116             # Create the new enemy and add it to sprite groups
117             new_enemy = Enemy()
118             enemies.add(new_enemy)
119             all_sprites.add(new_enemy)
120
121     # Get the set of keys pressed and check for user input
122     pressed_keys = pygame.key.get_pressed()
123     player.update(pressed_keys)
124
125     # Update enemy position
126     enemies.update()

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

Enemies flying by in pygame

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

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

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

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

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

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

130 # Draw all sprites
131 for entity in all_sprites:
132     screen.blit(entity.surf, entity.rect)
133
134 # Check if any enemies have collided with the player
135 if pygame.sprite.spritecollideany(player, enemies):
136     # If so, then remove the player and stop the loop
137     player.kill()
138     running = False

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

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

Pygame window

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

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

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

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

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

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

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

 7 # Import pygame.locals for easier access to key coordinates
 8 # Updated to conform to flake8 and black standards
 9 # from pygame.locals import *
10 from pygame.locals import (
11     RLEACCEL,
12     K_UP,
13     K_DOWN,
14     K_LEFT,
15     K_RIGHT,
16     K_ESCAPE,
17     KEYDOWN,
18     QUIT,
19 )
20
21 # Define constants for the screen width and height
22 SCREEN_WIDTH = 800
23 SCREEN_HEIGHT = 600
24
25
26 # Define the Player object by extending pygame.sprite.Sprite
27 # Instead of a surface, use an image for a better-looking sprite
28 class Player(pygame.sprite.Sprite):
29     def __init__(self):
30         super(Player, self).__init__()
31         self.image = pygame.image.load("jet.png").convert()
32         self.image.set_colorkey((255, 255, 255), RLEACCEL)
33         self.rect = self.image.get_rect()

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

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

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

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

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

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

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

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

  1. Создайте классCloud.

  2. Добавьте к нему изображение облака.

  3. Создайте метод.update(), который перемещаетcloud в левую часть экрана.

  4. Создайте настраиваемое событие и обработчик для создания новых объектовcloud через заданный интервал времени.

  5. Добавьте вновь созданные объектыcloud в новыйGroup с именемclouds.

  6. Обновите и нарисуйтеclouds в своем игровом цикле.

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

 83 # Define the cloud object by extending pygame.sprite.Sprite
 84 # Use an image for a better-looking sprite
 85 class Cloud(pygame.sprite.Sprite):
 86     def __init__(self):
 87         super(Cloud, self).__init__()
 88         self.surf = pygame.image.load("cloud.png").convert()
 89         self.surf.set_colorkey((0, 0, 0), RLEACCEL)
 90         # The starting position is randomly generated
 91         self.rect = self.surf.get_rect(
 92             center=(
 93                 random.randint(SCREEN_WIDTH + 20, SCREEN_WIDTH + 100),
 94                 random.randint(0, SCREEN_HEIGHT),
 95             )
 96
 97     # Move the cloud based on a constant speed
 98     # Remove the cloud when it passes the left edge of the screen
 99     def update(self):
100         self.rect.move_ip(-5, 0)
101         if self.rect.right < 0:
102             self.kill()

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

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

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

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

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

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

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

137 # Main loop
138 while running:
139     # Look at every event in the queue
140     for event in pygame.event.get():
141         # Did the user hit a key?
142         if event.type == KEYDOWN:
143             # Was it the Escape key? If so, then stop the loop.
144             if event.key == K_ESCAPE:
145                 running = False
146
147         # Did the user click the window close button? If so, stop the loop.
148         elif event.type == QUIT:
149             running = False
150
151         # Add a new enemy?
152         elif event.type == ADDENEMY:
153             # Create the new enemy and add it to sprite groups
154             new_enemy = Enemy()
155             enemies.add(new_enemy)
156             all_sprites.add(new_enemy)
157
158         # Add a new cloud?
159         elif event.type == ADDCLOUD:
160             # Create the new cloud and add it to sprite groups
161             new_cloud = Cloud()
162             clouds.add(new_cloud)
163             all_sprites.add(new_cloud)

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

167 # Update the position of enemies and clouds
168 enemies.update()
169 clouds.update()
170
171 # Fill the screen with sky blue
172 screen.fill((135, 206, 250))

Строка 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 перед началом игрового цикла:

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

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

188 # Flip everything to the display
189 pygame.display.flip()
190
191 # Ensure program maintains a rate of 30 frames per second
192 clock.tick(30)

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

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

Setting the frame rate in pygame

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

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

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

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

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

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

106 # Setup for sounds. Defaults are good.
107 pygame.mixer.init()
108
109 # Initialize pygame
110 pygame.init()
111
112 # Set up the clock for a decent framerate
113 clock = pygame.time.Clock()

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

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

135 # Load and play background music
136 # Sound source: http://ccmixter.org/files/Apoxode/59262
137 # License: https://creativecommons.org/licenses/by/3.0/
138 pygame.mixer.music.load("Apoxode_-_Electric_1.mp3")
139 pygame.mixer.music.play(loops=-1)
140
141 # Load all sound files
142 # Sound sources: Jon Fincher
143 move_up_sound = pygame.mixer.Sound("Rising_putter.ogg")
144 move_down_sound = pygame.mixer.Sound("Falling_putter.ogg")
145 collision_sound = pygame.mixer.Sound("Collision.ogg")

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

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

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

26 # Define the Player object by extending pygame.sprite.Sprite
27 # Instead of a surface, use an image for a better-looking sprite
28 class Player(pygame.sprite.Sprite):
29     def __init__(self):
30         super(Player, self).__init__()
31         self.surf = pygame.image.load("jet.png").convert()
32         self.surf.set_colorkey((255, 255, 255), RLEACCEL)
33         self.rect = self.surf.get_rect()
34
35     # Move the sprite based on keypresses
36     def update(self, pressed_keys):
37         if pressed_keys[K_UP]:
38             self.rect.move_ip(0, -5)
39             move_up_sound.play()
40         if pressed_keys[K_DOWN]:
41             self.rect.move_ip(0, 5)
42             move_down_sound.play()

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

201 # Check if any enemies have collided with the player
202 if pygame.sprite.spritecollideany(player, enemies):
203     # If so, then remove the player
204     player.kill()
205
206     # Stop any moving sounds and play the collision sound
207     move_up_sound.stop()
208     move_down_sound.stop()
209     collision_sound.play()
210
211     # Stop the loop
212     running = False

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

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

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

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

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

Pygame window

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

Возможно, вы заметили комментарий в строках 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 в этом руководстве.

Не стесняйтесь оставлять комментарии ниже. Счастливого Pythoning!