0
votes

I'm doing primitive animation in my Python game and wanted to sleep between frames a bit, but instead of sleeping each frame, Pygame just sleep all time and draw just final frame (I've tryed pygame.time.wait and pygame.time.delay and time.sleep functions, all gave same result)

I'm using this code as a game engine:

while not game.finished:
    dt = game.clock.tick() / 1000
    game.handle_events()
    game.update(dt)
    game.draw_frame()

and in some cases I want to freeze all my game and do something like this:

def delete_ball_sequence(self, start, end):
        for i in range(end - start + 1):
            del self.balls[start]
        for i in range(3):
            self.move_ball_by_distance(self.balls[i], 100)
            self.draw_frame()
            pygame.time.delay(500)
        self.clock.tick()

I expect to draw each frame for .5 seconds, but instead just got final one after 1.5 sec of waiting

5
What does self.draw_frame() do? How is delete_ball_sequence() called? - Johnny Mopp
it's just drawing some primitives like squares, circles and ends with pygame.display.flip() - PziStiv Chanell
and it's called in some cases from update method, so it means I want additional frames between main cycle iterations. So, the problem is that I just see last frame, instead of all - PziStiv Chanell

5 Answers

1
votes

In my code I have a main game loop

while gameRun == True:
     doThing()
     counter += 1

If I want to make a set time between each game iteration I can modify my code by importing time and using time.sleep(time). There are other ways to do this but this works great for me.

import time
while gameRun == True
     doThing()
     counter += 1
     time.sleep(0.0166) 

If your computer loads these frames instantly then this should go at ~~60 fps

0
votes

Its hard to tell what the problem is just from the code provided.

Does the draw_frame method call pygame.display.update or pygame.display.flip? It seems like the window is just not being told to switch to the next frame until the delete_ball_sequence method returns.

pygame needs to be told to update the display by calling either pygame.display.update or pygame.display.flip after all the drawing code for the frame.

i.e.

def draw_frame():
    # other drawing code goes here

    # update the display
    pygame.display.flip()


0
votes

I dont know if that is really a answer for you, but i used coroutine based animation system in my pygame based framework. And if you want to use it, you can make what you want with it.

Code is that:

from timeit import default_timer as timer

class GameEngineError(Exception): pass

coroutine_dict = {}
Coroutine_list = []

class WaitForLoop():
    def __init__(self,number):
        if number < 1:
            number = 1
        self.number = number

class WaitForSecond():
        def __init__(self,time):
            self.time = timer() + time

def StartCoroutine(generator,*args,**kwargs):
    """Starts generator function with args and kwargs"""
    gen = generator(*args,**kwargs)
    Coroutine_list.append(gen)
    coroutine_dict[gen] = WaitForLoop(1)

def Invoke(f,time,*args,**kwargs):
    """Call f function or generator after time second(s) with args and kwargs"""
    def Invoker():
        yield WaitForSecond(time)
        f(*args,**kwargs)
    StartCoroutine(Invoker)

def Coroutines():
    """Call that per frame"""
    global coroutine_dict
    if len(Coroutine_list) != 0:
        will_remove= []
        for cor in Coroutine_list:
            yielded = coroutine_dict[cor]
            if type(yielded)==WaitForSecond:
                if timer() >= yielded.time:
                    try:
                        new_yield = next(cor)
                    except StopIteration:
                        will_remove.append(cor)
                    else:
                        coroutine_dict[cor] = new_yield
            elif type(yielded)==WaitForLoop:
                yielded.number -= 1
                if yielded.number==0:
                    try:
                        new_yield = next(cor)
                    except StopIteration:
                        will_remove.append(cor)
                    else:
                        coroutine_dict[cor] = new_yield
            else:
                raise GameEngineError("Type of coroutine yield must be WaitForSecond or WaitForLoop. Not "+str(type(yielded))+" . Check your generator definition which named '"+cor.__name__+"' .")

        for i in will_remove:
            Coroutine_list.remove(i)
            coroutine_dict.pop(i)

You must call Coroutines every frame in your mainloop. Then, that is how to use that big code block;

Make animation like that:

def my_animation():
    make_something()
    yield WaitForSecond(1) # waits 1 second but not blocing your game or main loop
    make_another_thing()

Then you can start that animation any time by writing that:

StartCoroutine(my_animation)

For example this code write "Hello" every second:

def write_per_second(text):
    while True:
        print(text)
        yield WaitForSecond(1) # waits 1 second but not blocing your game or main loop
StartCoroutine(write_per_second,"hello") # "hello" will be first -and only- argument of write_per_second.

But dont forget calling Coroutines every frame in your mainloop.

Also that won't work true:

def write_per_second(text):
    second = WaitForSecond(1) # that is invalid using. You should instance WaitForSecond when you are yielding it.
    while True:
        print(text)
        yield second

If you use WaitForLoop(n) instead of WaitForSecond(n), that will wait n frame instead of waiting n second. You can use floats as argument of WaitForSecond.

I hope that helps you!

-2
votes

Did you try time.sleep? If yes, how did you write that?

-2
votes

Why you dont try timeit module. Maybe you can use time functions of pygame too.