1

I am hitting an issue after writing a small program with pygame. The program takes split .GIF animations and loads the images(frames of the gif) via:

pygame.image.load(filename)

This returns a pygame surface object which is then appended to an array. The program loads about 15 frames to an array, using 6 arrays in total.

The problem I am having is when accepting input through my while loop. The loop plays its idle animation and running animation fine, however, on accepting input from the keyboard (grabbing input from pygame's event list...)

for event in pygame.event.get(): via pygame.KEYDOWN

There is very noticeable lag, making for unresponsive animation set switching. If I am to make a game using this method, it will have to be fixed. I'm sure my code is inefficient, but it should suffice not to create pauses. Any help would be fantastic.

My guess? pygame.clock.tick() is creating some sort of event lag, but I am unsure as to how to go about fixing this, if event lag is even the case.

Here is the loop that I suspect to be problematic:

while running == 2:
pygame.display.flip()
#mouse = pygame.mouse.get_pos()
#events = pygame.event.get()
#(pygame.QUIT, pygame.KEYDOWN, pygame.KEYUP)
for event in pygame.event.get():
#event = pygame.event.wait()
    if event.type == pygame.QUIT:
        sys.exit(0)
    elif event.type == pygame.KEYDOWN:
        print event.key
        wait = 0
        if event.key == pygame.K_d:
            tmpcache = wr
            lastkey = "wr"
        elif event.key == pygame.K_a:
            tmpcache = wl
            lastkey = "wl"
    elif event.type == pygame.KEYUP:
        wait = 1
        if lastkey == "wr":
            tmpcache = sr
        elif lastkey == "wl":
            tmpcache = sl

if wait == 1:           
    for frame in tmpcache:
        screen.blit(test, (0,0))
        screen.blit(frame, (currentchar.posx, currentchar.posy))
        pygame.display.flip()
        clock.tick(charfps)

else:
    for frame in tmpcache:
        screen.blit(test, (0,0))
        screen.blit(frame, (currentchar.posx, currentchar.posy))
        pygame.display.flip()
        clock.tick(charfps)

some variables not shown here, but are used:

charfps = 30
currentchar.posx, currentchar.posy are both tuples set at (300, 240)

CR0SS0V3R
  • 236
  • 5
  • 11

1 Answers1

3

Your problem is that you create sub-loops inside your main-loop:

while running == 2:
    pygame.display.flip()
    for event in pygame.event.get():
        ...
    for frame in tmpcache:
        screen.blit(test, (0,0))
        screen.blit(frame, (currentchar.posx, currentchar.posy))
        pygame.display.flip()
        clock.tick(charfps)

So if there are 15 elements in tmpcache, you're calling clock.tick() 15 time per frame, and while code runs inside this sub loop, you don't proceed events.

Simply calling pygame.display.flip() and clock.tick(charfps) only once per frame should resolve your issue.

Here's a simple example, changing the image of the animation three times a second, while running at 60 FPS:

import pygame
from collections import deque

pygame.init()
screen = pygame.display.set_mode((300, 300))
clock = pygame.time.Clock()

# just some colored squares for our animation
def get_cache(colors):
    tmp=[]
    for c in colors:
        s = pygame.surface.Surface((50,50))
        s.fill(pygame.color.Color(c))
        tmp.append(s)
    return tmp

walk_left, walk_right = get_cache(('red', 'yellow', 'blue')), get_cache(('black', 'white', 'grey'))

rect = walk_left[0].get_rect(top=100, right=100)
cachedeque = deque(walk_left)
state = None
quit = False

# a simple variable to keep track of time
timer = 0

# a dict of {key: (animation, direction)}
moves = {pygame.K_LEFT:  (walk_left,  (-2, 0)),
         pygame.K_RIGHT: (walk_right, ( 2, 0))}

while not quit:
    quit = pygame.event.get(pygame.QUIT)
    pygame.event.poll()

    # state of the keys
    keys = pygame.key.get_pressed()

    # filter for the keys we're interessted in
    pressed = ((key, _) for (key, _) in moves.iteritems() if keys[key])
    key, (cache, dir) = next(pressed, (None, (None, None)))

    # if a key of the 'moves' dict is pressed:
    if key:
        # if we change the direction, we need another animation
        if state != key: 
            cachedeque = deque(cache)
            state = key
        # move the square                
        rect.move_ip(dir) 
    else:
        state = None

    screen.fill(pygame.color.Color('green'))

    # display first image in cachedeque    
    screen.blit(cachedeque[0], rect)

    # rotate cachedeque to the left, so the second image becomes the first
    # do this three times a second:
    if state and timer >= 1000./3:
        cachedeque.rotate(-1)
        timer = 0

    # call flip() and tick() only once per frame
    pygame.display.flip()

    # keep track of how long it took to draw this frame
    timer += clock.tick(60)

enter image description here

sloth
  • 91,747
  • 17
  • 156
  • 204
  • I understand what you are saying by updating the display and ticking the clock only once per frame. What you gave me simply does not work however. It only gives me the second frame in the animation when the respective key is pressed. I want an animation to play at 30 fps of the character walking (a total of 15 or so frames) on a loop depending on whether or not the key is held down without actually having an effect on input times(creating lag while waiting for the animation to finish its loop). – CR0SS0V3R Jan 31 '14 at 02:57
  • That definitely helped me understand what went where, I've never used deque before and I believe this will help me in future programming projects. I updated my code with the same technique you showed me and it works fine now, thank you! – CR0SS0V3R Jan 31 '14 at 16:21