Pygame лучший способ замедлить частоту кадров определенной анимации без остановки программы?

#animation #pygame #wait #python-3.7

#Анимация #pygame #подождите #python-3.7

Вопрос:

У меня возникла проблема с анимацией разных кадров спрайта. Моя программа работает со скоростью 60 кадров в секунду, и у некоторых моих спрайтов недостаточно кадров, чтобы выглядеть плавно и дать достаточно времени, чтобы быть видимыми для пользователя. Например, мой спрайт взрыва содержит 6 кадров. В то время как игра работает со скоростью 60 кадров в секунду, это означает, что вся анимация видна только в течение 1/10 секунды.

Я уже пробовал следующие функции: Pygame.подождите и Pygame.задержка и функция «//» floor. Pygame.wait / delay оба полностью приостанавливают программу, что будет означать, что просто для того, чтобы увидеть взрыв, пользователю придется остановить все, что они делают, и я не хочу дублировать кадры, чтобы получить 30 или 60 кадров, это просто кажется неэффективным.

Массив взрыва:

 Expl = [pygame.image.load("EX1.png"),pygame.image.load("EX2.png"),pygame.image.load("EX3.png"),pygame.image.load("EX4.png"),pygame.image.load("EX5.png"),pygame.image.load("EX6.png")]
  

Где это происходит:

             if self.explosionc   1 >= 6:
                self.explosionc = 0
            elif self.vel > 0:
                win.blit(Expl[self.explosionc // 5],(self.x,self.y))
                self.explosionc  =1
  

Класс проигрывателя, если вам нужно так много информации

 class Player(pygame.sprite.Sprite):

    def __init__(self,x,y,width,height):
        self.x = x
        self.y = y
        self.width = width
        self.height = height
        self.vel = 5
        self.health = 10
        self.visible = True
        self.explosions = False
        self.explosionc = 0
        self.hitbox = (self.x   5,self.y   10,60,60)
        self.canshoot = True
        self.dead = False
    def draw(self,win):
        global bgvel
        if self.y > 600:
            win.blit(EFTNA,(self.x,self.y))
            for enemy in enemies:
                enemy.vel = 2
                #bgvel = 1
        elif self.y <= 600 and self.y > 400:
            win.blit(EFTAL,(self.x,self.y))
            for enemy in enemies:
                enemy.vel = 3
                #bgvel = 2
        elif self.y <= 400 and self.y > 350:
            win.blit(EFTAM,(self.x,self.y))
            for enemy in enemies:
                enemy.vel = 4
                #bgvel = 3
        elif self.y <= 450:
            win.blit(EFTAH,(self.x,self.y))
            for enemy in enemies:
                enemy.vel = 5
                #bgvel = 4
        self.hitbox = (self.x   5,self.y   10,60,60)
        #pygame.draw.rect(win,(255,0,0),self.hitbox,2)
        if self.y   self.height > HEIGHT:
            self.y -= self.vel
        if self.x < 0:
            self.x  = self.vel
        if self.x   self.width > WIDTH:
            self.x -= self.vel
        if self.explosions == True:
            print("I exploded")
            self.canshoot = False
            self.dead = True
            index = self.explosionc//10
            if self.vel > 0:
                win.blit(Expl[index % 6],(self.x,self.y))
                self.explosionc  = 1
                #if self.explosionc >= 6*10:
                #    self.explosionc = 0

            self.visible = False
    def hit(self):
        if self.health > 0:
                self.health -=1
        else:

                self.explosions = True
  

Я ожидаю, что программа будет ждать определенное количество времени между каждым кадром без остановки всей программы. Я слышал, что «настил» — хороший способ сделать это, но он просто не кажется эффективным, поскольку либо делает его полностью невидимым для человеческого глаза, либо не дает эффекта.

Комментарии:

1. вы могли бы использовать pygame.time.get_ticks , чтобы контролировать, когда менять отображаемый кадр. В начале анимации вы получаете текущее время с помощью pygame.time.get_ticks , а позже в каждом цикле вы получаете время (также используя pygame.time.get_ticks ) и вычисляете разницу. если это ожидаемое значение, то вы меняете кадр и сохраняете текущее время для сравнения со временем в следующем цикле.

2. вы также можете использовать pygame.time.set_timer для создания собственного события каждые несколько секунд / миллисекунд. И затем вы можете использовать это событие для изменения кадра в анимации.

3. Что делает self.dead и self.visible ? Не следует ли изменять состояния после взрыва, а не в начале?

Ответ №1:

Вы можете использовать pygame.time.get_ticks() для использования времени, чтобы контролировать, как долго вы отображаете кадр

 start_time = pygame.time.get_ticks()

while running:

    current_time = pygame.time.get_ticks()
    if current_time - start_time >= expected_delay_in_ms:
        change_frame_in_animation()
        start_time = current_time
  

Я создаю start_time раньше while running , но вы должны создать его при запуске анимации.


Вы также можете использовать pygame.time.set_timer() для создания собственного события каждые несколько миллисекунд. И затем вы можете использовать это событие для изменения кадра в анимации

 pygame.time.set_timer(pygame.USEREVENT 1, expected_delay_in_ms)

while running:

    for event in pygame.event.get():
        if event.type = pygame.USEREVENT 1:
            change_frame_in_animation()
  

Я использовал pygame.time.set_timer() раньше while running , но вы должны использовать его при запуске анимации.


РЕДАКТИРОВАТЬ: Используя ваш новый код, о котором идет речь, это может быть (но я не могу его протестировать)

В self.__init__ я определяю

 self.frame_delay = 200 # 200 milliseconds
  

Когда игрок умирает, я установил self.start_delay = 0 — он должен отображать первый кадр без задержки.

     def hit(self):
        if self.health > 0:
           self.health -= 1
        else:
           self.explosions = True
           self.start_delay = 0 # or pygame.time.get_ticks()
  

В draw я использую self.start_delay для сравнения с current_time и self.frame_delay

         if self.explosions == True:

            current_time = pygame.time.get_ticks()
            if current_time - self.start_delay >= self.frame_delay:
                self.start_delay = current_time

                print("I exploded")
                self.canshoot = False
                self.dead = True

                self.explosionc = (self.explosionc   1) % 6
                if self.vel > 0: # ???
                    win.blit(Expl[self.explosionc],(self.x,self.y))

            self.visible = False
  

Я вычисляю следующий кадр с

 self.explosionc = (self.explosionc   1) % 6
  

Я не знаю только, почему вы используете if self.vel > 0:

Полный код

 class Player(pygame.sprite.Sprite):

    def __init__(self,x,y,width,height):
        self.x = x
        self.y = y
        self.width = width
        self.height = height
        self.vel = 5
        self.health = 10
        self.visible = True
        self.explosions = False
        self.explosionc = 0
        self.hitbox = (self.x   5,self.y   10,60,60)
        self.canshoot = True
        self.dead = False

        self.frame_delay = 200 # 200 milliseconds


    def draw(self,win):
        global bgvel

        if self.y > 600:
            win.blit(EFTNA,(self.x,self.y))
            for enemy in enemies:
                enemy.vel = 2
                #bgvel = 1
        elif self.y <= 600 and self.y > 400:
            win.blit(EFTAL,(self.x,self.y))
            for enemy in enemies:
                enemy.vel = 3
                #bgvel = 2
        elif self.y <= 400 and self.y > 350:
            win.blit(EFTAM,(self.x,self.y))
            for enemy in enemies:
                enemy.vel = 4
                #bgvel = 3
        elif self.y <= 450:
            win.blit(EFTAH,(self.x,self.y))
            for enemy in enemies:
                enemy.vel = 5
                #bgvel = 4
        self.hitbox = (self.x   5,self.y   10,60,60)
        #pygame.draw.rect(win,(255,0,0),self.hitbox,2)

        if self.y   self.height > HEIGHT:
            self.y -= self.vel
        if self.x < 0:
            self.x  = self.vel
        if self.x   self.width > WIDTH:
            self.x -= self.vel

        if self.explosions == True:

            current_time = pygame.time.get_ticks()
            if current_time - self.start_delay >= self.frame_delay:
                self.start_delay = current_time

                print("I exploded")
                self.canshoot = False
                self.dead = True

                self.explosionc = (self.explosionc   1) % 6

                if self.vel > 0: # ???
                    win.blit(Expl[self.explosionc],(self.x,self.y))

            self.visible = False

    def hit(self):
        if self.health > 0:
           self.health -= 1
        else:
           self.explosions = True
           self.start_delay = 0 # or pygame.time.get_ticks()
  

Комментарии:

1. Что касается решения 1, я понимаю, что вы имеете в виду, но я не уверен, как бы я реализовал это «change_frame_in_animation()»

2. Вы должны получить следующий кадр и запустить его — так что, вероятно, self.explosionc = (self.explosionc 1) % 6 и win.blit(Expl[self.explosionc],(self.x,self.y))