Драки с монстрами

Итак, для начала создадим два новых демона для атак в level.py:

self.attack_sprites = pygame.sprite.Group() #атакующий спрайт
self.attackable_sprites = pygame.sprite.Group() #атакуемый спрайт

Думаю, по названиям понятно, что демоны нужны для обозначения процесса атаки. Дополним метод вызова врагов помимо видимых спрайтов, атакуемыми спрайтами (self.attack_sprites):

Enemy(monster_name, (x, y), [self.visible_sprites, self.attackable_sprites], self.obstacle_sprites)

Также, дополним метод create_attack атакующим спрайтом (self.attack_sprites) :

def create_attack(self):
    self.current_attack = Weapon(self.player, [self.visible_sprites, self.attack_sprites])

Далее пропишем новый метод с логикой атаки игрока:

def player_attack_logic(self):
    if self.attack_sprites:
        for attack_sprites in self.attack_sprites:
            collision_sprites = pygame.sprite.spritecollide(attack_sprites, self.attackable_sprites, False)
            if collision_sprites:
                for target_sprite in collision_sprites:
                    target_sprite.kill()

Самая интересная строка тут, это строка с методом пайгейма pygame.sprite.spritecollide. Данный метод позволяет удалять спрайт из группы. Первый аргумент функции — спрайты для атаки, второй — группа спрайтов, из которой мы будем удалять спрайт, третий — DoKill. Если у DoKill установлено значение True, все спрайты, которые сталкиваются, будут удалены из группы. Далее, в run-методе пропишем наш метод. Исход:

Немного улучшим метод player_attack_logic:

Мы стали сопоставлять наши спрайты по типам. В моём проекте типов только два (enemy и weapon). Я прописал в файле weapon.py в демоне строку для присваивания ему нового типа:

Далее нам не нужно удалять врага при ударе. Нам нужно прописывать ему урон от нашего оружия. Собственно, теперь нужно в файле enemy.py прописать новый метод — get_damage:

Тут мы прописываем новый метод (get_full_weapon_damage), который должен высчитывать сумму урона от оружия и от силы самого Линка (прямо как в Dark Souls). Пропишем же данный метод в player.py:

Находим и складываем уроны из наших списков. Возможно, тут встанет вопрос: "Зачем так сложно, если у нас только один меч и всё?". Я хотел бы сделать проект так, чтобы вы могли самостоятельно с ним "поиграться". Собственно и цель книги не номинация "Игра года" в The Game Awards, а лишь попытка продемонстрировать работоспособность языка Python как неплохого движка. Ну да вернёмся к коду. Помимо урона, я решил сразу прописать кулдаун оружия и заменил строку:

На строку:

Напишем новый метод в enemy.py на проверку смерти монстра:

Тут я даже не знаю что ещё подсветить в коде :) Не забудьте добавить данный метод в update-метод. Теперь один удар приводит к смерти врага. Таким образом, PyGame считает, что пока оружие соприкасается с врагом (вызывается метод коллизий), удары наносятся один за другим. Как итог — Линк танк, который уничтожает всё на своём пути. Исправим это. Для начала, создадим новых демонов enemy.py:

Тут достаточно прозрачные демоны. Важный момент — флаг уязвимости. Если он поднят — враг может получать урон. Теперь встроим их в get_damage:

Далее необходимо дополнить код cooldown-метода:

Тут, как я говорил ранее, сразу добавляем вариацию флага. У нас было место, где флаг опускается и теперь в cooldown-методе он поднимается по истечению указанного времени неуязвимости. Теперь все враги убиваются весьма приятно. Теперь, нужно добавить отбивание врага на дистанцию, которая указана у каждого врага. Создадим ещё метод:

Осталось вычислить положение в методе get_damage:

Теперь добавим мерцание во время удара, чтобы понять что удар был сделан. В PyGame все сигнатуры (синусоидные функции) лежат в диапазоне от -255 до 255, а позиции удобно брать из синусоид, так как интерпретация в Python будет работать на основе степеней (как и любой калькулятор), а затем будет процесс получения точки на синусоиде. Этот процесс я описывал дольше, чем будет писаться метод отображения пульсации:

Данный метод написан в entity.py. Не забудьте импортировать sin-метод из библиотеки math. Теперь вызовем данный метод при ударе по врагу в методе animate:

Сейчас мы бьём врагов абсолютно верно и можем отследить когда монстры атакуют нас (в терминал приходит сообщение "attack"). Осталось сделать метод, который делает урон Линку. Пропишем его в level.py:

Дополним в create_map в строку вызова монстров нанесение урона Линку:

Добавим в Enemy-класс параметр (damage_player) и пропишем нового демона — self.damage_player = damage_player. Теперь вместо простого вывода сообщения "attack" выполним вызов нашего метода:

Добавим параметры таймеров в демонов нашего player-класса:

Они аналогичны демонам в enemy.py. Далее традиционно пропишем смену флага в наш кулдаун-метод:

Теперь пропишем наше мерцание. Тут всё также, как и ранее. Прописывать будем в animate-методе:

Теперь нам нужно восстанавливать энергию (повторим механику из Dark Souls). Для этого создадим метод energy_recover:

Далее пропишем, что при ударе у нас теряется 10 очков стамины, а если её не хватает — атака не проходит:

Последнее, что я хотел бы сделать — добавить экспу за убийство врага в level.py:

Добавим этот же метод для create_map в Enemy. Далее повторим всё, что делали ранее с damage_player.

Файлы данного шага тутarrow-up-right. Приступим к последнему шагу — музыке.

Last updated