Анимированный спрайт из нескольких изображений

Я искал хороший учебник о создании простой анимации спрайта из нескольких изображений в Python с помощью Pygame. Я все еще не нашел то, что искал.

мой вопрос прост: как сделать анимированный спрайт из нескольких изображений (например: сделать несколько изображений взрыва с размерами 20x20px как один, но анимированный)

хорошие идеи?

3 ответов


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

редактировать:

вот краткий пример, который я составил:

import pygame
import sys

def load_image(name):
    image = pygame.image.load(name)
    return image

class TestSprite(pygame.sprite.Sprite):
    def __init__(self):
        super(TestSprite, self).__init__()
        self.images = []
        self.images.append(load_image('image1.png'))
        self.images.append(load_image('image2.png'))
        # assuming both images are 64x64 pixels

        self.index = 0
        self.image = self.images[self.index]
        self.rect = pygame.Rect(5, 5, 64, 64)

    def update(self):
        '''This method iterates through the elements inside self.images and 
        displays the next one each tick. For a slower animation, you may want to 
        consider using a timer of some sort so it updates slower.'''
        self.index += 1
        if self.index >= len(self.images):
            self.index = 0
        self.image = self.images[self.index]

def main():
    pygame.init()
    screen = pygame.display.set_mode((250, 250))

    my_sprite = TestSprite()
    my_group = pygame.sprite.Group(my_sprite)

    while True:
        event = pygame.event.poll()
        if event.type == pygame.QUIT:
            pygame.quit()
            sys.exit(0)

        # Calling the 'my_group.update' function calls the 'update' function of all 
        # its member sprites. Calling the 'my_group.draw' function uses the 'image'
        # and 'rect' attributes of its member sprites to draw the sprite.
        my_group.update()
        my_group.draw(screen)
        pygame.display.flip()

if __name__ == '__main__':
    main()

предполагается, что у вас есть два изображения, называемые image1.png и image2.png внутри той же папке кода.


существует два типа анимации: зависит от кадра и зависит от времени. Оба работают одинаково.


перед главным циклом

  1. Загрузите все изображения в список.
  2. создать три переменных:
    1. index, который отслеживает текущий индекс списка изображений.
    2. current_time или current_frame который отслеживает текущее время или текущий кадр с момента последнего индекс поменялся.
    3. animation_time или animation_frames это определяет, сколько секунд или кадров должно пройти перед переключением изображения.

во время основного цикла

  1. инкремент current_time на количество секунд, прошедших с момента последнего увеличения или увеличения current_frame к 1.
  2. проверить, если current_time >= animation_time или current_frame >= animation_frame. Если true, продолжайте с 3-5.
  3. сбросить current_time = 0 или current_frame = 0.
  4. увеличить индекс, если он не будет равен или больше, чем количество изображений. В этом случае сбросьте index = 0.
  5. измените изображения спрайта.

полный рабочий пример

import os
import pygame
pygame.init()

SIZE = WIDTH, HEIGHT = 720, 480
BACKGROUND_COLOR = pygame.Color('black')
FPS = 60

screen = pygame.display.set_mode(SIZE)
clock = pygame.time.Clock()


def load_images(path):
    """
    Loads all images in directory. The directory must only contain images.

    Args:
        path: The relative or absolute path to the directory to load images from.

    Returns:
        List of images.
    """
    images = []
    for file_name in os.listdir(path):
        image = pygame.image.load(path + os.sep + file_name).convert()
        images.append(image)
    return images


class AnimatedSprite(pygame.sprite.Sprite):

    def __init__(self, position, images):
        """
        Animated sprite object.

        Args:
            position: x, y coordinate on the screen to place the AnimatedSprite.
            images: Images to use in the animation.
        """
        super(AnimatedSprite, self).__init__()

        size = (32, 32)  # This should match the size of the images.

        self.rect = pygame.Rect(position, size)
        self.images = images
        self.images_right = images
        self.images_left = [pygame.transform.flip(image, True, False) for image in images]  # Flipping every image.
        self.index = 0
        self.image = images[self.index]  # 'image' is the current image of the animation.

        self.velocity = pygame.math.Vector2(0, 0)

        self.animation_time = 0.1
        self.current_time = 0

        self.animation_frames = 6
        self.current_frame = 0

    def update_time_dependent(self, dt):
        """
        Updates the image of Sprite approximately every 0.1 second.

        Args:
            dt: Time elapsed between each frame.
        """
        if self.velocity.x > 0:  # Use the right images if sprite is moving right.
            self.images = self.images_right
        elif self.velocity.x < 0:
            self.images = self.images_left

        self.current_time += dt
        if self.current_time >= self.animation_time:
            self.current_time = 0
            self.index = (self.index + 1) % len(self.images)
            self.image = self.images[self.index]

        self.rect.move_ip(*self.velocity)

    def update_frame_dependent(self):
        """
        Updates the image of Sprite every 6 frame (approximately every 0.1 second if frame rate is 60).
        """
        if self.velocity.x > 0:  # Use the right images if sprite is moving right.
            self.images = self.images_right
        elif self.velocity.x < 0:
            self.images = self.images_left

        self.current_frame += 1
        if self.current_frame >= self.animation_frames:
            self.current_frame = 0
            self.index = (self.index + 1) % len(self.images)
            self.image = self.images[self.index]

        self.rect.move_ip(*self.velocity)

    def update(self, dt):
        """This is the method that's being called when 'all_sprites.update(dt)' is called."""
        # Switch between the two update methods by commenting/uncommenting.
        self.update_time_dependent(dt)
        # self.update_frame_dependent()


def main():
    images = load_images(path='temp')  # Make sure to provide the relative or full path to the images directory.
    player = AnimatedSprite(position=(100, 100), images=images)
    all_sprites = pygame.sprite.Group(player)  # Creates a sprite group and adds 'player' to it.

    running = True
    while running:

        dt = clock.tick(FPS) / 1000  # Amount of seconds between each loop.

        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                running = False
            elif event.type == pygame.KEYDOWN:
                if event.key == pygame.K_RIGHT:
                    player.velocity.x = 4
                elif event.key == pygame.K_LEFT:
                    player.velocity.x = -4
                elif event.key == pygame.K_DOWN:
                    player.velocity.y = 4
                elif event.key == pygame.K_UP:
                    player.velocity.y = -4
            elif event.type == pygame.KEYUP:
                if event.key == pygame.K_RIGHT or event.key == pygame.K_LEFT:
                    player.velocity.x = 0
                elif event.key == pygame.K_DOWN or event.key == pygame.K_UP:
                    player.velocity.y = 0

        all_sprites.update(dt)  # Calls the 'update' method on all sprites in the list (currently just the player).

        screen.fill(BACKGROUND_COLOR)
        all_sprites.draw(screen)
        pygame.display.update()


if __name__ == '__main__':
    main()

когда выбирал

зависит от времени анимация позволяет воспроизводить анимацию с той же скоростью, независимо от того, как медленно / быстро частота кадров или медленно / быстро ваш компьютер. Это позволяет вашей программе свободно изменять частоту кадров, не влияя на анимацию, и она также будет согласована, даже если компьютер не может идти в ногу с частотой кадров. Если программа отстает, анимация догонит состояние, как будто никакого отставания не произошло.

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

  1. Кадр 1; 0.00 секунд; изображение 1
  2. Кадр 2; 0,05 секунды; изображение 1
  3. Кадр 3; 0,10 секунды; изображение 2
  4. Кадр 4; 0,15 секунды; изображение 1
  5. Кадр 5; 0,20 секунды; изображение 1
  6. кадр 6; 0,25 секунды; изображение 2

и так далее...

зависит от кадра можно посмотреть более плавный, если ваш компьютер может обрабатывать частоту кадров последовательно. Если отставание произойдет, оно приостановится в текущем состоянии и перезапустится, когда отставание остановится, что делает отставание более заметным. Эта альтернатива немного проще реализовать, так как вам просто нужно увеличить current_frame С 1 на каждом вызове, вместо того, чтобы иметь дело с Дельта-временем (dt) и передавая его каждому объекту.

спрайты

enter image description here enter image description here enter image description here enter image description here enter image description here enter image description here

результат

enter image description here


У вас должны быть все ваши анимации спрайтов на одном большом "холсте", поэтому для 3 кадров спрайта взрыва 20x20 у вас будет изображение 60x20. Теперь вы можете получить правильные кадры, загрузив область изображения.

внутри вашего класса sprite, скорее всего, в методе обновления у вас должно быть что-то вроде этого (жестко закодированный для простоты, я предпочитаю иметь отдельный класс, который будет отвечать за выбор правильного кадра анимации). self.f = 0 on __init__.

def update(self):
    images = [[0, 0], [20, 0], [40, 0]]
    self.f += 1 if self.f < len(images) else 0
    self.image = your_function_to_get_image_by_coordinates(images[i])