Стрелялка с Pygame №5: улучшенные столкновения

260

Пятая часть проекта «Стрелялка с Pygame». Если пропустили, обязательно вернитесь и начните с первой части. В этот раз будем менять то, как Pygame обрабатывает столкновения спрайтов.

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

Что происходит со столкновениями?

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

Столкновения AABB

Тип столкновения по умолчанию в Pygame — функция collide_rect(), которая высчитывает атрибуты rect спрайтов, чтобы понять, пересекаются ли они. Это столкновения AABB, которые работают быстро и надежно. Однако если изображения спрайтов не являются прямоугольниками, то происходит ситуация как на изображении выше. Прямоугольники пересекаются, функция collide_rect возвращает True, но игрок разочарован, потому что он-то на самом деле избежал столкновения.

Вот как можно избежать этой ситуации.

Уменьшение количества «пустого» места

Используя collide_rect_ratio(), можно сделать прямоугольник меньшего размера, уменьшив количество «пустого» места, которое в противном случае воспринималось бы как пересечение. Но это не всегда сработает. На изображении выше крылья космического корабля все равно оказываются за пределами прямоугольника. Это значит, что иногда астероид будет проходить сквозь крыло, не нанося урона. Но это не так уж и плохо! На той скорости, на которой работает игра, игрок не заметит, но почувствует, что был очень близок к столкновению. Вместо разочарования он почувствует, что неплохо справляется.

Ограничивающий круг

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

Тест на знание python

Что нужно вcтавить после "if", для вывода "x четное число"
Стрелялка с Pygame №5: улучшенные столкновения
Что выведет этот код?
Стрелялка с Pygame №5: улучшенные столкновения
Как нельзя назвать функцию?
Что выведет этот код?
Стрелялка с Pygame №5: улучшенные столкновения
Какой будет результат выполнения этого кода?
Стрелялка с Pygame №5: улучшенные столкновения

Установка радиуса спрайта

Опираясь на возможности, описанные выше, для функции столкновения игрока с астероидом будут использоваться круги. В Pygame это сделать проще простого с помощью атрибута self.radius для каждого из спрайтов.

Сначала игрок. Какого размера должен быть круг? Придется поэкспериментировать, чтобы определить нужный радиус. Вот как сделать это в спрайте игрока __init()__:

	self.rect = self.image.get_rect()
	self.radius = 25
	pygame.draw.circle(self.image, RED, self.rect.center, self.radius)

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

	self.rect = self.image.get_rect()
	self.radius = int(self.rect.width / 2)
	pygame.draw.circle(self.image, RED, self.rect.center, self.radius)

Здесь удалось пойти на небольшую хитрость. Если со временем будет решено использовать астероиды разных размеров, то указав радиус как 1/2 ширины изображения, можно будет не возвращаться к редактированию кода.

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

Красный круг поверх изображения

Здесь видно, что для игрока радиус выбран чересчур большой —круг даже больше чем размер корабля по оси y. Лучше установить радиус self.radius = 20.

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

	self.radius = int(self.rect.width * .85 / 2)

Изменение типа столкновения

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

    # проверка, не ударил ли моб игрока
    hits = pygame.sprite.spritecollide(player, mobs, False, pygame.sprite.collide_circle)
    if hits:
        running = False

Изменение типа столкновения

Если все работает так, как хотелось, красные круги можно убрать. Лучше их просто закомментировать, если нужно будет поэкспериментировать с ними в будущем.

Код урока — shmup-5.py

Итого

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

В следующий раз речь пойдет о том, как добавить спрайтам анимацию.