Смена фаз босса pygame
Может кто знающий подскажет или поможет: Босс имеет 3 фазы, 2 из них реализованы так, как надо. В конце второй фазы босс улетает за границу экрана. Далее реализовать мою задумку не получается уже целый день. Попросту не знаю, как прописывать код дальше. Задумка: после того, как босс улетел за границу экрана (в конце второй фазы), через 7 секунд он должен плавно появиться из-за правой нижней части экрана (меняется только X, а Y строго должен быть равен 45% высоты экрана, ибо хитбокс создаётся через topleft). При этом он должен сменить свою текстуру и текстуру своего проджектайла, а также он будет иметь совершенно другую логику стрельбы в игрока. Проблема в том, что делаю я это на двух классах: Boss и условно Boss2, поэтому Boss2 я сделал дочерним элементом Boss, чтобы принять у него оставшееся и максимальное количество хп. С наследованием, вроде как, получилось. Даже босса вывел (не в цикле, а просто ради теста) – он отображается, хоть и без функционала, но хп с прошлых фаз сохраняет. Но вот вопрос в том, как сделать переход между 2 и 3 фазой, ибо вручную вызывать босса на экран – не есть решение.
def end_phase_2(self):
self.shooting_enabled = False # Останавливаем стрельбу
self.rect.y -= 10 # Скорость полета за экран
if self.rect.bottom < 0:
self.rect.y = -self.rect.height # Босс уходит за верхнюю границу экрана
if isinstance(self, Boss2): # Надобность в этом коде я вообще не понял, нужен он или нет, но его яро советует чатгпт
self.start_phase_3()
Как я наследую класс Boss и его hp и max_hp:
def __init__(self, x, y, images, projectile_image, hp, max_hp):
super().__init__(x, y, images, projectile_image)
enemy_images = load_enemy_images()
self.image = pygame.transform.scale(enemy_images["boss_img"], (700, 500))
self.rect = self.image.get_rect(topleft=self.rect.topleft)
self.hp = hp # Устанавливаем здоровье из предыдущей фазы
self.max_hp = max_hp # Устанавливаем максимальное здоровье из предыдущей фазы
self.phase = 3
projectile_images = load_projectile_images()
self.projectile_image = projectile_images
self.appear_delay = 7000 # Задержка в 7 секунд
self.appear_start_time = None # Время начала появления
self.appear_x = pygame.display.get_surface().get_width() # Начальная координата X для появления
self.final_x = x # Конечная координата X
self.appear_speed = 2 # Скорость перемещения босса по X
Как я пытался вывести босса так, как я хочу:
def start_phase_3(self):
self.appear_start_time = pygame.time.get_ticks()
self.appear_x = pygame.display.get_surface().get_width() # Начинаем появляться с правой стороны экрана
self.rect.topleft = (self.appear_x, pygame.display.get_surface().get_height() * 0.45) # Устанавливаем начальное положение босса
self.phase = 3
self.shooting_enabled = True # Включаем стрельбу для новой фазы
# boss = PanzerBoss(screen_width * 0.65, screen_height * 0.45, enemy_images, projectile_images["panzer_projectile"], boss.hp, boss.max_hp) - Как вызывал босса в игровом цикле вручную
Ответы (1 шт):
if isinstance(self, Boss2): # Надобность в этом коде я вообще не понял, нужен он или нет, но его яро советует чатгпт
self.start_phase_3()
вместо этого вам следует переопределить метод end_phase_2 в дочернем классе. В нем вызвать родительский end_phase_2, а затем свою логику. Сейчас ваша структура кода выглядит так
class Boss:
hp: float
stage: str
max_hp: float
def fire(...):
...
def end_phase_1(self):
...
def end_phase_2(self):
if isinstance(self, Boss2):
...
...
class Boss2(Boss):
# end_phase_1()
# end_phase_2()
def end_phase_3(self):
...
позвольте ввести еще один класс, который будет отвечать за наблюдение за состоянием босса и выделить классы отвечающие за механику.
class Stage:
"""Описание интерфейса для всех поведений!"""
name: str
def __init__(self, boss):
self._boss = boss
def fire(self, ...):
...
def start(...):
...
def end(...):
...
class Stage1(Stage):
"""Первая реализация интерфейса поведений босса."""
name = '1'
def fire(self, ...):
...
def start(...):
...
def end(...):
boss = self._boss
boss.shooting_enabled = False # Останавливаем стрельбу
boss.rect.y -= 10 # Скорость полета за экран
if boss.rect.bottom < 0:
boss.rect.y = -boss.rect.height # Босс уходит за верхнюю границу экрана
class Stage2(Stage1):
"""Вторая реализация интерфейса поведений босса."""
name = '2'
def start(...):
...
def end(...):
self.boss.visible = False
class Stage3(Stage):
"""Третья реализация интерфейса поведений босса."""
name = '3'
def fire(self, ...):
"""своя реализация стрельбы"""
...
def start(...):
from threading import Thread
Thread(target=self.thread).start()
def thread(...):
sleep(7) # так как мы в потоке, то игра продолжить быть интерактивной
boss = self._boss
boss.appear_start_time = pygame.time.get_ticks()
boss.appear_x = pygame.display.get_surface().get_width() # Начинаем появляться с правой стороны экрана
boss.rect.topleft = (boss.appear_x, pygame.display.get_surface().get_height() * 0.45) # Устанавливаем начальное положение босса
boss.shooting_enabled = True # Включаем стрельбу для новой фазы
boss.visible = True # отрисовываем босса
def end(...):
...
class Boss:
hp: float
stage: Stage
_stages: (Stage1, Stage2, Stage3)
max_hp: float
def __init__(self, ...):
...
def begin(...):
self.stage = next(self._stages)
self.stage.start()
return self
def end(...):
self.stage.end()
return self
def __iter__(self):
self._stages = iter(x(self) for x in self._stages)
return self.begin()
def __next__(self):
self.end()
return self.begin()
def fire(self, ...):
self.stage.fire(...)
Класс Boss меняет состояния по протоколу итератора.
boss = Boss()
_ = iter(boss)
print(boss.stage.name)
_ = next(boss)
print(boss.stage.name)
у третьей стратегии метод start запускает поток, который засыпает на 7 секунд. дальше можете отрисовывать босса.
Вместо потока, вам также доступен вариант с таймером pygame
...
class Stage3(Stage):
"""Третья реализация интерфейса поведений босса."""
name = '3'
def __init__(self, boss):
boss.hp = boss.max_hp = boss.max_hp * 2
def fire(self, ...):
"""своя реализация стрельбы"""
...
def start(...):
boss = self._boss
boss.appear_start_time = pygame.time.get_ticks()
boss.appear_x = pygame.display.get_surface().get_width() # Начинаем появляться с правой стороны экрана
boss.rect.topleft = (boss.appear_x, pygame.display.get_surface().get_height() * 0.45) # Устанавливаем начальное положение босса
boss.shooting_enabled = True # Включаем стрельбу для новой фазы
boss.visible = True # отрисовываем босса
def end(...):
...
...
import pygame
# Инициализация Pygame
pygame.init()
# Настройка экрана и времени
screen = pygame.display.set_mode((800, 600))
clock = pygame.time.Clock()
boss_visible = False
boss_timer = 0
boss_delay = 3000 # 3 секунды
# Основной игровой цикл
running = True
boss = iter(Boss())
boss_timer = 0
boss_delay = 7_000
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
# Логика игры
if boss_timer > 0:
boss_timer = pygame.time.get_ticks()
if boss_timer >= boss_delay:
boss.begin()
boss_timer = 0
if boss.hp == 0:
try:
boss.end()
if boss.stage.name == '2':
boss_timer = pygame.time.get_ticks()
_boss_delay = boss_timer + boss_delay
else:
boss.begin()
except StopIteration:
# босс повержен
raise
if not boss_visible:
# Начинаем отсчет времени
boss_timer += clock.get_time()
if boss_timer >= boss_delay:
boss_visible = True
boss_timer = 0
# Отрисовка
screen.fill((0, 0, 0))
if boss_visible:
# Отрисовать босса
pygame.draw.rect(screen, (255, 0, 0), (400, 300, 50, 50)) # Пример босса
pygame.display.flip()
clock.tick(60)
pygame.quit()
как то так