我有这个应用程序,正在创建一个名为“比萨点击器”的Cookie点击器的替代版本。它非常基本,但是运行速度非常慢,我不知道为什么。
import pyglet
window = pyglet.window.Window(fullscreen=True, caption="Click For Pizzas", resizable=True)
win_width = window.width
win_height = window.height
window.set_fullscreen(False)
window.set_size(win_width, win_height)
image_height = round(int(win_width/5)/1.4, 1)
class Main(object):
def __init__(self):
self.label = pyglet.text.Label('Pizzas: 0', font_size=100, color=(0, 0, 0, 255),
x=win_width//2, y=win_height - 100,
anchor_x='left', anchor_y='top')
self.points = 0
self.number = 1
def background(self):
background_img = pyglet.resource.image('pizza_clicker.png')
background_img.width = (win_width/5)*4
background_img.height = win_height
background_img.blit(int(win_width/5), 0, 0.5)
def drawSidebar(self):
width = int(win_width/5)
height = int(win_height)
sidebar_pattern = pyglet.image.SolidColorImagePattern(color=(100, 100, 100, 100))
sidebar = sidebar_pattern.create_image(width, height)
sidebar.blit(0, 0)
pizza = []
images = ('pizza_1.png', 'pizza_5.png', 'pizza_5.png', 'pizza_5.png')
for i in range (0, len(images)):
divideby = 1.4 / (i + 1)
pizza.append(pyglet.resource.image(images[i]))
pizza[i].width = width
pizza[i].height = round(width/1.4, 1)
pizza[i].blit(0, window.height - (round(width/divideby, 1)))
def getNumber(self, y):
if y > window.height - int(image_height):
self.number = 1
elif y > window.height - (int(image_height)*2):
self.number = 5
elif y > window.height - (int(image_height)*3):
self.number = 10
elif y > window.height - (int(image_height)*4):
self.number = 20
def addPoint(self):
self.points += self.number
self.label.text = 'Pizzas: %s' %self.points
@window.event
def on_mouse_press(x, y, buttons, modifiers):
if buttons & pyglet.window.mouse.LEFT and x > win_width/5:
main.addPoint()
elif buttons & pyglet.window.mouse.LEFT and x < win_width/5:
main.getNumber(y)
@window.event
def on_draw():
window.clear()
main.background()
main.label.draw()
main.drawSidebar()
main = Main()
pyglet.app.run()
所以问题是当我单击窗口的右侧时,它应该立即添加一个(或多个)点,但会滞后几秒钟。而且,只是没有人感到困惑,代码确实起作用了,但是速度很慢。我该怎么解决?
答案 0 :(得分:1)
在每个draw()
迭代中,您正在执行以下操作:
background_img = pyglet.resource.image('pizza_clicker.png')
这意味着您从硬盘驱动器加载每个渲染序列的同一张图片。您还要对不同的比萨饼图像进行for循环,还可以从硬盘驱动器中获取它们:
for i in range (0, len(images)):
divideby = 1.4 / (i + 1)
pizza.append(pyglet.resource.image(images[i]))
我强烈建议您阅读资源的加载方式,并使用cProfiler分析器。
here是如何配置代码的一个很好的例子。 由于这是一个外部资源,因此我还将包括两个同样不错的SO链接(但效果不佳或无法自我解释):
这是tl-dr版本:
python -m cProfile -o profile_data.pyprof your_script.py
pyprof2calltree -i profile_data.pyprof -k
这应该呈现一个所谓的“调用树”,其中包含您的代码执行的所有执行,花费了多长时间以及消耗了多少内存。从应用程序的开始到底部。
但是,我强烈建议您执行 1个渲染顺序,并在第一个渲染之后添加exit(1)
。只是为了让您配置1次运行,而不是每秒60次。
搜索关键字以了解为什么代码缓慢:Python,分析,kcachegrind,cprofile,cprofiling,调用栈。
要解决大多数问题,请将所有I / O密集型操作(加载图像,创建形状等)移至主类的__init__
调用中。
最终产品看起来像这样:
class Main(object):
def __init__(self):
self.label = pyglet.text.Label('Pizzas: 0', font_size=100, color=(0, 0, 0, 255),
x=win_width//2, y=win_height - 100,
anchor_x='left', anchor_y='top')
self.points = 0
self.number = 1
self.background_img = pyglet.resource.image('pizza_clicker.png')
self.background_img.width = (win_width/5)*4
self.background_img.height = win_height
sidebar_pattern = pyglet.image.SolidColorImagePattern(color=(100, 100, 100, 100))
self.sidebar = sidebar_pattern.create_image(width, height)
self.pizzas = []
width = int(win_width/5)
height = int(win_height)
self.pizza_images = ('pizza_1.png', 'pizza_5.png', 'pizza_5.png', 'pizza_5.png')
for i in range (0, len(pizza_images)):
resource = pyglet.resource.image(pizza_images[i])
resource.width = width
resource.height = round(width/1.4, 1) # Not sure why you're using width here.. meh.. keeping it -yolo-
self.pizzas.append(resource)
def background(self):
self.background_img.blit(int(win_width/5), 0, 0.5)
def drawSidebar(self):
width = int(win_width/5)
height = int(win_height) # You're using win_height here, but window.height later. It's strange.
self.sidebar.blit(0, 0)
for i in range (0, len(self.pizza_images)):
divideby = 1.4 / (i + 1)
self.pizzas[i].blit(0, window.height - (round(width/divideby, 1)))
def getNumber(self, y):
if y > window.height - int(image_height):
self.number = 1
elif y > window.height - (int(image_height)*2):
self.number = 5
elif y > window.height - (int(image_height)*3):
self.number = 10
elif y > window.height - (int(image_height)*4):
self.number = 20
def addPoint(self):
self.points += self.number
self.label.text = 'Pizzas: %s' %self.points
但是为什么在这里停下来,这里大量使用blit
。 Blit适用于一两个对象。但是,很快就很难跟踪将什么“渗墨”到什么地方。您还将在循环和填充中进行除法,加法和其他类型的计算。
请记住,循环是渲染的魔鬼。
如果您在某个地方出现了循环,则几乎可以肯定地开始在其中查找性能问题(任何人都在查看此注释并转到“ pff他不知道他在说什么”)。是的,我知道,但这是一个很好的初学者提示)。
我强烈建议您改为将图像放入pyglet.sprite.Sprite()对象中。它们跟踪位置,渲染,最重要的是,它们支持batched rendering。那是你祖国的圣杯!如果有什么方法可以在pyglet中节省性能方面的知识,那么3d渲染通常就是批处理渲染。
看,图形卡的设计只考虑了一件事情。采用一个巨大的数学方程式,并将其整个吞下。它特别擅长收集大量信息,然后将其拍摄到屏幕上。它不擅长多个命令。这意味着,如果要向图形卡发送来回的许多较小的数据包,则由于开销和其他因素,它不会表现出接近最佳的磨损。
因此,将图像放入精灵中,然后将这些精灵成批,而不使用任何for循环和渲染资源负载。
这是您的代码的样子:
class Main(object):
def __init__(self):
self.label = pyglet.text.Label('Pizzas: 0', font_size=100, color=(0, 0, 0, 255),
x=win_width//2, y=win_height - 100,
anchor_x='left', anchor_y='top')
self.points = 0
self.number = 1
self.background_layer = pyglet.graphics.OrderedGroup(0)
self.foreground_layer = pyglet.graphics.OrderedGroup(1)
self.batch = pyglet.graphics.Batch()
self.background_img = pyglet.sprite.Sprite(pyglet.resource.image('pizza_clicker.png'), batch=self.batch, group=self.background_layer)
self.background_img.width = (win_width/5)*4
self.background_img.height = win_height
self.background.x = int(win_width/5)
self.background.y = 0
sidebar_pattern = pyglet.image.SolidColorImagePattern(color=(100, 100, 100, 100))
self.sidebar = pyglet.sprite.Sprite(sidebar_pattern.create_image(width, height), batch=self.batch, group=self.background_layer)
self.sidebar.x = 0
self.sidebar.y = 0
self.pizzas = []
width = int(win_width/5)
height = int(win_height)
self.pizza_images = ('pizza_1.png', 'pizza_5.png', 'pizza_5.png', 'pizza_5.png')
for i in range (0, len(pizza_images)):
divideby = 1.4 / (i + 1)
resource = pyglet.sprite.Sprite(pyglet.resource.image(pizza_images[i]), batch=self.batch, group=self.foreground_layer)
resource.width = width
resource.height = round(width/1.4, 1) # Not sure why you're using width here.. meh.. keeping it -yolo-
resource.x = 0
resource.y = window.height - (round(width/divideby, 1))
self.pizzas.append(resource)
def draw(self):
# This is instead of doing:
# - self.background.draw()
# - self.sidebar.draw()
# - self.pizzas[i].draw()
self.batch.draw()
self.label.draw() # You could put this in a batch as well :)
def getNumber(self, y):
if y > window.height - int(image_height):
self.number = 1
elif y > window.height - (int(image_height)*2):
self.number = 5
elif y > window.height - (int(image_height)*3):
self.number = 10
elif y > window.height - (int(image_height)*4):
self.number = 20
def addPoint(self):
self.points += self.number
self.label.text = 'Pizzas: %s' %self.points
@window.event
def on_draw():
window.clear()
main.draw()
现在,代码并不完美。但这有望使您对应该前进的方向有所了解。我也没有执行此代码,主要是因为我没有所有的披萨图像或时间。我可能会回到这里,然后整理一下我最有可能的拼写错误。