我正在运行一个Python游戏,每帧在屏幕上绘制大约2k个矩形。
我的问题是它以12 fps的速度运行,我不知道如何解决该问题。当我删除所有矩形时,它会变为100 fps。我不会一次渲染所有这些,但是只有那些相机当前可以看到。如何解决这个滞后尖峰问题,是因为我使用的是pygame矩形还是因为我使用的是错误的矩形?
这是代码
import pygame
black = (0,0,0)
pygame.init()
gameDisplay = pygame.display.set_mode((0,0),pygame.FULLSCREEN)
gameDisplay.fill(black)
gameDisplay.convert()
clock = pygame.time.Clock()
display_width = 1920
display_height = 1080
from opensimplex import OpenSimplex
tmp = OpenSimplex()
dimensions = [100,100]
size = 40
def mapping(x):
y = (x + 1) * 10 + 40
return y
class GroundCell:
def __init__(self,x,y,dim):
self.x = x
self.y = y
self.dim = dim
tempcells = []
allCells = []
for a in range(0,dimensions[0]):
tempcells = []
for b in range(0,dimensions[1]):
tempcells.append(GroundCell(a*size,b*size,mapping(tmp.noise2d(a*0.11,b*0.11))))
allCells.append(tempcells)
font = pygame.font.Font("freesansbold.ttf", 20)
while True:
for event in pygame.event.get():
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
pygame.quit()
quit()
for a in allCells:
for b in a:
if b.x in range(0,display_width) \
and b.y in range(0,display_height) or\
b.x+size in range(0,display_width) \
and b.y+size in range(0,display_height) :
pygame.draw.rect(gameDisplay,(b.dim,b.dim,b.dim),(b.x,b.y,size,size))
fps = font.render('FPS: ' + str(int(clock.get_fps())), 1, (0, 0, 0))
gameDisplay.blit(fps, (20, 20))
pygame.display.update()
clock.tick(120)
gameDisplay.fill(black)
答案 0 :(得分:1)
正如@sloth所解释的,有更好的方法可以执行此操作,但是如果您想知道实际的问题是什么,
您没有绘制2000个矩形,而是绘制了10.000个矩形,因为尺寸为100x100
您正在检查矩形是否在性能方面以最坏的方式可见。只需检查一下,如果不留下支票并且不绘制矩形就会发生什么。您会看到性能有所提高,但距离120fps仍然很远。这是因为对于每个矩形,您会生成一个从0
到屏幕宽度的数字列表,以及另一个从零到屏幕高度的数字列表。您也要这样做两次。也就是说,在1920x1080
屏幕上:(1920 * 10000) + (1920 * 10000) + (1080 * 10000) + (1080*10000)
= 60000000
。因此,有60张百万支票。如果您具有120fps,则意味着每秒有6000万* 120 = 7.2 十亿次检查。
只需将支票更改为类似于if b.x+size < display_width and b.y+size < display_height and b.x > 0 and b.y > 0:
的东西,便可以提高性能。
也就是说,它仍然是10000个矩形,仍然是120fps,这意味着每秒1200000个矩形,基本上没有硬件加速并且使用了高级语言。不要指望最好的性能。
答案 1 :(得分:0)
而不是每隔一个刻度便将所有这些矩形绘制到屏幕上,而是创建具有噪声一次的Surface,然后重新使用它。
更多注意事项:
文本渲染很昂贵。如果您使用大量文本呈现,则最好缓存Font.render
创建的表面。请注意,pygame中还有一个新的freetype
模块,它在各个方面都比font
模块好。
您对屏幕内是否有矩形的检查非常奇怪。您可以只使用if 0 < b.x < display_width ...
之类的东西,甚至可以使用pygame的Rect
类,该类提供了不错的方法,例如contains()
。
一种根据任意数据创建Surface的好方法是使用numpy
和pygame的surfarray
模块。不要被吓到,使用起来并不难。
这是一个基于您的代码的运行示例:
import pygame
import numpy as np
black = (0,0,0)
pygame.init()
display_width = 1000
display_height = 1000
gameDisplay = pygame.display.set_mode((display_width, display_height))
gameDisplay.fill(black)
clock = pygame.time.Clock()
from opensimplex import OpenSimplex
tmp = OpenSimplex()
dimensions = [100,100]
size = 16
def mapping(x):
y = (x + 1) * 10 + 40
return y
# create an 2d array from the noise
def get_array():
rgbarray = np.zeros((dimensions[0], dimensions[1]))
for x in range(dimensions[0]):
for y in range(dimensions[1]):
c = int(mapping(tmp.noise2d(x*0.11, y*0.11)))
# simple way to convert the color value to all three (r,g,b) channels
rgbarray[x, y] = c | c << 8 | c << 16
return rgbarray
# create the array and copy it into a Surface
data = get_array()
surface = pygame.Surface((dimensions[0], dimensions[1]))
pygame.surfarray.blit_array(surface, data)
# scale the Surface to the desired size
scaled = pygame.transform.scale(surface, (dimensions[0]*size, dimensions[1]*size))
# simple way to cache font rendering
font = pygame.font.Font("freesansbold.ttf", 20)
cache = {}
def render(text):
if not text in cache:
cache[text] = font.render(text, 1, (0, 0, 0))
return cache[text]
x = 0
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
running = False
x-=1
if x < -1000:
x = 0
gameDisplay.blit(scaled, (x, 0))
fps = render('FPS: ' + str(int(clock.get_fps())))
gameDisplay.blit(fps, (20, 20))
pygame.display.update()
clock.tick(120)