【问题标题】:Python pygame how to set the FPSPython pygame如何设置FPS
【发布时间】:2019-12-07 14:56:46
【问题描述】:

我正在制作一个 python 游戏,但我不知道应该将我的 FPS 设置为什么。我的游戏卡住了,不流畅。我怎么知道 FPS 应该是什么?

这是我的代码:

http://bin.shortbin.eu:8080/x87bSUKUiN

【问题讨论】:

  • 请详细说明,“卡住”是什么意思?您的游戏“卡住”可能与 FPS 无关。您应该发布您的代码,以便我们知道它为什么“卡住”。此外,30 FPS 似乎很正常,通常选择它作为屏幕刷新率的倍数。
  • 它没有卡住它只是很慢而且不流畅@iliar
  • 我猜你的问题是别的,与 FPS 设置无关。您应该编辑您的问题以包含您的代码。
  • 好的,我添加了代码@iliar

标签: python python-2.7 pygame pycharm frame-rate


【解决方案1】:

运行您的代码后,我还注意到帧速率下降,这会损害游戏的流畅性。

这里有两个不同的问题:

1. FPS 下降

FPS 下降的原因可能是您无法控制的,例如垃圾收集器工作。 即使您无法控制此类问题,您也可以总体上提高游戏的性能。请参阅以下游戏分析器运行的屏幕截图:

您可以看到大部分时间花在blit 上。但是,很大一部分时间也花在get_y_list 上。 get_y_list 方法还使用大型列表,这会创建大量垃圾供垃圾收集器稍后收集,从而进一步降低性能。

据我了解,get_y_list 方法是您用于碰撞检测的一种非常粗略的方法的一部分,该方法基本上需要二次时间。您的算法似乎将每个对象分成大量的 2d 单元格,然后测试每对单元格之间的碰撞。相反,您可以只测试框到框的交叉点。如果您希望对象具有复杂的碰撞形状,您可以使用其他算法,互联网上到处都是。例如:https://developer.mozilla.org/en-US/docs/Games/Techniques/2D_collision_detection

使用其他算法进行碰撞检测将大大提高您的性能。

2。 FPS 下降时游戏变得不流畅。

对象的xy 位置正在更新,例如:player.x -= player.vx。物理上正确的方法是:player.x -= player.vx * DELTA_T。其中 DELTA_T 是自上一帧以来经过的时间。如果您不使用自上一帧以来经过的时间,则角色的运动速度将取决于 FPS。这正是我们所看到的。

我在哪里可以得到DELTA_T 你可能会问。你在调用tick的时候直接做:

def main():
    global DELTA_T

    finish = False
    background_x = 0
    background_y = 0
    background = pygame.image.load(BACKGROUND_IAMGE)

    while not finish:
        DELTA_T = clock.tick(REFRESH_RATE)

我尝试将乘法与DELTA_T 相加,但游戏变得不稳定,因为您假设物体在每一帧中精确推进vx 'cells' 以检测碰撞和地板穿透。请参阅下面的尝试:

import pygame

pygame.init()
WINDOW_WIDTH = 700
WINDOW_HEIGHT = 500
SIZE = (WINDOW_WIDTH, WINDOW_HEIGHT)
SCREEN = pygame.display.set_mode(SIZE)
pygame.display.set_caption("Python Game")
WHITE = (255, 255, 255)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)
PINK = (255, 0, 255)
BACKGROUND_IAMGE = 'back.png'
clock = pygame.time.Clock()
REFRESH_RATE = 30
FRAMES_DELAY = 3

PLAYER_HEIGHT = 72
PLAYER_WIDTH = 40

MUSHROOM_HEIGHT = 31
MUSHROOM_WIDTH = 40

BLOCK_HEIGHT = 30
BLOCK_WIDTH = 30

FLOOR_Y = 435

DELTA_T = 0

class Player:
    def __init__(self, x, y):
        self.__images = [pygame.image.load('mario1.png'), pygame.image.load('mario2.png'),
                         pygame.image.load('mario3.png'), pygame.image.load('mario4.png'),
                         pygame.image.load('mario5.png'), pygame.image.load('mario6.png')]
        self.__current_image = self.__images[0]
        self.x = x
        self.y = y
        self.vx = 5/33.
        self.vy = 10/33.
        self.__is_mid_air = False
        self.__direction = "right"
        self.__is_jumping = False
        self.__MAX_JUMP = 20
        self.__is_walking = False
        self.__counter = 0
        self.__jump_counter = 0

    def reset_jump_counter(self):
        self.__jump_counter = 0

    def get_bottom_y(self):
        return int(self.y + PLAYER_HEIGHT)

    def set_walk(self, bol):
        self.__is_walking = bol

    def set_is_mid_air(self, bol):
        self.__is_mid_air = bol

    def is_mid_air(self):
        return self.__is_mid_air

    def get_image(self):
        if self.__is_mid_air and self.__direction == "right":
            self.__current_image = self.__images[4]
            return self.__current_image

        if self.__is_mid_air and self.__direction == "left":
            self.__current_image = self.__images[5]
            return self.__current_image

        self.__counter += 1
        if self.__counter > FRAMES_DELAY:
            self.__counter = 0

        if self.__counter == FRAMES_DELAY and self.__direction == "right":
            if self.__is_walking and self.__current_image == self.__images[0]:
                self.__current_image = self.__images[1]
            else:
                self.__current_image = self.__images[0]

        if self.__counter == FRAMES_DELAY and self.__direction == "left":
            if self.__is_walking and self.__current_image == self.__images[2]:
                self.__current_image = self.__images[3]
            else:
                self.__current_image = self.__images[2]

        return self.__current_image

    def stand_still(self):
        if self.__direction == "right":
            self.__current_image = self.__images[0]
        if self.__direction == "left":
            self.__current_image = self.__images[2]

    def set_jump(self, bol):
        self.__is_jumping = bol
        if not bol:
            self.reset_jump_counter()

    def check_jump(self):
        if self.__jump_counter != self.__MAX_JUMP and self.__is_jumping:
            self.y -= self.vy * DELTA_T
            self.__jump_counter += 1
        if self.__jump_counter >= self.__MAX_JUMP:
            self.__is_jumping = False
            self.__jump_counter = 0

    def is_jumping(self):
        return self.__is_jumping

    def fall(self):
        if not self.__is_jumping:
            self.y += self.vy * DELTA_T

    def get_direction(self):
        return self.__direction

    def turn_around(self):
        if self.__direction == "right":
            self.__direction = "left"
        elif self.__direction == "left":
            self.__direction = "right"

    def get_x_list(self):
        result = []
        for i in range(PLAYER_WIDTH + 1):
            result.append(self.x + i)
        return result

    def get_y_list(self):
        result = []
        for i in range(PLAYER_HEIGHT + 1):
            result.append(self.y + i)
        return result

    def get_right_x(self):
        return self.x + PLAYER_WIDTH

    def is_crash(self, obj):
        is_x_equals = False
        for i in range(int(self.vx * DELTA_T+0.5)):
            if self.x + PLAYER_WIDTH + i in obj.get_x_list():
                is_x_equals = True
            if self.x - i in obj.get_x_list():
                is_x_equals = True

        is_y_equals = False
        for i in range(int(self.vy*DELTA_T+0.5)):
            if self.y + PLAYER_HEIGHT + i in obj.get_y_list():
                is_y_equals = True
            if self.y - i in obj.get_y_list():
                is_y_equals = True

        return is_x_equals and is_y_equals

class Monster:
    def __init__(self, x, y, vx, vy, monster_type):
        self.__images = [pygame.image.load(monster_type + '1.png').convert(),
                         pygame.image.load(monster_type + '2.png').convert()]
        if monster_type == "mushroom":
            self.__width = MUSHROOM_WIDTH
            self.__height = MUSHROOM_HEIGHT

        self.__current_image = self.__images[0]
        self.__FIRST_X = x
        self.__FIRST_Y = y
        self.__x = x
        self.__y = y
        self.__vx = vx
        self.__vy = vy
        self.__direction = "left"
        self.__monster_type = monster_type
        self.__counter = 0

    def get_image(self):
        self.__counter += 1
        if self.__counter > FRAMES_DELAY:
            self.__counter = 0

        if self.__monster_type == "mushroom" and self.__counter == FRAMES_DELAY:
            if self.__current_image == self.__images[1]:
                self.__current_image = self.__images[0]
                self.__current_image.set_colorkey(PINK)
            else:
                self.__current_image = self.__images[1]
                self.__current_image.set_colorkey(PINK)
            return self.__current_image
        elif self.__monster_type == "mushroom" and self.__counter != FRAMES_DELAY:
            self.__current_image.set_colorkey(PINK)
            return self.__current_image

    def get_x(self):
        return self.__x

    def get_vx(self):
        return self.__vx

    def get_vy(self):
        return self.__vx

    def get_y(self):
        return self.__y

    def step(self):
        if self.__direction == "right":
            self.__x += self.__vx * DELTA_T
        elif self.__direction == "left":
            self.__x -= self.__vx * DELTA_T

    def get_direction(self):
        return self.__direction

    def turn_around(self):
        if self.__direction == "right":
            self.__direction = "left"
        elif self.__direction == "left":
            self.__direction = "right"

    def get_x_list(self):
        result = []
        for i in range(MUSHROOM_WIDTH + 1):
            result.append(self.__x + i)
        return result

    def get_y_list(self):
        result = []
        for i in range(MUSHROOM_HEIGHT + 1):
            result.append(self.__y + i)
        return result

    def is_crash(self, obj):
        is_x_equals = False
        for i in range(int(self.__vx * DELTA_T+0.5)):
            if self.__x + self.__width + i in obj.get_x_list():
                is_x_equals = True
            if self.__x - i in obj.get_x_list():
                is_x_equals = True

        is_y_equals = False
        for i in range(int(self.__vy * DELTA_T+0.5)):
            if self.__y + self.__height + i in obj.get_y_list():
                is_y_equals = True
            if self.__y - i in obj.get_y_list():
                is_y_equals = True

        return is_x_equals and is_y_equals

class Block:
    def __init__(self, x, y, width=1, height=1):
        self.__image = pygame.image.load("block.png")
        self.__x = x
        self.__y = y
        self.__width = width
        self.__height = height

    def load_image(self, background_x):
        for i in range(self.__width):
            for j in range(self.__height):
                SCREEN.blit(self.__image, (self.__x + (i * BLOCK_WIDTH) + background_x, self.__y + (j * BLOCK_HEIGHT)))

    def get_x_list(self):
        result = []
        for i in range(BLOCK_WIDTH * self.__width + 1):
            result.append(self.__x + i)
        return result

    def get_y_list(self):
        result = []
        for i in range(BLOCK_HEIGHT * self.__height + 1):
            result.append(self.__y + i)
        return result

    def get_y(self):
        return self.__y

    def get_x(self):
        return self.__x

    def get_width(self):
        return self.__width

    def get_height(self):
        return self.__height

    def get_bottom_y(self):
        return self.__y + (BLOCK_HEIGHT * self.__height)

    def get_right_x(self):
        return self.__x + self.__width * BLOCK_WIDTH

player = Player(140, FLOOR_Y - PLAYER_HEIGHT)
blocks = [Block(270, 280, 1, 1), Block(301, FLOOR_Y - BLOCK_HEIGHT * 8, 1, 8),
          Block(600, FLOOR_Y - BLOCK_HEIGHT * 8, 2, 8)]
monsters = [Monster(380, FLOOR_Y - MUSHROOM_HEIGHT, 3/33., 3/33., "mushroom")]

def main():
    global DELTA_T

    finish = False
    background_x = 0
    background_y = 0
    background = pygame.image.load(BACKGROUND_IAMGE)

    while not finish:
        DELTA_T = clock.tick(REFRESH_RATE)
        SCREEN.blit(background, (background_x, background_y))
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                finish = True

        block_on_right = False
        block_on_left = False
        is_player_on_block = False
        for block in blocks:
            block.load_image(background_x)
            for i in range(int(player.vy * DELTA_T+0.5)):
                for x in player.get_x_list():
                    if player.get_bottom_y() + i == block.get_y() and x in block.get_x_list():
                        is_player_on_block = True
                        player.y += i
                    if player.y - i == block.get_bottom_y() and x in block.get_x_list():
                        player.set_jump(False)
                        player.y -= i

            for i in range(int(player.vx*DELTA_T+0.5)):
                for y in player.get_y_list():
                    if player.get_right_x() + i == block.get_x() and y in block.get_y_list():
                        block_on_right = True
                        player.x += (i - 1)
                        background_x -= (i - 1)
                    if player.x - i == block.get_right_x() and y in block.get_y_list():
                        block_on_left = True
                        player.x -= (i - 1)
                        background_x += (i - 1)
            for monster in monsters:
                if monster.is_crash(block):
                    monster.turn_around()

        keys = pygame.key.get_pressed()

        if (keys[pygame.K_UP] or keys[pygame.K_SPACE] or keys[pygame.K_w]) and not player.is_mid_air():
            player.set_jump(True)

        if (keys[pygame.K_RIGHT] or keys[pygame.K_d]) and not block_on_right:
            if player.get_direction() != "right":
                player.turn_around()
            player.set_walk(True)

            background_x -= player.vx * DELTA_T
            player.x += player.vx * DELTA_T

        if (keys[pygame.K_LEFT] or keys[pygame.K_a]) and not block_on_left:
            if player.get_direction() != "left":
                player.turn_around()
            player.set_walk(True)

            if background_x != 0:
                background_x += player.vx * DELTA_T
                player.x -= player.vx * DELTA_T

        if not any(keys):
            player.stand_still()
            player.set_walk(False)

        for monster in monsters:
            monster.step()
            SCREEN.blit(monster.get_image(), (background_x + monster.get_x(), monster.get_y()))

        is_player_on_ground = False
        for i in range(int(player.vy * DELTA_T+0.5)):
            if player.get_bottom_y() + i == FLOOR_Y:
                player.y += i
                is_player_on_ground = True

        if is_player_on_block or is_player_on_ground:
            player.set_is_mid_air(False)
        else:
            player.set_is_mid_air(True)
            player.fall()

        player.check_jump()

        player_image = player.get_image().convert()
        player_image.set_colorkey(PINK)

        SCREEN.blit(player_image, (player.x + background_x, player.y))
        pygame.display.flip()

    pygame.quit()

if __name__ == '__main__':
    main()

【讨论】:

  • click.tick() 是做什么的?
  • 来自:pygame.org/docs/ref/time.html#pygame.time.Clock.tick clock.tick() 将游戏时钟提前自上次调用滴答声后经过的时间。如果您提供一个参数(刷新率),它将暂停您的游戏,直到下一帧的时间到来。换句话说,当不带参数调用它时,它会将您的“所需”帧速率设置为无穷大,您不会等到下一帧的时间到来,而是立即显示它。
  • 所以如果clock.tick() 让游戏变得流畅,我应该就这样吗?
  • @yoav 无论如何,我认为加载图像可能是缓慢的部分,因此应该在进入游戏循环之前完成,而不是在每一帧都这样做。在游戏循环期间,您应该只移动精灵而不是重新加载图像。
  • @yoav 是的,你可以。但我怀疑它会。
猜你喜欢
  • 2016-06-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多