🎮
The Legend of PyZelda: Breath of the Snakes
  • Обложка
  • Введение
  • Подготовка
  • Пишем уровень
  • Создаём игрока
  • Создание камеры
  • Графика
  • Анимация Линка
  • Оружие героя
  • Интерфейс
  • Монстры
  • Драки с монстрами
  • Музыка
  • Деплой
  • Итоги
Powered by GitBook
On this page

Создание камеры

Предлагаю создать камеру с учётом вектора направления движения персонажа. Мы будем двигать камеру вместе с персонажем. Переходим к файлу уровня и создаём новый класс.

class YSortCameraGroup(pygame.sprite.Group):
    def __init__(self):
        super().__init__()

Всё, что я сделал — создал новый пустой класс с наследованием всего и вся. Далее применяем его вместо pygame.sprite.Group() в visible_sprites, дабы не тавтологироваться, а ссылаться на класс с более тонкими настройками. Теперь к тонкостям:

class YSortCameraGroup(pygame.sprite.Group):
    def __init__(self):
        super().__init__()
        self.display_surface = pygame.display.get_surface()
​
    def custom_draw(self):
        for sprite in self.sprites():
            self.display_surface.blit(sprite.image, sprite.rect) #прорисуем новую поверхность и отрисуем её

Здесь добавлены те же отображения новых поверхностей в главного демона с наследованием и создан метод ручной отрисовки (custom_draw).

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

В коде самый интересный пункт в display_surface.blit. Surface создаёт новый слой объектов, а blit отрисовывает их. Далее мы передаём картинки и фигуры. Скоро и это перепишем, так как нам нужны векторы. Этим и займёмся. Вектор знаком вам по ходьбе Линка. Далее снова улучшим класс:

class YSortCameraGroup(pygame.sprite.Group):
    def __init__(self):
        super().__init__()
        self.display_surface = pygame.display.get_surface()
        self.offset = pygame.math.Vector2()
​
    def custom_draw(self):
        for sprite in self.sprites():
            offset_pos = sprite.rect.topleft + self.offset #определяем позицию путём сравнения верхнего левого угла с вектором направления
            self.display_surface.blit(sprite.image, offset_pos) #прорисуем новую поверхность и отрисуем её

Из нового только одна конструкция в методе. Она направлена на определение верхнего левого угла, отрисовку и последующую сумму с вектором направления. Далее в run-методе, не забудьте поменять отрисовку с базовой на нашу кастомную. Примерно так:

self.visible_sprites.custom_draw()

Если вы сделали всё верно, вставив в вектор 2 значения (x и y), вы увидите перемещение карты. Я поставил -150 и -150 и получил это:

Осталось перемещаться, зацепившись за героя. Его мы оставим по центру экрана (да здравствует эпохе Dendy). Наш код по доработке:

class YSortCameraGroup(pygame.sprite.Group):
    def __init__(self):
        super().__init__()
        self.display_surface = pygame.display.get_surface()
        self.half_width = self.display_surface.get_size()[0] // 2 #середина отрисованного экрана по ширине
        self.half_heigth = self.display_surface.get_size()[1] // 2 #середина отрисованного экрана по высоте
        self.offset = pygame.math.Vector2()
​
    def custom_draw(self, player):
        self.offset.x = player.rect.centerx - self.half_width #координата x Линка
        self.offset.y = player.rect.centery - self.half_heigth #координата y Линка
        for sprite in self.sprites():
            offset_pos = sprite.rect.topleft - self.offset #определяем позицию путём сравнения верхнего левого угла с вектором направления
            self.display_surface.blit(sprite.image, offset_pos) #прорисуем новую поверхность и отрисуем её

В базовых демонов добавили размеры экрана и центр экрана (нацело делим пополам). В нашу кастомную рисовалку передадим координаты игрока и по x, y офсетам высчитаем их. Чтобы избавиться от "пьяной камеры", я вычитаю вектор направления, а не прибавляю его. Не забудьте run-методе передать self.player. Результат:

Теперь нужно разобраться с хитбоксами. Сейчас у нас графика из Денди, где каждый объект стоит по конкретным клеткам (тайлам). Нужно это исправить. Для начала перейдем к настройкам тайла и допишем один демон-элемент:

self.hitbox = self.rect.inflate(0, -10)

Таким образом, мы уменьшили хитбокс на 10 пикселей. Так как отрисовка идёт из центра — мы уменьшаем хитбокс сверху и снизу на 5 пикселей. В демонах игрока пропишем тот же код, но уменьшим хитбокс на 26 пикселей. Далее, наша задача двигаться и проверять коллизии через хитбоксы. Для этого объекты с rect.x заменим на hitbox.x. Тогда move-функция выглядит так:

def move(self, speed):
    if self.direction.magnitude() != 0:
        self.direction = self.direction.normalize()
    self.hitbox.x += self.direction.x * speed
    self.collision('horizontal')
    self.hitbox.y += self.direction.y * speed
    self.collision('vertical')
    self.rect.center = self.hitbox.center

Починим коллизии:

def collision(self, direction):
    if direction == 'horizontal':
        for sprite in self.obstacle_sprites:
            if sprite.hitbox.colliderect(self.hitbox):
                if self.direction.x > 0: #двигаем вправо
                    self.hitbox.right = sprite.hitbox.left
                if self.direction.x < 0: #двигаем влево
                    self.hitbox.left = sprite.hitbox.right
​
    if direction == 'vertical':
        for sprite in self.obstacle_sprites:
            if sprite.hitbox.colliderect(self.hitbox):
                if self.direction.y > 0: #двигаем вниз
                    self.hitbox.bottom = sprite.hitbox.top
                if self.direction.y < 0: #двигаем вверх
                    self.hitbox.top = sprite.hitbox.bottom

Результат:

Линку отрывает шапку. Это происходит из-за того, что изображения хаотично находятся на поверхности. Какие-то выше, какие-то ниже. Исправим это через класс YSort (не просто же так мы мы назвали класс YSortCameraGroup). Поправим только одну строку:

for sprite in self.sprites():

На строку:

for sprite in sorted(self.sprites(), key = lambda sprite: sprite.rect.centery):

Итог:

PreviousСоздаём игрокаNextГрафика

Last updated 1 year ago

Мы сделали сортировку по y-координате. Про лямбду можно отдельно , но если сказать грубо — лямбда-функция — функция, которая работает с анонимными функциями.

.

почитать
Ссылка на этап проекта