Библиотека Pygame / Часть 2. Работа со спрайтами

Вторая часть серии руководств «Разработка игр с помощью Pygame». Она предназначена для программистов начального и среднего уровней, которые заинтересованы в создании игр и улучшении собственных навыков кодирования на Python. Начать стоит с урока: «Библиотека Pygame / Часть 1. Введение».

Что такое спрайт?

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

Для загрузки и отрисовки спрайтов в случай этой игры их нужно добавить в разделы “Обновление” и “Визуализация” игрового цикла. Несложно представить, что если в игре много спрайтов, то цикл довольно быстро станет большим и запутанным. В Pygame для этого есть решение: группировка спрайтов.

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

clock = pygame.time.Clock()
all_sprites = pygame.sprite.Group() 

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

    # Обновление
    all_sprites.update()

<span style="box-sizing: border-box; font-weight: inherit !important; font-size: inherit; padding-left: 0px; color: rgb(243, 228, 203);" class="token comment"># Отрисовка</span>
screen<span style="box-sizing: border-box; font-weight: inherit !important; font-size: inherit; color: rgb(255, 255, 238);" class="token punctuation">.</span>fill<span style="box-sizing: border-box; font-weight: inherit !important; font-size: inherit; color: rgb(255, 255, 238);" class="token punctuation">(</span>BLACK<span style="box-sizing: border-box; font-weight: inherit !important; font-size: inherit; color: rgb(255, 255, 238);" class="token punctuation">)</span>
all_sprites<span style="box-sizing: border-box; font-weight: inherit !important; font-size: inherit; color: rgb(255, 255, 238);" class="token punctuation">.</span>draw<span style="box-sizing: border-box; font-weight: inherit !important; font-size: inherit; color: rgb(255, 255, 238);" class="token punctuation">(</span>screen<span style="box-sizing: border-box; font-weight: inherit !important; font-size: inherit; color: rgb(255, 255, 238);" class="token punctuation">)</span>

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

Создание спрайта

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

Начнем с определения нового спрайта:

class Player(pygame.sprite.Sprite):

class сообщает Python, что определяется новый объект, который будет спрайтом игрока. Его тип pygame.sprite.Sprite. Это значит, что он будет основан на заранее определенном в Pygame классе Sprite.

Первое, что нужно в определении class — специальная функция init(), включающая код, который будет запущен при создании нового объекта этого типа. Также у каждого спрайта в Pygame должно быть два свойства: image и rect.

class Player(pygame.sprite.Sprite):
    def init(self):
        pygame.sprite.Sprite.init(self)
        self.image = pygame.Surface((50, 50))
        self.image.fill(GREEN)
        self.rect = self.image.get_rect()

Первая строка, Pygame.sprite.Sprite.__init__(self) требуется в Pygame — она запускает инициализатор встроенных классов Sprite. Далее необходимо определить свойство image. Сейчас просто создадим квадрат размером 50х50 и заполним его зеленым (GREEN) цветом. Чуть позже вы узнаете, как сделать image спрайта красивее, используя, например, персонажа или космический корабль, но сейчас достаточно сплошного квадрата.

Дальше необходимо определить rect спрайта. Это сокращенное от rectangle (прямоугольник). Прямоугольники повсеместно используются в Pygame для отслеживания координат объектов. Команда get_rect() оценивает изображение image и высчитывает прямоугольник, способный окружить его.

rect можно использовать для размещения спрайта в любом месте. Начнем с создания спрайта по центру:

class Player(pygame.sprite.Sprite):
    def __init__(self):
        pygame.sprite.Sprite.__init__(self)
        self.image = pygame.Surface((50, 50))
        self.image.fill(GREEN)
        self.rect = self.image.get_rect()
        self.rect.center = (WIDTH / 2, HEIGHT / 2)

Теперь, после определения спрайта игрока Player, нужно отрисовать (создать) его, инициализировав экземпляр (instance) класса Player. Также нужно обязательно добавить спрайт в группу all_sprites.

all_sprites = pygame.sprite.Group()
player = Player()
all_sprites.add(player)

Сейчас, если запустить программу, по центру окна будет находиться зеленый квадрат. Увеличьте значения WIDTH и HEIGHT в настройках программы, чтобы создать достаточно пространства для движения спрайта в следующем шаге.

Движение спрайта

В игровом цикле есть функция all_sprites.update(). Это значит, что для каждого спрайта в группе Pygame ищет функцию update() и запускает ее. Чтобы спрайт двигался, нужно определить его правила обновления:

class Player(pygame.sprite.Sprite):
    def __init__(self):
        pygame.sprite.Sprite.__init__(self)
        self.image = pygame.Surface((50, 50))
        self.image.fill(GREEN)
        self.rect = self.image.get_rect()
        self.rect.center = (WIDTH / 2, HEIGHT / 2)

<span style="box-sizing: border-box; font-weight: inherit !important; font-size: inherit; color: rgb(128, 255, 255);" class="token keyword keyword-def">def</span> <span style="box-sizing: border-box; font-weight: inherit !important; font-size: inherit; color: rgb(255, 128, 176);" class="token function">update</span><span style="box-sizing: border-box; font-weight: inherit !important; font-size: inherit; color: rgb(255, 255, 238);" class="token punctuation">(</span>self<span style="box-sizing: border-box; font-weight: inherit !important; font-size: inherit; color: rgb(255, 255, 238);" class="token punctuation">)</span><span style="box-sizing: border-box; font-weight: inherit !important; font-size: inherit; color: rgb(255, 255, 238);" class="token punctuation">:</span>
    self<span style="box-sizing: border-box; font-weight: inherit !important; font-size: inherit; color: rgb(255, 255, 238);" class="token punctuation">.</span>rect<span style="box-sizing: border-box; font-weight: inherit !important; font-size: inherit; color: rgb(255, 255, 238);" class="token punctuation">.</span>x <span style="box-sizing: border-box; font-weight: inherit !important; font-size: inherit; color: rgb(255, 222, 164);" class="token operator">+=</span> <span style="box-sizing: border-box; font-weight: inherit !important; font-size: inherit; color: rgb(255, 148, 255);" class="token number">5</span>

Это значит, что при каждом игровом цикле x-координата спрайта будет увеличиваться на 5 пикселей. Запустите программу, чтобы посмотреть, как он скрывается за пределами экрана, достигая правой стороны.

Исправить это можно, заставив спрайт двигаться по кругу — когда он добирается до правой стороны экрана, просто переносить его влево. Это легко сделать, используя элемент управления rect спрайта:

Так, если левая сторона rect пропадает с экрана, просто задаем значение правого края равное 0:

class Player(pygame.sprite.Sprite):
    def __init__(self):
        pygame.sprite.Sprite.__init__(self)
        self.image = pygame.Surface((50, 50))
        self.image.fill(GREEN)
        self.rect = self.image.get_rect()
        self.rect.center = (WIDTH / 2, HEIGHT / 2)

<span style="box-sizing: border-box; font-weight: inherit !important; font-size: inherit; color: rgb(128, 255, 255);" class="token keyword keyword-def">def</span> <span style="box-sizing: border-box; font-weight: inherit !important; font-size: inherit; color: rgb(255, 128, 176);" class="token function">update</span><span style="box-sizing: border-box; font-weight: inherit !important; font-size: inherit; color: rgb(255, 255, 238);" class="token punctuation">(</span>self<span style="box-sizing: border-box; font-weight: inherit !important; font-size: inherit; color: rgb(255, 255, 238);" class="token punctuation">)</span><span style="box-sizing: border-box; font-weight: inherit !important; font-size: inherit; color: rgb(255, 255, 238);" class="token punctuation">:</span>
    self<span style="box-sizing: border-box; font-weight: inherit !important; font-size: inherit; color: rgb(255, 255, 238);" class="token punctuation">.</span>rect<span style="box-sizing: border-box; font-weight: inherit !important; font-size: inherit; color: rgb(255, 255, 238);" class="token punctuation">.</span>x <span style="box-sizing: border-box; font-weight: inherit !important; font-size: inherit; color: rgb(255, 222, 164);" class="token operator">+=</span> <span style="box-sizing: border-box; font-weight: inherit !important; font-size: inherit; color: rgb(255, 148, 255);" class="token number">5</span>
    <span style="box-sizing: border-box; font-weight: inherit !important; font-size: inherit; color: rgb(128, 255, 255);" class="token keyword keyword-if">if</span> self<span style="box-sizing: border-box; font-weight: inherit !important; font-size: inherit; color: rgb(255, 255, 238);" class="token punctuation">.</span>rect<span style="box-sizing: border-box; font-weight: inherit !important; font-size: inherit; color: rgb(255, 255, 238);" class="token punctuation">.</span>left <span style="box-sizing: border-box; font-weight: inherit !important; font-size: inherit; color: rgb(255, 222, 164);" class="token operator">&gt;</span> WIDTH<span style="box-sizing: border-box; font-weight: inherit !important; font-size: inherit; color: rgb(255, 255, 238);" class="token punctuation">:</span>
        self<span style="box-sizing: border-box; font-weight: inherit !important; font-size: inherit; color: rgb(255, 255, 238);" class="token punctuation">.</span>rect<span style="box-sizing: border-box; font-weight: inherit !important; font-size: inherit; color: rgb(255, 255, 238);" class="token punctuation">.</span>right <span style="box-sizing: border-box; font-weight: inherit !important; font-size: inherit; color: rgb(255, 222, 164);" class="token operator">=</span> <span style="box-sizing: border-box; font-weight: inherit !important; font-size: inherit; color: rgb(255, 148, 255);" class="token number">0</span>

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

На этом все. Отправляйтесь изучать и экспериментировать, но не забывайте, что все, что вы помещаете в метод update(), будет происходить в каждом кадре. Попробуйте научить спрайт двигаться сверху вниз (изменив координату y) или заставить его отталкиваться от стен (изменяя направлении по достижении края).

Комментарии (4)


  1. GritsanY
    12.11.2021 15:48
    +1

    Опять всё форматирование в блоках кода поехало.


  1. Myxach
    15.11.2021 03:53

    Разве это не должно быть в разработке игр?


  1. xtotdam
    16.11.2021 10:25

    Это всё супер круто, однако одними играми, несмотря ни на что, pygame не ограничивается. Это почти удобный движок для, например, быстрой на коленке визуализации результатов моделирования. Планируете ли вы углубиться в эту тему и рассказать о прочих возможностях pygame, или это останется не очень грамотным и не очень хорошо оформленным (и, судя по поехавшему оформлению, где-то, хм, скопированным без указания источника) гайдом "Как сделать очередной hello world"?


    1. secretplayer Автор
      18.11.2021 16:51

      Да но если брать игровой движок лучше учить c++