【问题标题】:Simple Pygame animation stuttering简单的 Pygame 动画口吃
【发布时间】:2016-10-03 16:38:54
【问题描述】:

我有一个用“Pygame”绘制的简单弹跳框窗口。一切似乎都正常,除了一点点烦恼。它经常口吃!我不知道是什么导致了口吃。我认为这可能是延迟,所以我实现了一个固定的时间步长以允许循环赶上,但这没有效果。

#--- initialize pygame window ---#
import pygame
import time
pygame.init()
size = (1200,500)
screen = pygame.display.set_mode(size, pygame.RESIZABLE)
fps = 60

#--- define color palette ---#
black = (0,0,0)
white = (255,255,255)

#--- define the player ---#
class player:
    def __init__(self,screen,surface, color):
        self.speed = 3
        self.direction_x = 1
        self.direction_y = 1
        self.screen = screen
        self.surface = surface
        self.rect = self.surface.get_rect()
        self.color = color
    def set_pos(self, x,y):
        self.rect.x = x
        self.rect.y = y
    def advance_pos(self):
        screen_width, screen_height = screen.get_size()
        if self.rect.x + self.rect.width > screen_width or player1.rect.x < 0:
            player1.direction_x *= -1
            player1.speed = 3
        elif player1.rect.y + player1.rect.height > screen_height or player1.rect.y < 0:
            player1.direction_y *= -1
            player1.speed = 3
        else:
            player1.speed -= 0.001
        self.rect.x += self.speed * self.direction_x
        self.rect.y += self.speed * self.direction_y
    def draw(self):
        pygame.draw.rect(self.surface, self.color, [0,0,self.rect.width,self.rect.height])
    def blit(self):
        screen.blit(self.surface, self.rect)
player1 = player(screen, pygame.Surface((50,50)), white)
player1.set_pos(50,50)
player1.draw()

#--- define game variables ---#
previous = time.time() * 1000
lag = 0.0
background = black
done = False

#--- game ---#
while not done:

    #--- update time step ---#
    current = time.time() * 1000
    elapsed = current - previous
    lag += elapsed
    previous = current

    #--- process events ---#
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            done = True
            break
        if event.type == pygame.VIDEORESIZE:
            screen = pygame.display.set_mode((event.w, event.h), pygame.RESIZABLE)

    #--- update logic ---#

    while True:
        player1.advance_pos()
        lag -= fps
        if lag <= fps:
            break

    #--- draw to screen ---#
    screen.fill(background)
    player1.blit()
    pygame.display.update()
    pygame.time.Clock().tick(fps)

【问题讨论】:

  • 你看过this链接吗?那里提供的答案有一些有用的建议。如果您需要 60 fps,则需要 vsync,我不确定 pygame 是否提供,除非您使用 pyopengl 之类的东西进行 pygame 渲染。
  • 我不知道 PyOpenGL。谢谢!
  • 如果您以前没有使用过 OpenGL,从头开始学习 OpenGL 可能会非常具有挑战性。如果有帮助,我可以发布一个演示,复制您已经使用 pygame 和 pyopengl 编写的内容。
  • 那太棒了
  • 我运行了您的代码,但没有遇到任何明显的卡顿。你得到什么FPS?如果它以 60 fps 运行,则不应该有任何明显的卡顿。我还建议创建一个时钟变量,例如 clock = pygame.time.Clock() 然后调用 clock.tick(fps) 而不是你的最后一行,这样每帧都会创建一个新的时钟对象。

标签: python pygame


【解决方案1】:

这是对您的代码的重写,它使用 opengl 代替渲染。主要变化如下:

  1. 我使用了 opengl 即时模式,该模式已过时且已弃用,但起初更容易理解。大多数 gl 调用要么在 player.draw() 方法中,要么在主循环中。
  2. 我修正了计时器的完成方式。我不是只做clock.tick(fps),而是手动跟踪对帧进行所有处理所需的时间,并添加适当的毫秒延迟以达到60 fps。您可以在迁移到 opengl 之前尝试使用现有的 pygame 代码进行修改,因为这可能足以消除大部分卡顿。

    import pygame
    import time
    from OpenGL.GL import *
    
    class Player:
        def __init__(self, screen, width, height, color):
            self.x = 0
            self.y = 0
            self.speed = 3
            self.direction_x = 1
            self.direction_y = 1
            self.screen = screen
            self.width = width
            self.height = height
            self.color = color
    
        def set_pos(self, x, y):
            self.x = x
            self.y = y
    
        def advance_pos(self):
            screen_width, screen_height = screen.get_size()
            if self.x + self.width > screen_width or self.x < 0:
                self.direction_x *= -1
                self.speed = 3
            elif self.y + self.height > screen_height or self.y < 0:
                self.direction_y *= -1
                self.speed = 3
            else:
                self.speed -= 0.001
            self.x += self.speed * self.direction_x
            self.y += self.speed * self.direction_y
    
        def draw(self):
            glMatrixMode(GL_MODELVIEW)
            glLoadIdentity()
            glTranslate(self.x, self.y, 0)
            glBegin(GL_QUADS)
            glColor(*self.color)
            glVertex(0, 0, 0)
            glVertex(self.width, 0, 0)
            glVertex(self.width, self.height, 0)
            glVertex(0, self.height, 0)
            glEnd()
    
    if __name__ == "__main__":
        pygame.init()
        size = width, height = (550, 400)
        screen = pygame.display.set_mode(size, pygame.RESIZABLE | pygame.DOUBLEBUF | pygame.OPENGL)
        fps = 60
        black = (0,0,0,255)
        white = (255,255,255,255)
    
        player1 = Player(screen, 50, 50, white)
        player1.set_pos(50,50)
    
        done = False
        previous = time.time() * 1000
        glClearColor(*black)
        glMatrixMode(GL_PROJECTION)
        glLoadIdentity()
        glOrtho(0, width, height, 0, -1, 1)
        clock = pygame.time.Clock()
    
        while not done:
            current = time.time() * 1000
            elapsed = current - previous
            previous = current
            delay = 1000.0/fps - elapsed
            delay = max(int(delay), 0)
    
            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    done = True
                    break
                if event.type == pygame.VIDEORESIZE:
                    size = width, height = event.w, event.h
                    screen = pygame.display.set_mode(size, pygame.RESIZABLE | pygame.DOUBLEBUF | pygame.OPENGL)
                    glMatrixMode(GL_PROJECTION)
                    glLoadIdentity()
                    glOrtho(0, width, height, 0, -1, 1)
                    glViewport(0, 0, width, height)
    
                    #reset player movement and position to avoid glitches where player is trapped outside new window borders
                    player1.set_pos(50, 50)
                    player1.direction_x = 1
                    player1.direction_y = 1
    
            player1.advance_pos()
            glClear(GL_COLOR_BUFFER_BIT)
            glClear(GL_DEPTH_BUFFER_BIT)
            player1.draw()
            pygame.display.flip()
            pygame.time.delay(delay)
    

【讨论】:

  • 太棒了!谢谢你的帮助。 OpenGL 会很有趣。
【解决方案2】:

我在进入“未完成”循环之前创建了一个时钟对象,并且没有更多的滞后

#--- initialize pygame window ---#
import pygame
import time
pygame.init()
size = (1200,500)
screen = pygame.display.set_mode(size, pygame.RESIZABLE)
fps = 60

#--- define color palette ---#
black = (0,0,0)
white = (255,255,255)

#--- define the player ---#
class player:
    def __init__(self,screen,surface, color):
        self.speed = 3
        self.direction_x = 1
        self.direction_y = 1
        self.screen = screen
        self.surface = surface
        self.rect = self.surface.get_rect()
        self.color = color
    def set_pos(self, x,y):
        self.rect.x = x
        self.rect.y = y
    def advance_pos(self):
        screen_width, screen_height = screen.get_size()
        if self.rect.x + self.rect.width > screen_width or player1.rect.x < 0:
            player1.direction_x *= -1
            player1.speed = 3
        elif player1.rect.y + player1.rect.height > screen_height or player1.rect.y < 0:
            player1.direction_y *= -1
            player1.speed = 3
        else:
            player1.speed -= 0.001
        self.rect.x += self.speed * self.direction_x
        self.rect.y += self.speed * self.direction_y
    def draw(self):
        pygame.draw.rect(self.surface, self.color, [0,0,self.rect.width,self.rect.height])
    def blit(self):
        screen.blit(self.surface, self.rect)
player1 = player(screen, pygame.Surface((50,50)), white)
player1.set_pos(50,50)
player1.draw()

#--- define game variables ---#
previous = time.time() * 1000
lag = 0.0
background = black
done = False
clock=pygame.time.Clock()

#--- game ---#
while not done:

    #--- update time step ---#
    current = time.time() * 1000
    elapsed = current - previous
    lag += elapsed
    previous = current

    #--- process events ---#
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            done = True
            break
        if event.type == pygame.VIDEORESIZE:
            screen = pygame.display.set_mode((event.w, event.h), pygame.RESIZABLE)

    #--- update logic ---#

    while True:
        player1.advance_pos()
        lag -= fps
        if lag <= fps:
            break

    #--- draw to screen ---#
    screen.fill(background)
    player1.blit()
    pygame.display.update()
    clock.tick(fps)

pygame.quit()

【讨论】:

    猜你喜欢
    • 2011-09-18
    • 1970-01-01
    • 2014-04-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-09-24
    相关资源
    最近更新 更多