Программирование аркадных игр и обучение информатике

Программирование аркадных игр
и обучение информатике

Chapter 13: Введение в спрайты

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

fig.blender01

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

Библиотека Pygame поддерживает спрайты. Этот код поможет с управлением анимацией, обнаружением столкновений, управлением групп спрайтов.

13.1 Обычные спрайты и столкновения

Этот пример показывает процесс создания набора чёрных блоков с их последовательным сбором при использовании красных блоков, управляемых мышью. Программа сохраняет “очки”, записывая, как много блоков уже было собрано. Код к этому примеру можно найти по ссылке:
http://cs.simpson.edu/index.php?chapter=example_code

# Sample Python/Pygame Programs
# Simpson College Computer Science
# http:
cs.simpson.edu import pygame import random # Задать цвета black = ( 0, 0, 0) white = ( 255, 255, 255) red = ( 255, 0, 0)

Библиотека pygame импортируется ради поддержки спрайтов. Библиотека random импортируется ради случайного размещения блоков. Установка цветов проходит по стандартной схеме. Пока в этом примере нет ничего нового.

# Этот класс представляет блок        
# Он наследуется от класса "Sprite" из Pygame
class Block(pygame.sprite.Sprite):  

Начинается определение класса Block. Заметьте, что на строке 15 этот класс является дочерним классом класса Sprite. pygame.sprite. обозначает библиотеку и пакет, которые будут обсуждены несколько позже. Вся стандартная функциональность класса Sprite отныне будет частью класса Block.

    # Конструктор. Ему передаётся цвет блока,
    # а также его ширина и высота
    def __init__(self, color, width, height):
        # Вызвать конструктор родительского класса (Sprite)
        super().__init__() 

Конструктор класса Block, подобно конструкторам других классов, также берёт параметр self. Он также берёт параметр, определяющий цвет, высоту и ширину объекта.

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

        # Create an image of the block, and fill it with a color.
        # This could also be an image loaded from the disk.
        self.image = pygame.Surface([width, height])
        self.image.fill(color)

Этот код создаёт картинку, которая в скором времени появится на экране. Строка 25 создаёт пустую картинку. Строка 26 заполняет её чёрным. Если программе нужно что-то кроме чёрного квадрата, нужно менять именно эти строки.

Например, посмотрите на нижеприведённый код:

        self.image = pygame.Surface([width, height])
        self.image.fill(white)
        self.image.set_colorkey(white)
        pygame.draw.ellipse(self.image,color,[0,0,width,height])

Если бы в создание блока был поставлен этот код, то все блоки были бы в форме эллипсов. Эллипс рисуется на 25й строке, а 26 строка делает белый цвет прозрачным.

        self.image = pygame.image.load("player.png").convert()
        self.image.set_colorkey(white)

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

        # Достать прямоугольный объект, обладающий размерами картинки
        # Обновить позицию этого объекта, задав значения rect.x и rect.y
        self.rect = self.image.get_rect()

Атрибут rect - переменная, являющаяся инстанцией класса Rect, предоставляемого Pygame. Прямоугольник представляет размеры спрайта. Этот класс прямоугольник содержит настраиваемые атрибуты для x и y. Pygame нарисует спрайт там, куда указывают атрибуты x и y. Так что для передвижения спрайта программисту потребуется менять mySpriteRef.rect.x и mySpriteRef.rect.y, где mySpriteRef - переменная, указывающая на спрайт.

# Инициализировать Pygame
pygame.init()

# Задать размер экрана
screen_width=700
screen_height=400
screen=pygame.display.set_mode([screen_width,screen_height])

Этот код инициализирует pygame и создаёт окно для игры. В этом нет ничего отличного от других программ pygame.

# Это список спрайтов. Каждый блок добавляется в этот список.
# Список управляется классом, называющимся 'Group.'
block_list = pygame.sprite.Group()

# Это список каждого спрайта. Все блоки, а также блок игрока.
all_sprites_list = pygame.sprite.Group()

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

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

Вышеприведённый код создаёт два списка. Переменная all_sprites_list будет содержать в себе все спрайты в игре. Этот список будет использован для рисования всех спрайтов. Переменная block_list содержит каждый объект, с которым игрок может столкнуться. В этом примере он будет содержать в себе каждый объект игры, исключая объект игрока. Очевидно, в этом примере мы не хотим проверять столкновение объекта игрока с объектом игрока, поэтому программе нужен список, который не содержит самого игрока.

for i in range(50):
    # Здесь создаётся блок
    block = Block(black, 20, 15)

    # Задать случайное местоположение блоку
    block.rect.x = random.randrange(screen_width)
    block.rect.y = random.randrange(screen_height)
    
    # Добавить блок в список объектов
    block_list.add(block)
    all_sprites_list.add(block)

Цикл, начинающийся на 46й строке, добавляет 50 чёрных спрайтов-блоков на экран. Строка 48 создаёт новый блок, задаёт ему цвет, ширину, высоту. Строки 51 и 52 задают кординаты, где появится объект. Строка 55 добавляет блок в список блоков, с которыми может столкнуться игрок. Строка 56 добавляет его в список всех блоков.

 
# Создать красный блок игрока
player = Block(red, 20, 15)
all_sprites_list.add(player)

Строка 59 создаёт красный блок, который будет представлять игрока. Этот блок добавляется в all_sprites_list, чтобы было возможным его нарисовать.

# Быть в цикле до момента, пока пользователь не решит закрыть программу
done=False

# Используется для определения скорости обновления экрана
clock=pygame.time.Clock()

score = 0

# -------- Основной цикл программы -----------
while done==False:
    for event in pygame.event.get(): # Пользователь что-то сделал
        if event.type == pygame.QUIT: # Если пользователь нажал "закрыть"
            done=True # Отметить, что всё готово, так чтобы программа вышла из цикла

    # Очистить экран
    screen.fill(white)

Это стандартный програмный цикл. Строка 68 ставит очки игрока на 0.

    # Получить текущую позицию мыши в виде списка из двух чисел
    pos = pygame.mouse.get_pos()
    
    # Достать из списка x и y, 
    # так же, как мы бы доставали буквы из строки.
    # Установить объект игрока на позицию курсора мыши.
    player.rect.x=pos[0]
    player.rect.y=pos[1]

Строка 81 достаёт координаты мыши, подобно тому, как это было сделано в разобнанных ранее Pygame программах. Важная часть содержится в строка 86-87, где прямоугольник, содержащий спрайт игрока, переносится в новое местоположение. Помните, что прямоугольник был создан на строке 32 и этот код не будет работать без той строки.

    # Проверить, столкнулся ли с чем-нибудь блок игрока
    blocks_hit_list = pygame.sprite.spritecollide(player, block_list, True)  

Эта строка проверяет спрайт player на столкновение со всеми спрайтами из списка block_list. Код возвращает список пересекающихся спрайтов. Если ни один спрайт не пересекается со спрайтом игрока, возвращается пустой лист. Значение True уберёт все столкнувшиеся с игроком спрайты из списка. Если вместо него передать False, спрайты убраны не будут.

 
    # Проверить список столкновений.
    if len(blocks_hit_list) > 0:
        score +=len(blocks_hit_list)
        print( score )

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

    
    # Нарисовать все спрайты
    all_sprites_list.draw(screen)

Это заставлит нарисовать все спрайты в all_sprites_list.

 
    # Поставить лимит на 20 кадров в секунду
    clock.tick(20)

    # Обновить экран, вывести то, что мы нарисовали
    pygame.display.flip()

pygame.quit()

Это обновляет экран, а также вызывает метод quit после завершения цикла.

13.2 Передвижение спрайтов

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

Первым шагом будет добавить новый метод в класс Block. Этот метод будет называться update. Функция update будет вызываться автоматически, когда вызывается метод update для всего списка.

Разместите это в классе блока:

    def update(self):
        # Подвинуть блок на один пиксель вниз
        self.rect.y += 1

Затем поставьте следующую строку в главный цикл программы:

    # Вызвать метод update() для всех блоков в block_list
    block_list.update()

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

    def update(self):
        # Передвинуть блок на один пиксель вниз
        self.rect.y += 1
        if self.rect.y > screen_height:
			self.rect.y = random.randrange(-100,-10)
			self.rect.x = random.randrange(0,screen_width)

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

    def reset_pos(self):
        self.rect.y = random.randrange(-100,-10)
        self.rect.x = random.randrange(0,screen_width)
			
    def update(self):
        # Передвинуть блок на один пиксель вниз
        self.rect.y += self.change_y
        if self.rect.y > screen_height:
            self.reset_pos()
            self.game.score -= 1

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

    # Проверить, столкнулся ли блок игрока с чем-нибудь.
    blocks_hit_list = pygame.sprite.spritecollide(player, block_list, True)  
    
    # Обработать список со столкновениями.
    if len(blocks_hit_list) > 0:
        score +=len(blocks_hit_list)
        print( score )

Найдите вышеприведённый код. Измените True на False, чтобы блоки не уничтожались. Поменяйте условие if на for, идущий через каждый блок, с которым столкнулся игрок. Вызовите block.reset_pos() на каждом из этих блоков. ProgramArcadeGames.com/python_examples/en/sprite_sheets


You are not logged in. Log in here and track your progress.