Pyglet图像渲染 [英] Pyglet Image Rendering

查看:2634
本文介绍了Pyglet图像渲染的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在为我的第一个深入Pyglet项目开发一种2D Minecraft克隆,并且遇到了一个问题。每当我在屏幕上显示数量不等的块时,帧速率都会大幅下降。

以下是我的渲染方法:
我使用字典,键是一个元组(代表该块的坐标)和该项目是一个纹理。



我遍历整个字典并渲染每个块:

 用于self.blocks中的键值:
self.blocks [key] .blit(key [0] * 40 + sx,key [1 ] * 40+ sy)

PS sx和sy是屏幕滚动的坐标偏移量

我想知道是否有更高效地渲染每个块的方法。

解决方案

我将尽我所能解释为什么以及如何选择代码而不真正知道代码的样子。



我会假设你有以下几点:

  self.blocks ['monster001'] = pyglet.image.load('./ roar.png')

这一切都很好,如果你想加载一个你不想用太多的静态图片。然而,你正在制作一款游戏,并且你将使用更多精灵和对象的地狱,而不仅仅是一个简单的图像文件。



现在,这是共享的地方物体,批次和精灵都派上用场。
首先,将图像输入到精灵,这是一个好的开始。

  sprite = pyglet.sprite.Sprite (pyglet.image.load('./ roar.png'))
sprite.draw()#这是代替blit。位置是通过sprite.x完成的... =

现在,绘制速度更快而不是 .blit(),原因很多,但我们现在将跳过原因并坚持极速升级



再次,这只是朝着成功帧速率迈出的一小步(除了有限的硬件外, duh )。

无论如何,回到pew可以让您的代码升级。

现在您还想要将精灵添加到批处理中,以便同时呈现 LOT (一次性阅读:批次),而不是手动将东西推送到显卡。图形卡的灵魂目的被设计成能够在一次疯狂的快速运行中处理吞吐量的千兆比特计算,而不是处理多个小I / O。



为此,您需要创建一个批量容器。并添加图层。

这非常简单,您只需要做以下操作:

  main_batch = pyglet.graphics.Batch()
background = pyglet.graphics.OrderedGroup(0)
#stuff_above_background = pyglet.graphics.OrderedGroup(1)
#...

现在我们将批量添加一批,您可能不需要更多的用于此学习目的。 >
好​​吧,你有你的批次,现在是什么?那么现在,我们尽最大努力扼杀你的显卡的活力,看看我们是否能够在压力下屈服。(在这个过程中没有图形汽车受到伤害,请不要呛到东西......) / strong>


还有一件事,请记住关于共享对象的注意事项?好吧,我们将在这里创建一个共享图像对象,我们将它推入精灵,而不是每加载一个新图像.. single ... time .. monster_image

  monster_image = pyglet.image.load('./ roar.png')
for i (100):#我们将创建100个测试怪物
self.blocks ['monster'+ str(i)] = pyglet.sprite.Sprite(imgage = monster_image,x = 0,y = 0, batch = main_batch,group = background)

现在你有 100 怪物创建并添加到批次 main_batch 到子组 background 中。



以下是kicker,而不是调用 self.blocks [key] .blit() .draw(),我们现在可以调用 main_batch.draw(),并且它会将每个怪物发射到显卡和产生的奇迹。



好的,现在您已经优化了代码的速度,但从长远来看,重新制作一款游戏。或者在这种情况下,您的游戏的图形引擎。你想要做的就是加入大联盟并使用课程。如果你很惊讶,你可能会放弃你的代码在你完成它之后看起来有多棒的大理石。



首先,你想创建在屏幕上显示对象的基类,可以在 baseSprite 中调用。

现在有一些事情需要用Pyglet解决,当继承 Sprite 对象试图设置 image 时,会在处理东西时导致各种各样的故障和错误所以我们直接设置 self.texture ,这基本上是一样的,但是我们将钩子放入pyglet库变量中,而不是。

  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
self.y = y

def move(self ,x,y):
这个函数只是为了向你展示
如何提高你的基类
更进一步
self.x + = x
self.y + = y

def _draw(self):

通常我们调用_draw()而不是.draw()上的sprites
,因为_draw()将包含比简单的
更多的绘制对象,它可能检查交互或
更新内嵌数据(以及最有可能的定位对象)。

self.draw()

现在这是您的基础,你可以通过这样做来创建怪物:

  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)
$ b $ ...
main_batch.draw()

如何使用默认的 @on_window_draw()示例 else 其他人正在使用,这很好,但是从长远来看,我发现它很慢,很丑并且不实用。你想做面向对象的编程。对吗?

这就是它叫做什么,我把它叫做可读代码,你喜欢整天观看。简单的RCTYLTWADL。



为此,我们需要创建一个 class 模仿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()

f或批处理名称,在self.batches.items()中进行批处理:
batch.draw()

for sprite_name,sprite in self.sprites.items():
sprite._draw ()

self.flip()#这会更新屏幕,非常重要。

def run(self):
while self.alive == 1:
self.render()

#------ ----->这是关键< ----------
#这是什么替换pyglet.app.run()
#但是GUI是不需要冻结的。
#基本上它刷新事件池,否则
#填满并阻塞缓冲区并挂起东西。
event = self.dispatch_events()

x = main()
x.run()

现在这又是一个基本的 main 类,它除了呈现黑色背景以及放入 self.sprites self.batches



注意!我们在精灵上调用 ._ draw(),因为我们之前创建了我们自己的精灵类?是的,这是真棒基础精灵类,您可以在每个精灵完成 draw()之前将自己的东西挂钩。



Anywho,这一切归结为几件事情。


  1. 制作游戏时使用精灵,你的生活将会更容易

  2. 使用批次,您的GPU将会爱上您,并且更新会更加惊人

  3. 使用类和东西,您的眼睛和代码mojo会喜欢你到底在哪里。

下面是一个令人困惑的所有片段的完整工作示例:

 从pyglet.gl导入pyglet 
导入*

glEnable(GL_BLEND)
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
glEnable(GL_LINE_SMOOTH)
glHint(GL_LINE_SMOOTH_HINT,GL_DONT_CARE)

pyglet.clock.set_fps_limit(60)
$ b $ 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
self.y = y

def move(self,x,y):
这个函数只是为了向你展示
如何提高你的基类
更进一步的
self.x + = x
self.y + = y

def _draw(self):

通常我们调用_draw()而不是在精灵
上.draw(),因为_draw()将包含比简单的
更多的绘制对象,它可能检查交互或
更新内嵌数据(并且最有可能的定位对象)。

self.draw()

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._handles = {}

self.batches ['main'] = pyglet.graphics.Batch()
self.subgroups ['base'] = pyglet.graphics.OrderedGroup(0)

monster_image = pyglet.image.load('./ roar.png')
for i in (100):
self._handles ['monster'+ str(i)] = baseSprite(monster_image,randint(0,50),randint(0,50),self.batches ['main'], self.subgroups ['base'])

#注意:我们把这些精灵放在`_handles`中,因为它们将通过
#``self.batches ['main']`批处理,并将它们放置在`self.sprites`中将使
#成为它们的两倍eed保留句柄,这样我们就可以在游戏制作过程的后期对项目使用`.move`和


self.alive = 1

def on_draw(self):
self.render()

def on_close(self):
self.alive = 0

def render(self):
self.clear()

for batch_name,批处理在self.batches.items()中:
batch.draw()

为sprite_name,sprite为self.sprites.items():
sprite._draw()

self.flip()#这更新了屏幕,非常重要。

def run(self):
while self.alive == 1:
self.render()

#------ ----->这是关键< ----------
#这是什么替换pyglet.app.run()
#但是GUI是不需要冻结的。
#基本上它刷新事件池,否则
#填满并阻塞缓冲区并挂起东西。
event = self.dispatch_events()

#有趣的事实:
#如果你想限制你的FPS,这就是你做它的地方
#例如检查这个SO链接:
#http://stackoverflow.com/questions/16548833/pyglet-not-running-properly-on-amd-hd4250/16548990#16548990

x = main()
x.run()



一些奖励的东西,我添加了GL选项,通常为您提供一些有益的东西。
我还添加了一个FPS限制器,您可以进行修补和操作。



编辑:

批处理更新



由于sprite对象可以用于一次性完成大量渲染, d想要分批更新。
例如,如果你想更新每个对象的位置,颜色或任何它可能。



这是聪明的编程发挥作用,而不是漂亮的小工具。
请参阅我在编程中的所有相关内容。如果您想要它。



假设您(在代码的顶部)一个名为:

  global_settings = {'player position':(50,50)} 
#在X线50和Y线50上。

在您的基本精灵中,您可以简单地执行以下操作: / p>

  class baseSprite(pyglet.sprite.Sprite):
def __init __(self,texture,x,y,batch,子组):
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

N请注意,由于批量渲染将调用 draw()(注意,不是 _draw())在绘制而不是 _draw )函数中有一点用来表达和更新每个渲染序列的位置更新。或者你可以创建一个继承 baseSprite 的新类,并且只更新这些类型的精灵:

  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

code .update()在怪物类型类/ sprites。

获得它有点棘手最佳,并有办法解决它,仍然使用批处理渲染,但沿着这些线的某处可能是一个好的开始。







重要提示我从头开始写了很多(不是我第一次在Pyglet中编写GUI类)和w这个* Nix实例找不到我的X服务器..所以不能测试代码。



我会在一个测试中给它一个测试当我下班的时候,但是这给了你在Pyglet制作游戏时想要做什么和想什么的一般想法。请记住,玩得开心,或者在开始之前就放弃了,因为游戏需要时间来制作 ^^



东西,祝你好运!


I'm working on a sort of a 2D Minecraft clone for my first in depth Pyglet project and I've run across a problem. Whenever I have a decent number of blocks on screen, the frame rate drops dramatically.

Here is my rendering method: I use a dictionary with the key being a tuple(which represents the coordinate for the block) and the item being a texture.

I loop through the entire dictionary and render each block:

for key in self.blocks:
    self.blocks[key].blit(key[0] * 40 + sx,key[1] * 40+ sy)

P.S. sx and sy are coordinate offsets for screen scrolling

I would like to know if there is a way to more efficiently render each block.

解决方案

I'm going to do my best to explain why and how to optemize your code without actually knowing what you code looks like.

I will assume you have something along the lines of:

self.blocks['monster001'] = pyglet.image.load('./roar.png')

This is all fine and dandy, if you want to load a static image that you don't want to do much with. However, you're making a game and you are going to use a hell of a lot more sprites and objects than just one simple image file.

Now this is where shared objects, batches and sprites come in handy. First off, input your image into a sprite, it's a good start.

sprite = pyglet.sprite.Sprite(pyglet.image.load('./roar.png'))
sprite.draw() # This is instead of blit. Position is done via sprite.x = ...

Now, draw is a hell of a lot quicker than .blit() for numerous of reasons, but we'll skip why for now and just stick with blazing speed upgrades.

Again, this is just one small step towards successful framerates (other than having limited hardware ofc.. duh).

Anyway, back to pew pew your code with upgrades.
Now you also want to add sprites to a batch so you can simultaneously render a LOT of things on one go (read: batch) instead of manually pushing things to the graphics card. Graphic cards soul purpose was designed to be able to handle gigabits of throughput in calculations in one insanely fast go rather than handle multiple of small I/O's.

To do this, you need to create a batch container. And add "layers" to it.
It's quite simple really, all you need to do is:

main_batch = pyglet.graphics.Batch()
background = pyglet.graphics.OrderedGroup(0)
# stuff_above_background = pyglet.graphics.OrderedGroup(1)
# ...

We'll stick one with batch for now, you probably don't need more for this learning purpose.
Ok so you got your batch, now what? Well now we try our hardest to choke that living hell out of your graphics card and see if we can even buckle it under pressure (No graphic cars were harmed in this process, and please don't choke things..)

Oh one more thing, remember the note about shared objects? well, we'll create a shared image object here that we push into the sprite, instead of loading one new image every.. single... time.. monster_image we'll call it.

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)

Now you have 100 monsters created and added to the batch main_batch into the sub-group background. Simple as pie.

Here's the kicker, instead of calling self.blocks[key].blit() or .draw(), we can now call main_batch.draw() and it will fire away every single monster onto the graphics card and produce wonders.

Ok, so now you've optimized the speed of your code, but really that won't help you in the long run if you're making a game. Or in this case, a graphics engine for your game. What you want to do is step up into the big league and use classes. If you were amazed before you'll probably loose your marbles of how awesome your code will look once you've done with it.

Ok so first, you want to create a base class for your objects on the screen, lets called in baseSprite.
Now there are some kinks and stuff you need to work around with Pyglet, for one, when inheriting Sprite objects trying to set image will cause all sorts of iffy glitches and bugs when working with stuff so we'll set self.texture directly which is basically the same thing but we hook into the pyglet libraries variables instead ;D pew pew hehe.

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
        self.y = y

    def move(self, x, y):
        """ This function is just to show you
            how you could improve your base class
            even further """
        self.x += x
        self.y += y

    def _draw(self):
        """
        Normally we call _draw() instead of .draw() on sprites
        because _draw() will contains so much more than simply
        drawing the object, it might check for interactions or
        update inline data (and most likely positioning objects).
        """
        self.draw()

Now that's your base, you can now create monsters by doing:

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()

How, you probably use the default @on_window_draw() example that everyone else is using and that's fine, but I find it slow, ugly and just not practical in the long run. You want to do Object Oriented Programming.. Right?
That's what it's called, I call it readable code that you like to watch all day long. RCTYLTWADL for short.

To do this, we'll need to create a class that mimics the behavior of Pyglet and call it's subsequent functions in order and poll the event handler otherwise sh** will get stuck, trust me.. Done it a couple of times and bottle necks are easy to create.
But enough of my mistakes, here's a basic main class that you can use that uses poll-based event handling and thus limiting the refresh rate to your programming rather than built in behavior in 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()

Now this is again just a basic main class that does nothing other than render a black background and anything put into self.sprites and self.batches.

Do note! we call ._draw() on the sprites because we created our own sprite class earlier? Yea that's the awesome base sprite class that you can hook in your own stuff before draw() is done on each individual sprite.

Anywho, This all boils down to a couple of things.

  1. Use sprites when making games, your life will be easier
  2. Use batches, your GPU will love you and the refreshrates will be amazing
  3. Use classes and stuff, your eyes and code mojo will love you in the end.

Here's a fully working example of all the pieces puzzled together:

import pyglet
from pyglet.gl import *

glEnable(GL_BLEND)
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
glEnable(GL_LINE_SMOOTH)
glHint(GL_LINE_SMOOTH_HINT, GL_DONT_CARE)

pyglet.clock.set_fps_limit(60)

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
        self.y = y

    def move(self, x, y):
        """ This function is just to show you
            how you could improve your base class
            even further """
        self.x += x
        self.y += y

    def _draw(self):
        """
        Normally we call _draw() instead of .draw() on sprites
        because _draw() will contains so much more than simply
        drawing the object, it might check for interactions or
        update inline data (and most likely positioning objects).
        """
        self.draw()

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._handles = {}

        self.batches['main'] = pyglet.graphics.Batch()
        self.subgroups['base'] = pyglet.graphics.OrderedGroup(0)

        monster_image = pyglet.image.load('./roar.png')
        for i in range(100):
            self._handles['monster'+str(i)] = baseSprite(monster_image, randint(0, 50), randint(0, 50), self.batches['main'], self.subgroups['base'])

        # Note: We put the sprites in `_handles` because they will be rendered via
        # the `self.batches['main']` batch, and placing them in `self.sprites` will render
        # them twice. But we need to keep the handle so we can use `.move` and stuff
        # on the items later on in the game making process ;)

        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()

            # Fun fact:
            #   If you want to limit your FPS, this is where you do it
            #   For a good example check out this SO link:
            #   http://stackoverflow.com/questions/16548833/pyglet-not-running-properly-on-amd-hd4250/16548990#16548990

x = main()
x.run()

Some bonus stuff, I added GL options that usually does some benefitial stuff for you. I also added sa FPS limiter that you can tinker and play with.

Edit:

Batched updates

Since the sprite object can be used to do massive renderings in one go by sending it all to the graphics card, similarly you'd want to do batched updates. For instance if you want to update every objects position, color or whatever it might be.

This is where clever programming comes into play rather than nifty little tools.
See, everything i relevant in programming.. If you want it to be.

Assume you have (at the top of your code) a variable called:

global_settings = {'player position' : (50, 50)}
# The player is at X cord 50 and Y cord 50.

In your base sprite you could simply do the following:

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

Note that you'd have to tweak the draw() (note, not _draw() since batched rendering will call upon draw and not _draw) function a little bit to honor and update position updates per rendering sequence. That or you could create a new class that inherits baseSprite and have only those types of sprite updated:

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

And thus only call .update() on monster type classes/sprites.
It's a bit tricky to get it optimal and there's ways to solve it and still use batched rendering, but somewhere along these lines is probably a good start.



IMPORTANT NOTE I just wrote a lot of this from the top of my head (not the first time I've written a GUI class in Pyglet) and for whatever reason this *Nix instance of mine doesn't find my X-server.. So can't test the code.

I'll give it a test in an hour when I get off work, but this gives you a general Idea of what to do and what to think for when making games in Pyglet. Remember, have fun while doing it or you're quit before you even started because games take time to make ^^

Pew pew lazors and stuff, best of luck!

这篇关于Pyglet图像渲染的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆