Pygame display flip что это
Перейти к содержимому

Pygame display flip что это

  • автор:

Обработка ввода и анимации¶

Взаимодействие пользователя с компьютером основано на событиях, любые действия производимые пользователем порождают события — движение мыши, нажатия клавиш, специальных кнопок. Внутри библиотеки PyGame есть инструменты для обратки событий, которые происходят внутри приложений.

Мы с вами уже знакомы с методом получения всех произошедших событий — pygame.event.get() , который возвращает события, произошедшие с последнего обращения.

Помимо событий выхода из приложения pygame.QUIT , есть множество других — например, нажатия клавиш pygame.KEYDOWN или отпускания pygame.KEYUP .

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

import sys import pygame pygame.init() screen = pygame.display.set_mode((640, 480)) rect = pygame.Rect(40, 40, 120, 120) color = (0, 0, 0) while True: for event in pygame.event.get(): if event.type == pygame.QUIT: pygame.quit() sys.exit() if event.type == pygame.KEYDOWN: color = (255, 255, 255) pygame.draw.rect(screen, color, rect, 0) pygame.display.flip() 

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

При нажатии клавиш

При отпускании клавиш

При нажатии кнопки закрытия программы

При движении мыши

Обработка нажатий клавиш¶

Когда мы нажимаем клавишу, в систему передаётся не только информация о том, что какая-то кнопка нажата, но и её код.

  1. pygame.event.get()
  2. pygame.key.get_pressed()

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

keys = pygame.key.get_pressed() if keys[pygame.K_RETURN]: print("Нажата клавиша enter") 

Перемещение объектов¶

Умея получать от пользователя ввод, мы можем реализовать движения наших фигур на экране:

import sys import pygame pygame.init() screen = pygame.display.set_mode((640, 480)) rect = pygame.Rect(40, 40, 120, 120) while True: for event in pygame.event.get(): if event.type == pygame.QUIT: pygame.quit() sys.exit() if event.type == pygame.KEYDOWN: if event.key == pygame.K_LEFT: rect.move_ip(-40, 0) elif event.key == pygame.K_RIGHT: rect.move_ip(40, 0) elif event.key == pygame.K_UP: rect.move_ip(0, -40) elif event.key == pygame.K_DOWN: rect.move_ip(0, 40) pygame.draw.rect(screen, (255, 0, 0), rect, 0) pygame.display.flip() 

Для движения объектов используются методы move() и move_ip() , которыми обладают объекты, созданные с помощью функций из pygame.draw . Первый создаёт новую фигуру такого же типа, находящуюся по заданному смещению, второй непосредственно изменяет положение имеющейся фигуры.

Запустив данный код, вы можете заметить, что при перемещении фигуры от неё остаётся “след”. Это связано с тем, что при перемещении объекта, мы его не перемещаем на экране, а рисуем на новом месте поверх старого кадра.

Чтобы избежать данной проблемы, надо добавить вызов метода fill() у нашего экрана, на котором мы рисуем и передать ему цвет, которым надо закрасить фон. Данное действие надо проводить каждый раз перед отрисовкой кадра:

# здесь могла быть ваша проверка событий screen.fill((0, 0, 0)) pygame.draw.rect(screen, (255, 0, 0), rect, 0) pygame.display.flip() 

Использование спрайтов¶

Спрайт — двумерное изображение, используемое в играх.

pygame.image. load ( path ) ¶

Функция для загрузки спрайта из картинки. Path — путь до изображения, возвращает объект типа Surface, который можно использовать для рисования.

Для отрисовки спрайта на экране надо вызвать метод blit() у поверхности на которой производится отрисовка и передать объект спрайта вместе с координатами на которых необходимо отрисовать:

screen = pygame.display.set_mode((640, 480)) sprite = pygame.image.load("sprite.png") screen.blit(sprite, (20, 20)) pygame.quit() 

Анимации¶

В pygame анимации создаются при помощи набора спрайтов, которые последовательно отрисовываются:

animation_set = [pygame.image.load(f"ri>.png") for i in range(1, 6)] window = pygame.display.set_mode((640, 480)) clock = pygame.time.Clock() i = 0 while True: for event in pygame.event.get(): if event.type == pygame.QUIT: pygame.quit() sys.exit() window.fill((0,0,0)) window.blit(animation_set[i // 12], (100, 20)) i += 1 if i == 60: i = 0 pygame.display.flip() clock.tick(60) 

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

animation_set = [pygame.image.load(f"ri>.png") for i in range(1, 6)] 

Создаём часы, для ограничения количества кадров в секунду:

clock = pygame.time.Clock() 

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

i = 0 

Выбор анимации в зависимости от номера кадра и его отрисовка:

window.blit(animation_set[i // 12], (100, 20)) 

Изменение переменной, помогающей выбрать нужный кадр:

i += 1 if i == 60: i = 0 

Ограничение количества кадров в секунду, благодаря чему становится проще просчитывать анимации и синхронизировать события:

clock.tick(60) 

Задания¶

  1. Написать программу, которая будет писать в консоль названия нажатых клавиш. Реализовать поддержку enter, space, w, a, s, d, esc.
  2. С помощью циклов, используя квадрат 10х10 пикселей и его след, нарисовать рамку 100х100 пикселей.
  3. Написать программу, в которой по нажатию клавиш будет двигаться квадрат размером 20х20 пикселей. Учесть, что квадрат не должен выходить за границы экрана.
  4. Доработать программу, чтобы квадрат при каждом движении менял свой цвет на случайный.
  5. Реализовать анимацию движения персонажа. При движении влево или вправо должны показываться соответствующие анимации. Кадры для них взять из архива animations.zip

© Copyright Revision d00c0df4 .

Built with Sphinx using a theme provided by Read the Docs.
Read the Docs v: latest

Versions latest Downloads html On Read the Docs Project Home Builds Free document hosting provided by Read the Docs.

Каркас игры на Pygame

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

Эти команды (импорт модуля, вызовы функций, цикл) создают своего рода скелет, или каркас, программного кода. Выполнив его, вы получите «пустую» игру. Далее на этот скелет «подвешивается мясо», т. е. объявляются объекты и программируется логика игры.

Первое, что нужно сделать, это импортировать модуль pygame . После этого можно вывести на экран главное графическое окно игры с помощью функции set_mode модуля display , входящего в состав библиотеки pygame:

import pygame pygame.display.set_mode((600, 400))

Если выполнить этот код, то появится окно размером 600×400 пикселей и сразу закроется (в Linux, в Windows может зависнуть).

Функция set_mode принимает несколько аргументов – размер в виде кортежа или списка из двух целых чисел, флаги, глубину цвета и др. Их можно не указывать. В этом случае окно займет весь экран, цветовая глубина будет соответствовать системной. Обычно указывают только первый аргумент – размер окна.

Флаги предназначены для переключения на аппаратное ускорение, полноэкранный режим, отключения рамки окна и др. Например, команда pygame.display.set_mode((640, 560), pygame.RESIZABLE) делает окно изменяемым в размерах.

Выражение вида pygame.RESIZABLE (вместо RESIZABLE может быть любое другое слово большими буквами) обозначает обращение к той или иной константе, определенной в модуле pygame . Часто можно встретить код, в котором перед константами не пишется имя модуля (вместо, например, pygame.QUIT пишут просто QUIT ). В этом случае в начале программы надо импортировать не только pygame , но и содержимое модуля locals через from … import :

import pygame from pygame.locals import *

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

Вызов set_mode() возвращает объект типа Surface (поверхность). В программе может быть множество объектов данного класса, но тот, что возвращает set_mode() особенный. Его называют display surface, что можно перевести как экранная (дисплейная) поверхность. Она главная.

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

Функции update() и flip() модуля display обновляют содержимое окна игры. Это значит, что каждому пикселю заново устанавливается цвет. Представьте, что на зеленом фоне движется красный круг. За один кадр круг смещается на 5 пикселей. От кадра к кадру картинка целого окна изменяется незначительно, но в памяти окно будет перерисовываться полностью. Если частота составляет 60 кадров в секунду (FPS=60), то за секунду в памяти компьютера произойдет 60 обновлений множества значений, соответствующих экранным пикселям, что дает по большей части бессмысленную нагрузку на вычислительные мощности.

Если функции update() не передавать аргументы, то будут обновляться значения всей поверхности окна. Однако можно передать более мелкую прямоугольную область или список таковых. В этом случае обновляться будут только они.

Функция flip() решает проблему иным способом. Она дает выигрыш, если в set_mod() были переданы определенные флаги. Например,

pygame.display.set_mode(flags=pygame.FULLSCREEN | pygame.OPENGL | pygame.DOUBLEBUF)

Вернемся к нашим двум строчкам кода, где мы импортируем pygame создаем главное окно размером 600×400 пикселей. Почему окно сразу закрывается? Очевидно потому, что программа заканчивается после выполнения этих выражений. Ни импорт библиотеки, ни вызов set_mode() не предполагают входа в «режим циклического ожидания событий». В tkinter для этого используется метод mainloop() экземпляра Tk() . В pygame же требуется собственноручно создать бесконечный цикл, заставляющий программу зависнуть. Основная причина в том, что только программист знает, какая часть его кода должна циклически обрабатываться, а какая – нет. Например, код, создающий классы, объекты и функции не помещают в цикл.

Итак, создадим в программе бесконечный цикл:

# Осторожно! Эта программа зависнет. import pygame as pg pg.display.set_mode((600, 400)) while 1: pass 

После такого окно уже не закроется, а программа благополучно зависнет насовсем. Многократные клики по крестику не помогут. Только принудительная остановка программы через среду разработки или Ctrl + С , если запускали через терминал.

Как сделать так, чтобы программа закрывалась при клике на крестик окна, а также при нажатии Alt + F4 ? Pygame должен воспринимать такие действия как определенный тип событий.

Добавим в цикл магии:

# Окно закроется, но с ошибкой. import pygame as pg pg.display.set_mode((600, 400)) while 1: for i in pg.event.get(): if i.type == pg.QUIT: pg.quit()

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

Рассмотрим выражение pygame.event.get() . Модуль event библиотеки pygame содержит функцию get , которая забирает список событий из очереди, в которую записываются все произошедшие события. То, что возвращает get() – это список. Забранные события удаляются из очереди, то есть второй раз они уже забираться не будут, а в очередь продолжают записываться новые события.

Цикл for просто перебирает схваченный на данный момент (в текущей итерации цикла) список событий. Каждое событие он присваивает переменной i или любой другой. Чтобы было понятней, перепишем программу таким образом:

# Окно закроется, но с ошибкой. import pygame as pg pg.display.set_mode((600, 400)) while 1: events = pg.event.get() print(events) for i in events: if i.type == pg.QUIT: print(pg.QUIT) print(i) print(i.type) pg.quit()

На экране вы увидите примерно такое:

… [] [)>, )>] 256 )> 256 Traceback (most recent call last): File "ex3_event2.py", line 7, in events = pg.event.get() pygame.error: video system not initialized

Вверху будет множество пустых квадратных скобок, которые соответствуют пустым спискам events , создаваемым на каждой итерации цикла while . И только когда окно закрывается, генерируются два события. Свойство type второго имеет значение 256, что совпадает со значением константы QUIT .

В pygame событие – это объект класса Event . А если это объект, то у него есть атрибуты (свойства и методы). В данном случае мы отслеживаем только те события, у которых значение свойства type совпадает со значением константы QUIT модуля pygame . Это значение присваивается type тогда, когда происходят события нажатия на крестик или Alt + F4 . Когда эти события происходят, то в данном случае мы хотим, чтобы выполнилась функция quit() модуля pygame , которая завершает его работу.

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

В данном случае происходит переход к следующей итерации цикла while . И здесь выполнить функцию get() модуля event оказывается уже невозможным. Возникает исключение и программа завершается. По-сути программу завершает не функция pygame.quit() , а выброшенное, но не обработанное, исключение.

Данную проблему можно решить разными способами. Часто используют функцию exit() модуля sys . В этом случае код выглядит примерно так:

import pygame as pg import sys pg.display.set_mode((600, 400)) while 1: for i in pg.event.get(): if i.type == pg.QUIT: pg.quit() sys.exit()

Сначала отключается pygame , потом происходит выход из программы. Такой вариант вероятно следует считать наиболее безопасным завершением. Команда pygame.quit() не обязательна. Если завершается программа, то отключится и pygame .

Другой вариант – не допустить следующей итерации цикла. Для этого потребуется дополнительная переменная:

import pygame as pg pg.display.set_mode((600, 400)) play = True while play: for i in pg.event.get(): if i.type == pg.QUIT: play = False

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

Нередко код основной ветки программы помещают в функцию, например, main() . Она выполняется, если файл запускается как скрипт, а не импортируется как модуль. В этом случае для завершения программы проще использовать оператор return , который осуществляет выход из функции.

import pygame as pg def main(): pg.display.set_mode((600, 400)) while True: for i in pg.event.get(): if i.type == pg.QUIT: return if __name__ == "__main__": main()

Теперь зададимся вопросом, с какой скоростью крутится цикл while ? С большой, зависящей от мощности компьютера. Но в данном случае такая скорость не есть необходимость, она даже вредна, так как бессмысленно расходует ресурсы. Человек дает команды и воспринимает изменения куда медленнее.

Для обновления экрана в динамической игре часто используют 60 кадров в секунду, а в статической, типа пазла, достаточно будет 30-ти. Из этого следует, что циклу незачем работать быстрее.

Поэтому в главном цикле следует выполнять задержку. Делают это либо вызовом функции delay() модуля time библиотеки pygame, либо создают объект часов и устанавливают ему частоту кадров. Первый способ проще, второй – более профессиональный.

. while 1: for i in pg.event.get(): if i.type == pg.QUIT: sys.exit() pg.time.delay(20)

Функция delay() принимает количество миллисекунд (1000 мс = 1 с). Если передано значение 20, то за секунду экран обновится 50 раз. Другими словами, частота составит 50 кадров в секунду.

import pygame as pg import sys pg.display.set_mode((600, 400)) clock = pg.time.Clock() while 1: for i in pg.event.get(): if i.type == pg.QUIT: sys.exit() clock.tick(60)

Методу tick() класса Clock передается непосредственно желаемое количество кадров в секунду. Задержку он вычисляет сам. То есть если внутри цикла указано tick(60) – это не значит, что задержка будет 60 миллисекунд или произойдет 60 обновлений экрана за одну итерацию цикла. Это значит, что на каждой итерации цикла секунда делится на 60 и уже на вычисленную величину выполняется задержка.

Нередко частоту кадров выносят в отдельную константоподобную переменную:

. FPS = 60 . clock = pg.time.Clock() while 1: . clock.tick(FPS)

В начало цикла или конец вставлять задержку зависит от контекста. Если до цикла происходит отображение каких-либо объектов на экране, то скорее всего надо вставлять в начало цикла. Если первое появление объектов на экране происходит внутри цикла, то в конец.

В итоге каркас игры на Pygame должен выглядеть примерно так:

# здесь подключаются модули import pygame import sys # здесь определяются константы, # классы и функции FPS = 60 # здесь происходит инициация, # создание объектов pygame.init() pygame.display.set_mode((600, 400)) clock = pygame.time.Clock() # если надо до цикла отобразить # какие-то объекты, обновляем экран pygame.display.update() # главный цикл while True: # задержка clock.tick(FPS) # цикл обработки событий for i in pygame.event.get(): if i.type == pygame.QUIT: sys.exit() # -------- # изменение объектов # -------- # обновление экрана pygame.display.update()

Функция pygame.init() необходима для инициации всех модулей библиотеки pygame. Для инициации только функций модуля pygame.display следует вызывать pygame.display.init() . Однако функция pygame.display.set_mod() , помимо того что создает главную поверхность, также выполняет инициацию модуля display . Поэтому если set_mod() вызывается до вызова других объектов этого модуля, то вызов init() не является обязательным.

Практическая работа

В модуле pygame.display есть функция set_caption() . Ей передается строка, которую она устанавливает в качестве заголовка окна. Сделайте так, чтобы каждую секунду заголовок окна изменялся.

Курс с примерами решений практических работ:
pdf-версия

X Скрыть Наверх

Pygame. Введение в разработку игр на Python

Создаём «Змейку» — первую игру на Python и Pygame

Учимся программировать через разработку игр. Сегодня напишем знакомую всем «Змейку» — вспомним правила игры и реализуем их на Python.

Иллюстрация: Оля Ежак для Skillbox Media

Антон Яценко

Антон Яценко
Изучает Python, его библиотеки и занимается анализом данных. Любит путешествовать в горах.

Pygame — популярная библиотека для создания игр под различные устройства на Windows, macOS, Linux или Android. Она помогает разработчику не только описать геймплей, но и работать с клавиатурой, мышью, акселерометром, звуком и видео.

Первая версия Pygame была представлена Питом Шиннерсом в октябре 2000 года. За 22 года вокруг библиотеки сложилось большое комьюнити, а о работе с ней написано несколько десятков книг. Последняя стабильная версия на июль 2022 года — 2.1.2.

Давайте разберёмся в том, как устроена Pygame, и напишем свою первую игру — классическую «Змейку» на Python, которую студенты часто берут для курсовой работы по программированию.

Устанавливаем Pygame и разбираемся

Pygame — не самостоятельная библиотека. На самом деле это обёртка для библиотеки SDL, Simple DirectMedia Layer. Именно SDL позволяет задействовать любые внешние устройства — например, мышь или клавиатуру. А Pygame делает работу с ними удобной для Python-разработчика.

Установить Pygame просто. Для этого воспользуемся терминалом или командной строкой и командой pip:

Теперь игровое окно не закрывается само по себе. Однако и закрыть его мы тоже не сможем — если нажать на кнопку «Выход», ничего не произойдёт. Исправляем это с помощью кода: добавляем событие QUIT, закрывающее окно.

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

Шаг 3

Описываем движения змейки

Управлять перемещением змейки можно с помощью специального класса Pygame KEYDOWN. Класс позволяет использовать четыре стандартных события, получая их с клавиавтуры: K_UP, K_DOWN, K_LEFT и K_RIGHT — они соответствуют движениям змейки вверх, вниз, влево и вправо. Срабатывание любого события из класса KEYDOWN приводит к изменению положения змейки. Зададим шаг этого движения в 10 пикселей.

Кроме того, мы должны создать две переменные для хранения значений координат первой клетки нашей змейки по осям x и y. Назовём их x1_change и y1_change.

Шаг 4

Учитываем препятствия — границы игрового поля

Если змейка попадает на границу экрана, то игрок терпит поражение, а игра заканчивается. Чтобы закодить это правило, можно воспользоваться оператором if, который определяет координаты x и y для змейки и анализирует, выходят ли они за границы игрового поля. Добавим необходимый код.

Шаг 5

Добавляем еду для змейки

Теперь добавим «еду». Используем библиотеку random, чтобы она появлялась в случайном месте на игровом поле. Когда наша змейка будет проходить через еду, то её длина будет увеличиваться. Это мы добавим на следующем шаге. Кроме того, дадим возможность игроку выйти из игры или начать игру заново после проигрыша.

А если выполнить условие для завершения игры, то появится сообщение с предложением выйти из игры или начать её заново:

Шаг 6

Увеличиваем длину змейки

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

Шаг 7

Добавляем отображение счёта

Добавим отображение счёта текущей игры. Для этого создадим функцию Your_score. Она будет отображать длину змейки, вычитая из неё 1 (ведь 1 — это начальный размер змейки, и это не является достижением игрока).

Можно считать, что наша работа над «Змейкой» закончена. Мы полностью реализовали геймплей, который запланировали на старте работы.

Итоговый код

Наш код полностью и без комментариев:

import pygame import time import random pygame.init() white = (255, 255, 255) yellow = (255, 255, 102) black = (0, 0, 0) red = (213, 50, 80) green = (0, 255, 0) blue = (50, 153, 213) dis_width = 800 dis_height = 600 dis = pygame.display.set_mode((dis_width, dis_height)) pygame.display.set_caption('Змейка от Skillbox') clock = pygame.time.Clock() snake_block = 10 snake_speed = 15 font_style = pygame.font.SysFont("bahnschrift", 25) score_font = pygame.font.SysFont("comicsansms", 35) def Your_score(score): value = score_font.render("Ваш счёт: " + str(score), True, yellow) dis.blit(value, [0, 0]) def our_snake(snake_block, snake_list): for x in snake_list: pygame.draw.rect(dis, black, [x[0], x[1], snake_block, snake_block]) def message(msg, color): mesg = font_style.render(msg, True, color) dis.blit(mesg, [dis_width / 6, dis_height / 3]) def gameLoop(): game_over = False game_close = False x1 = dis_width / 2 y1 = dis_height / 2 x1_change = 0 y1_change = 0 snake_List = [] Length_of_snake = 1 foodx = round(random.randrange(0, dis_width - snake_block) / 10.0) * 10.0 foody = round(random.randrange(0, dis_height - snake_block) / 10.0) * 10.0 while not game_over: while game_close == True: dis.fill(blue) message("Вы проиграли! Нажмите Q для выхода или C для повторной игры", red) Your_score(Length_of_snake - 1) pygame.display.update() for event in pygame.event.get(): if event.type == pygame.KEYDOWN: if event.key == pygame.K_q: game_over = True game_close = False if event.key == pygame.K_c: gameLoop() for event in pygame.event.get(): if event.type == pygame.QUIT: game_over = True if event.type == pygame.KEYDOWN: if event.key == pygame.K_LEFT: x1_change = -snake_block y1_change = 0 elif event.key == pygame.K_RIGHT: x1_change = snake_block y1_change = 0 elif event.key == pygame.K_UP: y1_change = -snake_block x1_change = 0 elif event.key == pygame.K_DOWN: y1_change = snake_block x1_change = 0 if x1 >= dis_width or x1 < 0 or y1 >= dis_height or y1 < 0: game_close = True x1 += x1_change y1 += y1_change dis.fill(blue) pygame.draw.rect(dis, green, [foodx, foody, snake_block, snake_block]) snake_Head = [] snake_Head.append(x1) snake_Head.append(y1) snake_List.append(snake_Head) if len(snake_List) > Length_of_snake: del snake_List[0] for x in snake_List[:-1]: if x == snake_Head: game_close = True our_snake(snake_block, snake_List) Your_score(Length_of_snake - 1) pygame.display.update() if x1 == foodx and y1 == foody: foodx = round(random.randrange(0, dis_width - snake_block) / 10.0) * 10.0 foody = round(random.randrange(0, dis_height - snake_block) / 10.0) * 10.0 Length_of_snake += 1 clock.tick(snake_speed) pygame.quit() quit() gameLoop()

Что дальше?

Узнать об особенностях работы с Pygame и возможностях библиотеки можно в официальной документации. Углубиться в разработку и попробовать другие игры можно благодаря специализированным книгам:

  • «Учим Python, делая крутые игры» Эла Свейгарта;
  • «Beginning Game Development with Python and Pygame: From Novice to Professional» Уилла Макгугана;
  • «Program Arcade Games: With Python and Pygame» Пола Винсента Крэйвена.

Больше интересного про код в нашем телеграм-канале. Подписывайтесь!

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

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

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

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

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

Предыстория и установка

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

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

$ pip install pygame

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

$ python3 -m pygame.examples.aliens

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

Базовая программа 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()

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

Разберем этот код по частям:

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

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

Концепции PyGame

Поскольку библиотеки 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().

Изображения и прямоугольники

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

Как упоминалось выше, объекты Surface представлены прямоугольниками, как и многие другие объекты в pygame. Прямоугольники используются настолько широко, что существует специальный класс 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.. Импортируя определенные константы из pygame.locals, вы можете вместо этого использовать синтаксис . Это сэкономит вам несколько нажатий клавиш и улучшит общую читаемость.

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

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

# 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. Это часть окна, которой вы можете управлять, в то время как ОС управляет границами окна и строкой заголовка.

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

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

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

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

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

  • Игрок сталкивается с препятствием.
  • Игрок закрывает окно.

Первое, что делает игровой цикл, — обрабатывает пользовательский ввод, чтобы позволить игроку перемещаться по экрану. Следовательно, вам нужен какой-то способ захвата и обработки различных входных данных. Вы делаете это, используя систему событий 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 проверяют, является ли текущее KEYDOWN событием. Если да, то программа проверяет, какая клавиша была нажата, по атрибуту event.key. Если ключом является Esc, обозначенный K_ESCAPE, то он выходит из игрового цикла, устанавливая running = False.
  • Строки 41 и 42 выполняют аналогичную проверку для типа события с именем QUIT. Это событие происходит только тогда, когда пользователь нажимает кнопку закрытия окна. Пользователь также может использовать любое другое действие операционной системы, чтобы закрыть окно.

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

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

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

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

  • screen.fill() — заполнить фон
  • 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, в строке 48 создается новый Surface. Он имеет ширину 50 пикселей, высоту 50 пикселей и назначается surf. На этом этапе вы относитесь к нему так же, как к файлу screen. Итак, в строке 51 вы заполняете его черным цветом. Вы также можете получить доступ к его основе, используя .get_rect(). Это сохраняет rect для последующего использования.

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

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

# 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 принимает два аргумента:

  • Рисование Surface_
  • Место, в котором нужно нарисовать его

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

Причина, по которой изображение выглядит не по центру, заключается в том, что верхний левый угол не помещает .blit() в указанное место. Если вы хотите быть в центре, то вам придется сделать некоторые математические расчеты, чтобы сместить его вверх и влево. Вы можете сделать это, вычитая ширину и высоту из ширины и высоты экрана, разделив каждую на 2, чтобы найти центр, а затем передав эти числа в качестве аргументов :surfsurfsurfscreen.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 объектами, чтобы упростить рисование, но как узнать, где их рисовать? Как узнать, столкнулось ли препятствие с игроком? Что происходит, когда препятствие улетает за пределы экрана? Что делать, если вы хотите рисовать фоновые изображения, которые также движутся? Что делать, если вы хотите, чтобы ваши изображения были анимированными? Вы можете справиться со всеми этими и другими ситуациями с помощью спрайтов .

В терминах программирования спрайт — это двухмерное представление чего-либо на экране. По сути, это картинка. 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, расширяя строку 22 pygame.sprite.Sprite. Затем используете .__init__() .super()для вызова метода.

Далее вы определяете и инициализируете сохранение изображения .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()

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

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *