简体   繁体   English

Python pygame 如何设置FPS

[英]Python pygame how to set the FPS

I am making a python game and I don't know what should I set my FPS to.我正在制作一个 python 游戏,但我不知道我应该将 FPS 设置为什么。 My game is stuck and not smooth.我的游戏卡住了,不流畅。 How do I know what should the FPS be?我怎么知道 FPS 应该是多少?

This is my code:这是我的代码:

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

After running your code I have also noticed frame rate drops which hurt the smoothness of the game.运行您的代码后,我还注意到帧速率下降会影响游戏的流畅性。

There are two separate issues here:这里有两个不同的问题:

1. The FPS drops 1. FPS 下降

The FPS drops probably happen because of something you cannot control, like the garbage collector working. FPS 下降可能是由于您无法控制的事情而发生的,例如垃圾收集器的工作。 Even though you have no control of such issues, you can in general improve the performance of your game.即使您无法控制此类问题,您也可以总体上提高游戏的性能。 See the following screen shot of a profiler run of your game:请参阅以下游戏分析器运行的屏幕截图: 探查器结果

You can see that the largest part of the time is spent on blit .您可以看到大部分时间都花在了blit However, a very large part of the time is also being spent in get_y_list .但是,很大一部分时间也花在了get_y_list The get_y_list method also uses large lists which create a lot of garbage for the garbage collector to later collect, thus causing a further performance hit. get_y_list方法还使用大列表,这会为垃圾收集器稍后收集创建大量垃圾,从而进一步降低性能。

As I understand it, the get_y_list method is part of a very crude method that you're using for collision detection, one which basically takes quadratic time.据我了解, get_y_list方法是您用于碰撞检测的一种非常粗糙的方法的一部分,该方法基本上需要二次时间。 Your algorithm seems to divide each object into a large amount of 2d cells, and then test collision between each pair of cells.您的算法似乎将每个对象划分为大量 2d 单元格,然后测试每对单元格之间的碰撞。 Instead you can just test box to box intersection.相反,您可以只测试框到框的交集。 If you wish to have a complicated collision shape for the objects you can use other algorithms, the internet is full of them.如果您希望对象具有复杂的碰撞形状,您可以使用其他算法,互联网上到处都是。 See this for example: https://developer.mozilla.org/en-US/docs/Games/Techniques/2D_collision_detection例如,请参见: https : //developer.mozilla.org/en-US/docs/Games/Techniques/2D_collision_detection

Using other algorithms for collision detection will greatly improve your performance.使用其他算法进行碰撞检测将大大提高您的性能。

2. The game becoming non-smooth when there's an FPS drop. 2. FPS下降时游戏变得不流畅。

The x and y positions of your objects are being updated like this for example: player.x -= player.vx .对象的xy位置正在更新,例如: player.x -= player.vx The physically correct method would be: player.x -= player.vx * DELTA_T .物理上正确的方法是: player.x -= player.vx * DELTA_T Where DELTA_T is the time elapsed since the last frame.其中 DELTA_T 是自上一帧以来经过的时间。 If you don't use the time elapsed since the last frame, the speed of motion of your characters becomes dependent on FPS.如果您不使用自上一帧以来经过的时间,则角色的运动速度将取决于 FPS。 Which is exactly what we're seeing.这正是我们所看到的。

Where do I get DELTA_T you might ask.你可能会问我从哪里得到DELTA_T You do it directly when calling tick:调用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)

I tried adding the multiplication by DELTA_T but the game becomes unstable, because you assume that objects advance exactly vx 'cells' in each frame to detect collisions and floor penetrations.我尝试通过DELTA_T添加乘法,但游戏变得不稳定,因为您假设对象在每一帧中精确推进vx 'cells' 以检测碰撞和地板穿透。 See my attempt below:请参阅下面的我的尝试:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM