从几张图片动画精灵(Animated sprite from few images)

2019-07-17 14:19发布

我一直在寻找关于使用pygame的Python中的几张图片进行简单的精灵动画一些很好的教程。 我还没有找到我要找的。

我的问题很简单:如何从几张图片的动画精灵(对于一个例子:使爆炸的几张图片尺寸为20x20px是作为一个动画却)

任何好的想法?

Answer 1:

你可以尝试修改你的精灵,使其换出的图像不同的一个内部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.pngimage2.png同一文件夹中的代码是在里面。



Answer 2:

有两种类型的动画: 帧依赖性时间依赖性的 。 以类似的方式都工作。


主循环之前

  1. 加载所有图片到一个列表。
  2. 创建三个变量:
    1. index ,该跟踪的图像列表的当前索引。
    2. current_timecurrent_frame ,保持跟踪在当前时间或当前帧自上次索引切换。
    3. animation_timeanimation_frames定义多少秒或帧应该在切换图像之前通过。

在主循环

  1. 增量current_time由自从我们上次增加它已经经过的秒数,或增量current_frame 1。
  2. 检查current_time >= animation_timecurrent_frame >= animation_frame 。 如果真的继续3-5。
  3. 重置current_time = 0current_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 ),并将它传递给每一个对象。

精灵

结果



Answer 3:

你应该有一个大的“画布”所有你的精灵动画,所以3个20x20的爆炸精灵帧,你将有60x20图像。 现在,您可以通过加载图像的某个区域得到正确的帧。

里面你的精灵类,最有可能在更新方法,你应该有这样的事情(硬编码为简单起见,我喜欢有单独的类来负责挑选合适的动画帧)。 self.f = 0__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])


文章来源: Animated sprite from few images