Как спать между кадрами, используя Pygame? - PullRequest
0 голосов
/ 29 октября 2019

Я делаю примитивную анимацию в своей игре на Python и хотел немного поспать между кадрами, но вместо того, чтобы спать каждый кадр, Pygame просто спит все время и рисует только последний кадр (я пробовал pygame.time.wait иФункции pygame.time.delay и time.sleep, все дали одинаковый результат)

Я использую этот код в качестве игрового движка:

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

и в некоторых случаях я хочу заморозитьвсе мои игры и делайте что-то вроде этого:

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()

Я ожидаю нарисовать каждый кадр за .5 секунд, но вместо этого только что получил последний после 1,5 секунд ожидания

Ответы [ 3 ]

0 голосов
/ 29 октября 2019

Трудно сказать, в чем проблема, только по предоставленному коду.

Вызывает ли метод draw_frame pygame.display.update или pygame.display.flip? Похоже, что окну просто не говорят перейти к следующему кадру, пока не вернется метод delete_ball_sequence.

pygame необходимо сообщить об обновлении дисплея, вызвав либо pygame.display.update, либо pygame.display.flip послевесь чертежный код для рамки.

т.е.

def draw_frame():
    # other drawing code goes here

    # update the display
    pygame.display.flip()


0 голосов
/ 29 октября 2019

Я не знаю, если это действительно ответ для вас, но я использовал систему анимации на основе сопрограмм в моей основе на основе Pygame. И если вы хотите использовать его, вы можете сделать то, что вы хотите с ним.

Код таков:

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)

Вы должны вызывать Coroutines каждый кадр в основном цикле. Затем, как использовать этот большой блок кода:

Сделать анимацию так:

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

Затем вы можете запустить эту анимацию в любое время, написав:

StartCoroutine(my_animation)

Например, этот код пишет «Hello» каждую секунду:

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.

Но не забывайте вызывать сопрограммы для каждого кадра в основном цикле.

Также это не сработает:

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

Если вы используете WaitForLoop (n) вместо WaitForSecond (n), он будет ждать n кадров вместо ожидания n секунд. Вы можете использовать float в качестве аргумента WaitForSecond.

Надеюсь, это поможет вам!

0 голосов
/ 29 октября 2019

В моем коде у меня есть основной цикл игры

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

Если я хочу установить время между каждой итерацией игры, я могу изменить свой код, импортируя время и используя time.sleep (время). Есть и другие способы сделать это, но это прекрасно работает для меня.

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

Если ваш компьютер мгновенно загружает эти кадры, тогда это должно быть ~~ 60 fps

...