首页 > 解决方案 > 为什么这个简单的 pygame 程序滞后?

问题描述

所以,我学了很短时间的编程,决定用pygame制作贪吃蛇游戏。然而,在制作程序的基础时,我意识到玩家控制的矩形(蛇)在移动时每秒都会传送(可能是滞后)。这是代码:

import pygame

pygame.init()

# Window
window = (1280, 720)
center = (window[0]//2, window[1]//2)
screen = pygame.display.set_mode(window)
pygame.display.set_caption("Snake")

# Colors
COLOR_LIGHT_GREY = (200, 200, 200)
COLOR_DARK_GREY = pygame.Color('gray12')

# Game loop
game_loop = True
game_clock = pygame.time.Clock()


# Create image
def img(name):
    img_path = "./assets/natanael.lucena_" + name + ".png"
    return pygame.image.load(img_path).convert_alpha()


# Set object coordinates
def set_obj_coordinates(obj, x, y):
    obj.x = x
    obj.y = y


# Check player key press
def check_player_key(b):
    global snake_direction
    if event.key == pygame.K_w or event.key == pygame.K_s or event.key == pygame.K_a or event.key == pygame.K_d:
        snake_direction[event.key] = b


# Check key events in-game
def event_conditional():
    global game_loop
    if event.type == pygame.QUIT:
        game_loop = False
    elif event.type == pygame.KEYDOWN:
        check_player_key(True)
    elif event.type == pygame.KEYUP:
        check_player_key(False)

# Check if the snake collided and the game is over
def game_over():
    if snake.y < 0 or snake.y > 720 or snake.x < 0 or snake. x > 1280:
        return True


# Snake
snake_img = img("snake")
snake = snake_img.get_rect()
move_keys = [pygame.K_w, pygame.K_d, pygame.K_s, pygame.K_a]
snake_direction = {k: False for k in move_keys}
snake_score = 0
snake_vel = 10
set_obj_coordinates(snake, center[0], center[1])


# Apple
apple_img = img("apple")
apple = apple_img.get_rect()
apple_eaten = False
set_obj_coordinates(apple, 40, 40)


# Main game loop
while game_loop:
    for event in pygame.event.get():
        event_conditional()
    # score_text = text_render(snake_score)
    if not game_over():
        for i in range(4):
            if i % 2:
                coord_aux = "x "
            else:
                coord_aux = "y "
            if i % 3:
                op = "+= "
            else:
                op = "-= "
            if snake_direction[move_keys[i]]:
                exec("snake." + coord_aux + op + "snake_vel")

        # the for loop above is equivalent to :
        # if snake_direction[move_keys[0]]:
        #    snake.y -= snake_vel
        # if snake_direction[move_keys[1]]:
        #    snake.x += snake_vel
        # if snake_direction[move_keys[2]]:
        #    snake.y += snake_vel
        # if snake_direction[move_keys[3]]:
        #    snake.x -= snake_vel

        screen.fill(COLOR_DARK_GREY)
        screen.blit(snake_img, snake)
        screen.blit(apple_img, apple)

    # Update screen
    pygame.display.flip()
    game_clock.tick(60)

pygame.quit()

我怎么了 如果有人能告诉我问题的原因,我真的很感激。

编辑:看起来问题只是发生在我身上

标签: python-3.xpygame

解决方案


这是 pygame 的一个常见问题,尤其是在 pygame 2/sdl 2 之后,您无法再使用directx视频驱动程序并启用 vsync。

您应该执行以下操作:

  • 跟踪移动游戏对象的亚像素坐标。ARect只能在其xy 属性中存储整数值,因此您需要另一个变量来存储它。我通常使用 a Vector2,因为它易于使用,而且性能损失通常无关紧要。

  • 使用准确的时钟。Pygame 的时钟对象只使用毫秒,这对于真正平滑的移动来说不够精确。如果你在 Windows 上,通常最好的计时方法是GetSystemTimePreciseAsFileTime函数。

  • 使用增量计时

您还可以为需要不同计时方法的游戏部分使用不同的线程(例如,您的游戏逻辑需要固定的 30 或 60 FPS 并且您的绘图代码希望尽可能快地运行),但这对于您的小型游戏来说太过分了。

所以这是我一起编写的一个例子,它可以让你流畅地移动(至少这是我通常使用的,因为它对我来说很好。注意它是特定于 Windows 的):

import pygame

import ctypes.wintypes

pygame.init()


screen = pygame.display.set_mode((1280, 720))
center = screen.get_rect().center
pygame.display.set_caption("Snake")


game_loop = True

# https://stackoverflow.com/a/28574340/142637
def utcnow_microseconds():
    system_time = ctypes.wintypes.FILETIME()
    ctypes.windll.kernel32.GetSystemTimePreciseAsFileTime(ctypes.byref(system_time))
    large = (system_time.dwHighDateTime << 32) + system_time.dwLowDateTime
    return large // 10 - 11644473600000000

# Snake
snake_img = pygame.Surface((40, 40))
snake_img.fill('white')
snake = snake_img.get_rect()

snake_vel = 10
snake_pos = pygame.Vector2(center[0], center[1])
snake.topleft = snake_pos.x, snake_pos.y

# Apple
apple_img = pygame.Surface((40, 40))
apple_img.fill('red')
apple = apple_img.get_rect(topleft=(40, 40))


dt = 0
while game_loop:

    t1 = utcnow_microseconds()
    
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            game_loop = False
            
    keys = pygame.key.get_pressed()
    snake_pos.x += (keys[pygame.K_d] - keys[pygame.K_a]) * snake_vel * dt
    snake_pos.y += (keys[pygame.K_s] - keys[pygame.K_w]) * snake_vel * dt
    snake.topleft = snake_pos.x, snake_pos.y

    screen.fill('darkgrey')
    screen.blit(snake_img, snake)
    screen.blit(apple_img, apple)

    pygame.display.flip()

    t2 = utcnow_microseconds()
    dt = (t2 - t1) / 1000. / 1000. * 30

pygame.quit()

进一步阅读


推荐阅读