3

I read up on how to use GIFs in PyGame, and most of them said to split the GIF into images, and render each of them using a list, I tried implementing that, but I got one static image. I guess that it was too fast for me to see. I also tried a library called GIFImage here, it, however failed and produced the same static image. Edit: The static image is 'idle/idle (4).jpg'

Code:

import pygame
from random import *
pygame.init()
screen = pygame.display.set_mode((400, 300))
running = True
spriteVY = 3
clock = pygame.time.Clock()

idle_imgs = (
    "idle/idle (1).jpg",
    "idle/idle (2).jpg",
    "idle/idle (3).jpg",
    "idle/idle (4).jpg"
)

class MySprite(pygame.sprite.Sprite):
    def __init__(self, x, y):
        super().__init__()
        self.vel = (0, 0)
        self.image = pygame.Surface((100, 200))
        self.image.fill((255, 255, 255))
        self.rect = self.image.get_rect(topleft = (x, y))

class Platform(pygame.sprite.Sprite):
    def __init__(self, x, y):
        super().__init__()
        self.image = pygame.Surface((100, 10))
        self.image.fill((142, 212, 25))
        self.rect = self.image.get_rect(topleft = (x, y))

sprite = MySprite(100, 100)
platform = Platform(50, 20)
group = pygame.sprite.Group([sprite, platform])

on_ground = True
while running:

    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False

    if pygame.key.get_pressed()[pygame.K_SPACE]: sprite.rect.y -= 20
    if pygame.key.get_pressed()[pygame.K_d]: sprite.rect.x += 10
    if pygame.key.get_pressed()[pygame.K_a]: sprite.rect.x -= 10

    if platform.rect.colliderect(sprite.rect):
        pass
    if sprite.rect.x <= 0:
        sprite.rect.x = 0
    if sprite.rect.x >= 300:
        sprite.rect.x = 300
    if sprite.rect.y < 100:
        on_ground = False
    if sprite.rect.y >= 100:
        spriteVY = 0
        on_ground = True
        sprite.rect.y = 100
    if not on_ground:

        sprite.rect.y += spriteVY
        spriteVY += 1
    screen.fill((0, 0, 0))
    group.draw(screen)
    for img in idle_imgs:
        print(img)
        chosen_img = pygame.image.load(img)
        screen.blit(chosen_img, ((100, 100)))
    pygame.display.flip()
    clock.tick(24)

2 Answers2

0

This is the relevant code:

for img in idle_imgs:
    print(img)
    chosen_img = pygame.image.load(img)
    screen.blit(chosen_img, ((100, 100)))
pygame.display.flip()
clock.tick(24)

The problem is relatively clear. You blit the 4 images to the screen, but you do not call flip() until after the last one. That means the last one is the only one that will get displayed.

The screen surface that you are drawing to does not get sent to the actual display surface until you call flip(), so none of the images other than the last one get sent to the display.

You could try to resolve that by putting a flip() in the for loop, but that will not resolve your issue either. It will push the images to the actual display, but they will be displayed very fast (which is what you incorrectly thought was happening currently). You need to slow it down to the frame rate by calling your clock.tick(24). That will interfere with your game loop though. So that is not the right approach.

Normally for this kind of thing I would have a flag indicating whether or not it should be displaying the idle loop (something like idling = True when it is supposed to display it). Then you need another variable that keeps track of the current idle frame to be displayed (say something like idle_frame). When that reaches the last frame (idle_frame == len(idle_imgs)) you reset it to 0 and start again. When you do not want the idle animation to play set idling == False and skip that display code.

That should give you enough help to code it yourself.

I noticed something else that is going to cause you performance issues. You have your names of the images in the list. Instead you should actually load the images into a list and display the already loaded images. Loading them from a file evey time is needlessly slow. Also you should run convert() on the images after you load them (or convert_alpha() if the image has transparency). That is important to speed up the displaying of images. You can read about convert() and convert_alpha() here.

Glenn Mackintosh
  • 2,765
  • 1
  • 10
  • 18
  • Thank you for the answer, however it did not work, I tried putting `pygame.display.flip()` in the for loop, and the error persists. Edit: Never mind, it works, it just impacts game performance –  May 25 '20 at 05:54
  • @PySnoo I added a quick answer that just explained the issue, but then extended it to illustrate how do better solve it. You should not put `flip()` in the for loop. I never suggested that. – Glenn Mackintosh May 25 '20 at 06:12
0

You blit all the images before you use pygame.display.flip() therefore all images will be replaced by the last one before the screen gets updated. You can fix this by only blitting one image every frame instead of all of them.

In the following example I keep track of wich frame is currently displayed. The code then increments that value every frame and loops back around if the last frame is reached.

I also store the images directly inside of the list as loading the image from a file every frame is very inefficient.

import pygame

pygame.init()
screen = pygame.display.set_mode((sc_w, sc_h))

...

frames = [] # to store the different images of the GIF
for i in range(4):
    frames.append(pygame.image.load(f"idle/idle ({i+1}).jpg").convert_alpha())

running = True
current_frame = 0 # keep an index of what frame we're currently looking at
while running:
    for ev in pygame.event.get():
        ...
    
    ...

    screen.blit(frames[current_frame], (x,y)) # draw the current frame to the screen
    current_frame += 1 # go to the next frame
    current_frame %= len(frames) # loop back around if you reached the end
    pygame.display.flip()
    clock.tick(FPS)

I stripped a lot of the unrelevant code, i hope it is still clear what i've changed.