pygame如何制作两帧动画作品

时间:2020-06-25 11:09:34

标签: python pygame

我正在pygame上做游戏,我想对如何制作一个简单的两帧动画进行一些建议,并在按下某个键时退出。这是代码,并附有完整文件。

                screen.blit(win_screen1, [0, 0])
                pygame.display.update()
                clock.tick(13)
                screen.blit(win_screen2, [0, 0])
                clock.tick(13)
                pygame.display.update()
                for event in pygame.event.get():
                    if event.type == pygame.KEYDOWN:
                        playing = True
                        win = False
                        reset()

我本以为这会使动画以13 FPS的速度运行,并在按下某个键时停止播放,但是它的播放过程确实不一致且缓慢,并且按任何键都无济于事。我在另一篇文章中看到有人说他们使用了pygame.time.Clock(),但我不确定它的工作原理,而且许多方法仅限制帧速率,而不设置帧速率。

1 个答案:

答案 0 :(得分:1)

将动画项目包装到一个对象中,这样它可以保持自己的内部时序,并绘制正确的位图。我会为此使用PyGame精灵,但是您也可以自己制作。

该想法是使用内部PyGame时钟记录首次显示当前帧的时间。然后,在某种类型的每帧更新功能中,确定是否经过了足够的毫秒数以保证移动到下一帧。您希望获得13 FPS,因此每77毫秒为1000/13->。

这给了我们一个粗略的功能:

drawAnimation()
    What is the time now?
    Has 77 milliseconds elapsed since the last update?
       Advance to the next image in the list
    Draw the image

在循环播放帧和保持时间戳方面有一些内务处理,但这并不比上面的伪代码复杂得多。诀窍是将所有包含在可以传递的单个数据对象中。这样可以使其他帧定时(例如窗口FPS)保持很好的分离。就像我说的那样,PyGame Sprite是保存所有这些数据的理想选择,但是即使是简单的python列表也可以正常使用。

example_image

动画来自:https://rvros.itch.io/animated-pixel-hero(在许可许可下使用)。缩放200%

这是一个基于Sprite的实现。如有疑问,请添加评论。

class MultiFrameSprite(pygame.sprite.Sprite):
    def __init__( self, fps, list_of_frames ):
        pygame.sprite.Sprite.__init__( self )

        # Load all the specified animation frames
        self.frames = []
        for filename in list_of_frames:
            self.frames.append( pygame.image.load( filename ).convert_alpha() )

        # Start the animation at the first frame
        self.image = self.frames[0]
        self.rect  = self.image.get_rect()

        # Frame handling - index frame, and time used
        self.millisec_rate = 1000 // fps   # inter-frame delay in milliseconds
        self.current_frame = 0
        self.last_frame_at = 0

        # we need to be somewhere on-screen
        self.rect.center = ( WINDOW_WIDTH//2, WINDOW_HEIGHT//2 )

    def update(self):
        # Compare the time of the last frame to time-now
        # and determine if it's time to show the next frame
        time_now = pygame.time.get_ticks()
        if ( time_now > self.last_frame_at + self.millisec_rate ):
            # new frame needed!
            self.last_frame_at = time_now
            # Advance to the next frame
            self.current_frame += 1
            if ( self.current_frame == len( self.frames ) ):
                self.current_frame = 0                           # wrap frame loop index
            # Set the new image
            self.image = self.frames[ self.current_frame ]
            self.rect  = self.image.get_rect()
            # TODO: handle any x/y changes needed by differing animation sizes

请注意,我们绝不会只制作2帧,3帧或10帧的动画。这只是 N 个图像的列表。当我们到达列表的末尾(无论时间长短)时,它就可以回到开始。这样,可以将相同的代码用于222帧动画。

参考代码:

import pygame
import random

# Window size
WINDOW_WIDTH    = 300
WINDOW_HEIGHT   = 300
WINDOW_SURFACE  = pygame.HWSURFACE|pygame.DOUBLEBUF|pygame.RESIZABLE

DARK_BLUE = (   3,   5,  54)

class MultiFrameSprite(pygame.sprite.Sprite):
    def __init__( self, fps, list_of_frames ):
        pygame.sprite.Sprite.__init__( self )

        # Load all the specified animation rames
        self.frames = []
        for filename in list_of_frames:
            self.frames.append( pygame.image.load( filename ).convert_alpha() )

        # Start the animation at the first frame
        self.image = self.frames[0]
        self.rect  = self.image.get_rect()

        # Frame handling
        self.millisec_rate = 1000 // fps   # inter-frame delay in milliseconds
        self.current_frame = 0
        self.last_frame_at = 0

        # we need to be somewhere on-screen
        self.rect.center = ( WINDOW_WIDTH//2, WINDOW_HEIGHT//2 )

    def update(self):
        # Compare the time of the last frame to time-now
        # and determine if it's time to show the next frame
        time_now = pygame.time.get_ticks()
        if ( time_now > self.last_frame_at + self.millisec_rate ):
            # new frame needed!
            self.last_frame_at = time_now
            # preserve the centroid in case the frames are differing sizes
            x,y = self.rect.center
            # Advance to the next frame
            self.current_frame += 1
            if ( self.current_frame == len( self.frames ) ):
                self.current_frame = 0                           # wrap frame loop index
            # Set the new image
            self.image = self.frames[ self.current_frame ]
            self.rect  = self.image.get_rect()
            self.rect.center = (x,y)                             # restore the centroid co-ordinate


### initialisation
pygame.init()
pygame.mixer.init()
window = pygame.display.set_mode( ( WINDOW_WIDTH, WINDOW_HEIGHT ), WINDOW_SURFACE )
pygame.display.set_caption( "Multi-Frame Sprite" )


### Create Sprites
all_sprites = pygame.sprite.Group()
adventurer  = MultiFrameSprite( 5, [ 'adventurer-idle-00.png', 
                                     'adventurer-idle-01.png', 
                                     'adventurer-idle-02.png', 
                                     'adventurer-idle-03.png' ] )
all_sprites.add( adventurer )


### Main Loop
clock = pygame.time.Clock()
done = False
while not done:

    # Handle user-input
    for event in pygame.event.get():
        if ( event.type == pygame.QUIT ):
            done = True

    # Update the window, but not more than 60fps
    all_sprites.update()
    window.fill( DARK_BLUE )
    all_sprites.draw( window )       # Paint ALL the sprites
    pygame.display.flip()

    # Clamp FPS
    clock.tick_busy_loop(60)


pygame.quit()

注意:上面的代码中我使用了5 FPS,因为它非常适合动画。