Search code examples
pythonpygameframe-rate

Rendering Images At a Different Framerate Than The Game Window


I have an animation I need to play every time the mouse button is clicked.

I have the frames stored in a list, for every frame that Pygame renders, one frame in the list is blit to the screen.

My issue is that because this happens every frame, the animation is played at 1000fps, even though there are only 8 frames, so the animation is unnoticeable. Is there any way to run the animation at 16fps while still allowing Pygame to run through the game loop at normal speeds? I have a game that needs to be run at Pygame's normal render speed but the animations should be played slower.

Here is an example of what I am currently doing:

pygame.init()
screen = pygame.display.set_mode((800, 600))


running = True
while running:
    screen.fill('#000000')
    mouse_pos = pygame.mouse.get_pos()

    explosion = (pygame.image.load('assets/Explosion/E_1.png'), pygame.image.load('assets/Explosion/E_2.png'),
                 pygame.image.load('assets/Explosion/E_3.png'), pygame.image.load('assets/Explosion/E_4.png'),
                 pygame.image.load('assets/Explosion/E_5.png'), pygame.image.load('assets/Explosion/E_6.png'),
                 pygame.image.load('assets/Explosion/E_7.png'), pygame.image.load('assets/Explosion/E_8.png'))

    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False
            exit()
        if event.type == pygame.MOUSEBUTTONDOWN:
            for frame in explosion:
                framerect = frame.get_rect(center=mouse_pos)
                screen.blit(frame, framerect)
        
    pygame.display.update()

pygame.quit()

Solution

  • Actually, the animation is rendered in 1 frame because you render the animations in a for loop inside the application loop. You need to use the application loop. Limit the number of frames with pygame.time.Clock.tick and load the frames once before the application loop to improve performance.

    In the following example, clock.tick(100) sets the frames per second, and animate_frames = 10 defines how many frames of animation will be displayed:

    import pygame
    
    pygame.init()
    screen = pygame.display.set_mode((800, 600))
    
    try:
        explosion = (pygame.image.load('assets/Explosion/E_1.png'), pygame.image.load('assets/Explosion/E_2.png'),
                    pygame.image.load('assets/Explosion/E_3.png'), pygame.image.load('assets/Explosion/E_4.png'),
                    pygame.image.load('assets/Explosion/E_5.png'), pygame.image.load('assets/Explosion/E_6.png'),
                    pygame.image.load('assets/Explosion/E_7.png'), pygame.image.load('assets/Explosion/E_8.png'))
    except:
        explosion = []
        for i in range(10):
            e = pygame.Surface((100, 100), pygame.SRCALPHA)
            pygame.draw.circle(e, (200 - i * 10, 200 - i * 20, 0), (50, 50), i*5)
            explosion.append(e) 
    
    clock = pygame.time.Clock() 
    animate_frames = 10
    animations = []
    running = True
    while running:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                running = False
                exit()
            if event.type == pygame.MOUSEBUTTONDOWN:
                animations.append([event.pos, 0])
    
        screen.fill('#000000')
        for animation in animations[:]:
            animation[1] += 1
            frame_index = animation[1] // animate_frames
            if frame_index < len(explosion):
                frame = explosion[frame_index]
                framerect = frame.get_rect(center=animation[0])
                screen.blit(frame, framerect)
            else:
                animations.remove(animation)
            
        pygame.display.update()
        clock.tick(100)
    
    pygame.quit()
    

    PYGBAG demo


    Also see How can I show explosion image when collision happens?