Pyglet App运行缓慢

问题描述 投票:1回答:1

我有这个应用程序正在创建一个名为pizza clicker的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()

所以问题是,当我点击窗口的右侧时,它应该立即添加一个点(或许多点),但它会滞后几秒钟。此外,只是让没有人感到困惑,代码确实有效,但只是缓慢。我该怎么做才能解决它?

python pyglet
1个回答
1
投票

在每个draw()迭代中,您正在做:

background_img = pyglet.resource.image('pizza_clicker.png')

这意味着您将从硬盘驱动器和每个渲染序列加载到相同的图片中。你也在不同的披萨图片上做一个循环,你也可以从硬盘中取出它们:

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,profiling,kcachegrind,cprofile,cprofiling,callstack。

剧透警报

要解决大多数问题,请将所有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()

现在,代码并不完美。但它有望让你了解你应该走向何方。我也没有执行此代码,主要是因为我没有所有披萨图片或时间。我可能会回到这里并且这样做,并整理(最可能的)拼写错误。

© www.soinside.com 2019 - 2024. All rights reserved.