python - Python pygame如何设置FPS
问题描述
我正在制作一个 python 游戏,但我不知道应该将我的 FPS 设置为什么。我的游戏卡住了,不流畅。我怎么知道 FPS 应该是什么?
这是我的代码:
解决方案
运行您的代码后,我还注意到帧速率下降,这会损害游戏的流畅性。
这里有两个单独的问题:
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下降时游戏变得不流畅。
您的对象的x
和y
位置正在像这样更新,例如: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
在每一帧中准确地推进“细胞”以检测碰撞和地板穿透。请参阅下面的尝试:
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()
推荐阅读
- active-directory - 静默重新验证 Active Directory 帐户
- flutter - 在 FutureBuilder 调用的异步函数中捕获同步错误
- java - 如何使用 mockito 模拟 LocalDatTime.now()?
- python - 将特定频道中的 YouTube 视频 url 抓取到 mysql
- java - 通过 HTTP 端点的 CORDA 启动流程
- c++ - Google Kickstart Wandering Robot 错误答案
- c# - 如何使用 WPF 中的代码绑定 DataGridTextColumn 的可见性属性?
- sql - 如何使用内连接连接 SQL Server 2008 中的所有行?
- angular - 无法编译入口点@angular/material/select
- java - 黄瓜Java最新版本