Библиотека 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) или заставить его отталкиваться от стен (изменяя направлении по достижении края).