【问题标题】:How to sleep between frames using Pygame?如何使用 Pygame 在帧之间休眠?
【发布时间】:2020-02-24 19:16:42
【问题描述】:

我在我的 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()

我希望每帧绘制 0.5 秒,但在等待 1.5 秒后才得到最后一帧

【问题讨论】:

  • self.draw_frame() 是做什么的? delete_ball_sequence() 怎么称呼?
  • 它只是绘制一些图元,如正方形、圆形,并以 pygame.display.flip() 结尾
  • 并且在某些情况下从 update 方法中调用它,因此这意味着我想要在主循环迭代之间添加额外的帧。所以,问题是我只看到最后一帧,而不是全部

标签: python python-3.x pygame sleep


【解决方案1】:

为什么你不尝试 timeit 模块。也许你也可以使用pygame的时间函数。

【讨论】:

    【解决方案2】:

    你试过time.sleep吗?如果有,你是怎么写的?

    【讨论】:

      【解决方案3】:

      我不知道这是否真的适合你,但我在基于 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)
      
      

      您必须在主循环中的每一帧调用协程。 那么,这就是如何使用那个大代码块了;

      制作这样的动画:

      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 秒。 您可以使用浮点数作为 WaitForSecond 的参数。

      希望对你有帮助!

      【讨论】:

        【解决方案4】:

        仅从提供的代码很难判断问题所在。

        draw_frame 方法是否调用pygame.display.updatepygame.display.flip? 似乎在 delete_ball_sequence 方法返回之前,不会告诉窗口切换到下一帧。

        需要通过调用pygame.display.updatepygame.display.flip 来告知pygame 更新显示,方法是在框架的所有绘图代码之后调用。

        def draw_frame():
            # other drawing code goes here
        
            # update the display
            pygame.display.flip()
        
        
        

        【讨论】:

        • 是的,它最后调用 pygame.display.flip() ,我不知道为什么,但在我的屏幕上我只看到最后一帧,而另一帧只是跳过....这就是问题所在
        • @PziStivChanell 嗯...好吧。问题可能出在代码中的其他地方。也许对象没有按照您期望的方式更新?不看其余代码很难知道。
        【解决方案5】:

        在我的代码中,我有一个主游戏循环

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

        如果我想在每次游戏迭代之间设置一个时间,我可以通过导入时间和使用 time.sleep(time) 来修改我的代码。还有其他方法可以做到这一点,但这对我很有用。

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

        如果您的计算机立即加载这些帧,那么这应该以 ~~60 fps 的速度进行

        【讨论】:

          猜你喜欢
          • 2015-04-17
          • 2014-11-10
          • 2012-09-30
          • 1970-01-01
          • 2018-04-10
          • 2013-02-15
          • 1970-01-01
          • 2013-10-19
          • 2015-11-07
          相关资源
          最近更新 更多