Python Pyglet图像渲染
我正在为我的第一个深入的Pyglet项目制作一种2D Minecraft克隆,我遇到了一个问题。每当我在屏幕上有相当数量的块时,帧速率就会急剧下降 以下是我的渲染方法: 我使用一个字典,其中键是一个元组(表示块的坐标),项是一个纹理 我循环浏览整个字典并渲染每个块:Python Pyglet图像渲染,python,graphics,rendering,pyglet,Python,Graphics,Rendering,Pyglet,我正在为我的第一个深入的Pyglet项目制作一种2D Minecraft克隆,我遇到了一个问题。每当我在屏幕上有相当数量的块时,帧速率就会急剧下降 以下是我的渲染方法: 我使用一个字典,其中键是一个元组(表示块的坐标),项是一个纹理 我循环浏览整个字典并渲染每个块: for key in self.blocks: self.blocks[key].blit(key[0] * 40 + sx,key[1] * 40+ sy) p.S.sx和sy是屏幕滚动的坐标偏移 我想知道是否有一种方法
for key in self.blocks:
self.blocks[key].blit(key[0] * 40 + sx,key[1] * 40+ sy)
p.S.sx和sy是屏幕滚动的坐标偏移
我想知道是否有一种方法可以更有效地渲染每个块。我将尽全力解释为什么以及如何在不知道代码外观的情况下选择代码 我会假设你有以下几点:
self.blocks['monster001'] = pyglet.image.load('./roar.png')
如果你想加载一个你不想处理的静态图像,这一切都很好。然而,你正在制作一个游戏,你将使用更多的精灵和对象,而不仅仅是一个简单的图像文件
现在,共享对象、批处理和精灵就在这里派上了用场。
首先,把你的图像输入一个精灵,这是一个好的开始
sprite = pyglet.sprite.Sprite(pyglet.image.load('./roar.png'))
sprite.draw() # This is instead of blit. Position is done via sprite.x = ...
现在,draw比.blit()
快得多,原因有很多,但我们现在跳过为什么,只需坚持极速升级即可
同样,这仅仅是迈向成功帧率的一小步(除了有限的硬件ofc..duh)
不管怎样,请返回pew pew查看升级后的代码。现在,您还需要将精灵添加到批处理中,这样您就可以一次性(读取:批处理)同时渲染大量内容,而不是手动将内容推送到图形卡上图形卡soul purpose的设计目的是能够在计算中以惊人的速度处理千兆位的吞吐量,而不是处理多个小型I/O 为此,您需要创建一个批处理容器。并向其添加“层”。
其实很简单,你需要做的就是:
main_batch = pyglet.graphics.Batch()
background = pyglet.graphics.OrderedGroup(0)
# stuff_above_background = pyglet.graphics.OrderedGroup(1)
# ...
现在我们将使用批处理,您可能不需要更多的批处理来实现此学习目的。好了,你拿到了你的那批货,现在怎么办?好吧,现在我们尽我们最大的努力把你的显卡掐死,看看我们是否能在压力下扣上它。(在这个过程中没有图形车受到伤害,请不要掐死东西。 哦,还有一件事,还记得关于共享对象的注释吗?好的,我们将在这里创建一个共享图像对象,并将其推入精灵中,而不是每。。单身。。。时间<代码>怪物图片我们称之为
monster_image = pyglet.image.load('./roar.png')
for i in range(100): # We'll create 100 test monsters
self.blocks['monster'+str(i)] = pyglet.sprite.Sprite(imgage=monster_image, x=0, y=0, batch=main_batch, group=background)
现在,您已经创建了100个
怪物,并将其添加到批main\u批
子组background
。非常简单
这是kicker,而不是调用self.blocks[key].blit()
或.draw()
,我们现在可以调用main\u batch.draw()
,它将把每一个怪物发射到图形卡上,创造奇迹
好的,现在您已经优化了代码的速度,但是如果您正在制作一个游戏,那么从长远来看,这对您并没有帮助。或者在本例中,为您的游戏提供一个图形引擎。你想做的是进入大联盟,使用课程。如果您之前感到惊讶,您可能会大惊小怪,一旦您完成了,您的代码将看起来多么棒
好的,首先,您想为屏幕上的对象创建一个基类,让我们在baseSprite
中调用现在你需要和Pyglet一起解决一些问题,当继承
Sprite
对象时,尝试设置image
会在处理东西时导致各种不确定的小故障和bug,因此我们将直接设置self.texture
,这基本上是一样的,但我们将挂钩到pyglet库变量中;皮皮,皮,呵呵
现在这是您的基础,您可以通过以下操作创建怪物:
main_batch = pyglet.graphics.Batch()
background = pyglet.graphics.OrderedGroup(0)
monster_image = pyglet.image.load('./roar.png')
self.blocks['monster001'] = baseSprite(monster_image, 10, 50, main_batch, background)
self.blocks['monster002'] = baseSprite(monster_image, 70, 20, main_batch, background)
...
main_batch.draw()
如何,您可能会使用其他所有人都在使用的默认的@on\u window\u draw()
示例,这很好,但我发现它很慢,很难看,而且从长远来看不实用。你想做面向对象的编程。。对吗?这就是它的名字,我称之为可读代码,你喜欢看一整天。简称RCTYLTWADL。
要做到这一点,我们需要创建一个模仿Pyglet行为的
类
,并依次调用它的后续函数并轮询事件处理程序,否则sh**会卡住,相信我。。做了几次,瓶颈很容易创造。但我的错误已经够多了,这里有一个基本的
main
类,您可以使用它来使用基于轮询的事件处理,从而将刷新率限制在编程中,而不是Pyglet中的内置行为
class main(pyglet.window.Window):
def __init__ (self):
super(main, self).__init__(800, 800, fullscreen = False)
self.x, self.y = 0, 0
self.sprites = {}
self.batches = {}
self.subgroups = {}
self.alive = 1
def on_draw(self):
self.render()
def on_close(self):
self.alive = 0
def render(self):
self.clear()
for batch_name, batch in self.batches.items():
batch.draw()
for sprite_name, sprite in self.sprites.items():
sprite._draw()
self.flip() # This updates the screen, very much important.
def run(self):
while self.alive == 1:
self.render()
# -----------> This is key <----------
# This is what replaces pyglet.app.run()
# but is required for the GUI to not freeze.
# Basically it flushes the event pool that otherwise
# fill up and block the buffers and hangs stuff.
event = self.dispatch_events()
x = main()
x.run()
一些额外的东西,我添加了GL选项,通常对你有好处。
我还添加了sa FPS限制器,您可以修补和玩
编辑:
批量更新
由于sprite对象可以通过将其全部发送到图形卡一次完成大规模渲染,因此类似地,您也希望进行批量更新。
例如,如果您想更新每个对象的位置、颜色或任何可能的内容
这是聪明的编程发挥作用的地方,而不是漂亮的小工具。看,我在编程方面所做的一切。。如果你想的话 假设(在代码顶部)有一个变量,名为:
global_settings = {'player position' : (50, 50)}
# The player is at X cord 50 and Y cord 50.
在基本精灵中,您可以简单地执行以下操作:
class baseSprite(pyglet.sprite.Sprite):
def __init__(self, texture, x, y, batch, subgroup):
self.texture = texture
super(baseSprite, self).__init__(self.texture, batch=batch, group=subgroup)
self.x = x + global_settings['player position'][0]#X
self.y = y + global_settings['player position'][1]#Y
请注意,您必须稍微调整draw()
(注意,不是\u draw()
,因为批处理渲染将调用draw
而不是\u draw
)函数,以便按照渲染序列执行和更新位置更新。或者您可以创建一个新类,继承baseSprite
,并且只更新这些类型的sprite:
class monster(baseSprite):
def __init__(self, monster_image, main_batch, background):
super(monster, self).__init__(imgage=monster_image, x=0, y=0, batch=main_batch, group=background)
def update(self):
self.x = x + global_settings['player position'][0]#X
self.y = y + global_settings['player position'][1]#Y
一个
class baseSprite(pyglet.sprite.Sprite):
def __init__(self, texture, x, y, batch, subgroup):
self.texture = texture
super(baseSprite, self).__init__(self.texture, batch=batch, group=subgroup)
self.x = x + global_settings['player position'][0]#X
self.y = y + global_settings['player position'][1]#Y
class monster(baseSprite):
def __init__(self, monster_image, main_batch, background):
super(monster, self).__init__(imgage=monster_image, x=0, y=0, batch=main_batch, group=background)
def update(self):
self.x = x + global_settings['player position'][0]#X
self.y = y + global_settings['player position'][1]#Y