尝试绘制精灵或更改图片 pyglet
trying to draw over sprite or change picture pyglet
我正在尝试学习 pyglet 并使用问卷练习一些 python 编码,但我无法找到一种方法来删除背景图片或在其上绘制 10 秒.我是新手,缺乏很多我需要的知识,谢谢你的帮助!
import pyglet
from pyglet.window import Window
from pyglet.window import key
from pyglet import image
import time
card1 = False
cat_image = pyglet.image.load("cat.png")
dog_image = pyglet.image.load("dog.png")
image = pyglet.image.load("backg.png")
background_sprite = pyglet.sprite.Sprite(image)
cat = pyglet.sprite.Sprite(cat_image)
dog = pyglet.sprite.Sprite(dog_image)
window = pyglet.window.Window(638, 404, "Life")
mouse_pos_x = 0
mouse_pos_y = 0
catmeme = pyglet.image.load("catmeme.png")
sprite_catmeme = pyglet.sprite.Sprite(catmeme)
@window.event
def on_draw():
window.clear()
background_sprite.draw()
card_draw1(63, 192, 385, 192)
def card1():
while time.time() < (time.time() + 10):
window.clear()
sprite_catmeme.draw()
@window.event
def card_draw1(x1, y1, x2, y2):
cat.set_position(x1, y1)
dog.set_position(x2, y2)
cat.draw()
dog.draw()
def card_draw2():
pass
@window.event
def on_mouse_press(x, y, button, modifiers):
if x > cat.x and x < (cat.x + cat.width):
if y > cat.y and y < (cat.y + cat.height):
card1()
game = True
while game:
on_draw()
pyglet.app.run()
您做事的顺序和方式存在一些缺陷。
我会尽力描述它们,并为您提供一段代码,可能会更好地满足您的需求。
我还认为你对问题的描述有点 XY Problem 这在你认为你接近解决方案的复杂问题上寻求帮助时很常见,所以你问寻求有关您提出的解决方案而不是问题的帮助。
我假设您想显示 "Splash screen" 10 秒,这恰好是您的背景?然后在上面呈现 cat.png
和 dog.png
,对吗?
如果是这种情况,您可能需要在此处进行更改以使其正常工作:
draw()
函数
它并没有真正更新屏幕,它只是向图形内存添加内容。更新屏幕的是您或告诉图形库您已完成向屏幕添加内容,是时候更新您拥有的所有内容了 .draw()
'n。因此,循环中最后需要的是 window.flip()
,以便实际显示您绘制的内容。
如果您尝试摆动 window,您的东西可能会显示,它应该触发场景的 re-draw,因为 pyglet 的内部机制是如何工作的..
如果您不调用 .flip()
- 很可能 redraw()
调用永远不会发生 - 这又是 Pyglet/GL 的一种内部机制,它告诉显卡某些内容已更新,我们已完成更新,是时候重新绘制场景了。
一个场景
这是用户看到的最常用的词。
我可能会在我的文本中经常使用它,所以很高兴知道这是用户看到的内容,而不是您 .draw()
'n 或已删除的内容,它是显卡到显示器。
但由于图形缓冲区的工作方式,我们可能已经在内存中删除或添加了内容,但尚未真正绘制出来。请记住这一点。
pyglet.app.run()
调用
这本身就是一个永无止境的循环,所以在 while game:
循环中没有任何意义,因为 .run()
将 "hang" 您的整个应用程序,您编写的任何代码想要执行需要在 def on_draw
或从图形代码本身生成的 event
中。
为了更好地理解这一点,请查看我的代码,这些年来我在 SO 上将它粘贴了几次,它是两个自定义 classes 的基本模型,继承了Pyglet 的行为,但允许您设计自己的 classes 使其行为略有不同。
而且大多数功能都在 on_???
函数下,这几乎总是用于捕获 Events 的函数。 Pyglet 内置了很多这些,我们将用我们自己的覆盖它们(但名称必须相同)
import pyglet
from pyglet.gl import *
key = pyglet.window.key
class CustomSprite(pyglet.sprite.Sprite):
def __init__(self, texture_file, x=0, y=0):
## Must load the texture as a image resource before initializing class:Sprite()
self.texture = pyglet.image.load(texture_file)
super(CustomSprite, self).__init__(self.texture)
self.x = x
self.y = y
def _draw(self):
self.draw()
class MainScreen(pyglet.window.Window):
def __init__ (self):
super(MainScreen, self).__init__(800, 600, fullscreen = False)
self.x, self.y = 0, 0
self.bg = CustomSprite('bg.jpg')
self.sprites = {}
self.alive = 1
def on_draw(self):
self.render()
def on_close(self):
self.alive = 0
def on_key_press(self, symbol, modifiers):
if symbol == key.ESCAPE: # [ESC]
self.alive = 0
elif symbol == key.C:
print('Rendering cat')
self.sprites['cat'] = CustomSprite('cat.png', x=10, y=10)
elif symbol == key.D:
self.sprites['dog'] = CustomSprite('dog.png', x=100, y=100)
def render(self):
self.clear()
self.bg.draw()
for sprite_name, sprite_obj in self.sprites.items():
sprite_obj._draw()
self.flip()
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
#
event = self.dispatch_events()
x = MainScreen()
x.run()
现在,这段代码有意保持简单,我通常在 SO 上粘贴的完整代码可以在 Torxed/PygletGui, the gui.py 找到,这是大部分代码的来源,它是主循环。
我在这里所做的只是用 class 中的 "actual" 函数替换 Decorators。 class 本身继承了传统 pyglet.window.Window
的功能,一旦您将这些功能命名为与继承的 onces 相同的名称,您就可以用您决定的任何内容替换 Window()
的核心功能。 . 在这种情况下,我模仿了相同的功能,但添加了一些我自己的功能。
on_key_press
一个这样的例子是 on_key_press()
,它通常只包含一个 pass
调用,什么都不做,在这里,我们检查是否按下了 key.C
,如果是,我们添加一个self.sprites
.. self.sprites
的项目恰好在我们的 render()
循环中,其中的任何内容都将呈现在背景之上。
这是我使用的图片:
(命名为 bg.jpg
、cat.png
、dog.png
- 注意不同的文件结尾)
class:自定义精灵
CustomSprite
是一个非常简单的 class 旨在让您的生活更轻松,仅此而已。它的功能非常有限,但它所做的一点点都很棒。
它的核心目的是获取文件名,将其作为图像加载,您可以像对待传统对象一样对待对象 pyglet.sprite.Sprite
,这意味着您可以移动它并以多种方式对其进行操作。
它节省了几行必须加载您需要的所有图像的代码,正如您在 gui_classes_generic.py 中看到的那样,您可以添加一堆 "invisible" 且通常不容易使用的函数普通精灵 class.
我用这一堆!但是代码变得非常复杂,所以我故意让这个 post 简单。
翻转功能
即使在我的 class 中,我仍然需要使用 flip()
来更新屏幕内容。这是因为 .clear()
如您所料清除了 window,这也会触发场景的重绘。
bg.draw()
我可以如果数据足够大或发生其他情况,某些情况下会触发重绘,例如您移动 window.
但是调用 .flip()
会告诉 GL 后端强制重绘。
进一步优化
有一种叫做批量渲染的东西,基本上图形卡被设计成可以获取大量数据并一次性渲染,所以在多个调用.draw()
项目只会在 GPU 有机会发光之前堵塞 CPU。阅读更多关于 Batched rendering and graphics 的信息!它将为您节省大量帧速率。
另一件事是在 render()
循环中保留尽可能少的功能,并使用事件触发器作为编码风格的主要来源。
Pyglet 在速度方面做得很好,尤其是当您只在事件驱动的任务上做事时。
尽量避免使用计时器,但如果您确实确实需要使用时间,例如在一定时间后删除 cat.png
,请使用 clock/time event 调用一个函数来删除那只猫。不要尝试使用您自己的 t = time()
风格的代码,除非您知道将它放在哪里以及为什么。有一个很好的计时器,我很少使用它。但是如果你刚开始,你应该这样做。
这是一大堆文字,我希望它能对您在图形和其他东西的生活中有所启发。坚持下去,进入这种东西是一个障碍,但是一旦你掌握了它就会非常有益(我还没有):)
我正在尝试学习 pyglet 并使用问卷练习一些 python 编码,但我无法找到一种方法来删除背景图片或在其上绘制 10 秒.我是新手,缺乏很多我需要的知识,谢谢你的帮助!
import pyglet
from pyglet.window import Window
from pyglet.window import key
from pyglet import image
import time
card1 = False
cat_image = pyglet.image.load("cat.png")
dog_image = pyglet.image.load("dog.png")
image = pyglet.image.load("backg.png")
background_sprite = pyglet.sprite.Sprite(image)
cat = pyglet.sprite.Sprite(cat_image)
dog = pyglet.sprite.Sprite(dog_image)
window = pyglet.window.Window(638, 404, "Life")
mouse_pos_x = 0
mouse_pos_y = 0
catmeme = pyglet.image.load("catmeme.png")
sprite_catmeme = pyglet.sprite.Sprite(catmeme)
@window.event
def on_draw():
window.clear()
background_sprite.draw()
card_draw1(63, 192, 385, 192)
def card1():
while time.time() < (time.time() + 10):
window.clear()
sprite_catmeme.draw()
@window.event
def card_draw1(x1, y1, x2, y2):
cat.set_position(x1, y1)
dog.set_position(x2, y2)
cat.draw()
dog.draw()
def card_draw2():
pass
@window.event
def on_mouse_press(x, y, button, modifiers):
if x > cat.x and x < (cat.x + cat.width):
if y > cat.y and y < (cat.y + cat.height):
card1()
game = True
while game:
on_draw()
pyglet.app.run()
您做事的顺序和方式存在一些缺陷。
我会尽力描述它们,并为您提供一段代码,可能会更好地满足您的需求。
我还认为你对问题的描述有点 XY Problem 这在你认为你接近解决方案的复杂问题上寻求帮助时很常见,所以你问寻求有关您提出的解决方案而不是问题的帮助。
我假设您想显示 "Splash screen" 10 秒,这恰好是您的背景?然后在上面呈现 cat.png
和 dog.png
,对吗?
如果是这种情况,您可能需要在此处进行更改以使其正常工作:
draw()
函数
它并没有真正更新屏幕,它只是向图形内存添加内容。更新屏幕的是您或告诉图形库您已完成向屏幕添加内容,是时候更新您拥有的所有内容了 .draw()
'n。因此,循环中最后需要的是 window.flip()
,以便实际显示您绘制的内容。
如果您尝试摆动 window,您的东西可能会显示,它应该触发场景的 re-draw,因为 pyglet 的内部机制是如何工作的..
如果您不调用 .flip()
- 很可能 redraw()
调用永远不会发生 - 这又是 Pyglet/GL 的一种内部机制,它告诉显卡某些内容已更新,我们已完成更新,是时候重新绘制场景了。
一个场景
这是用户看到的最常用的词。
我可能会在我的文本中经常使用它,所以很高兴知道这是用户看到的内容,而不是您 .draw()
'n 或已删除的内容,它是显卡到显示器。
但由于图形缓冲区的工作方式,我们可能已经在内存中删除或添加了内容,但尚未真正绘制出来。请记住这一点。
pyglet.app.run()
调用
这本身就是一个永无止境的循环,所以在 while game:
循环中没有任何意义,因为 .run()
将 "hang" 您的整个应用程序,您编写的任何代码想要执行需要在 def on_draw
或从图形代码本身生成的 event
中。
为了更好地理解这一点,请查看我的代码,这些年来我在 SO 上将它粘贴了几次,它是两个自定义 classes 的基本模型,继承了Pyglet 的行为,但允许您设计自己的 classes 使其行为略有不同。
而且大多数功能都在 on_???
函数下,这几乎总是用于捕获 Events 的函数。 Pyglet 内置了很多这些,我们将用我们自己的覆盖它们(但名称必须相同)
import pyglet
from pyglet.gl import *
key = pyglet.window.key
class CustomSprite(pyglet.sprite.Sprite):
def __init__(self, texture_file, x=0, y=0):
## Must load the texture as a image resource before initializing class:Sprite()
self.texture = pyglet.image.load(texture_file)
super(CustomSprite, self).__init__(self.texture)
self.x = x
self.y = y
def _draw(self):
self.draw()
class MainScreen(pyglet.window.Window):
def __init__ (self):
super(MainScreen, self).__init__(800, 600, fullscreen = False)
self.x, self.y = 0, 0
self.bg = CustomSprite('bg.jpg')
self.sprites = {}
self.alive = 1
def on_draw(self):
self.render()
def on_close(self):
self.alive = 0
def on_key_press(self, symbol, modifiers):
if symbol == key.ESCAPE: # [ESC]
self.alive = 0
elif symbol == key.C:
print('Rendering cat')
self.sprites['cat'] = CustomSprite('cat.png', x=10, y=10)
elif symbol == key.D:
self.sprites['dog'] = CustomSprite('dog.png', x=100, y=100)
def render(self):
self.clear()
self.bg.draw()
for sprite_name, sprite_obj in self.sprites.items():
sprite_obj._draw()
self.flip()
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
#
event = self.dispatch_events()
x = MainScreen()
x.run()
现在,这段代码有意保持简单,我通常在 SO 上粘贴的完整代码可以在 Torxed/PygletGui, the gui.py 找到,这是大部分代码的来源,它是主循环。
我在这里所做的只是用 class 中的 "actual" 函数替换 Decorators。 class 本身继承了传统 pyglet.window.Window
的功能,一旦您将这些功能命名为与继承的 onces 相同的名称,您就可以用您决定的任何内容替换 Window()
的核心功能。 . 在这种情况下,我模仿了相同的功能,但添加了一些我自己的功能。
on_key_press
一个这样的例子是 on_key_press()
,它通常只包含一个 pass
调用,什么都不做,在这里,我们检查是否按下了 key.C
,如果是,我们添加一个self.sprites
.. self.sprites
的项目恰好在我们的 render()
循环中,其中的任何内容都将呈现在背景之上。
这是我使用的图片:
(命名为 bg.jpg
、cat.png
、dog.png
- 注意不同的文件结尾)
class:自定义精灵
CustomSprite
是一个非常简单的 class 旨在让您的生活更轻松,仅此而已。它的功能非常有限,但它所做的一点点都很棒。
它的核心目的是获取文件名,将其作为图像加载,您可以像对待传统对象一样对待对象 pyglet.sprite.Sprite
,这意味着您可以移动它并以多种方式对其进行操作。
它节省了几行必须加载您需要的所有图像的代码,正如您在 gui_classes_generic.py 中看到的那样,您可以添加一堆 "invisible" 且通常不容易使用的函数普通精灵 class.
我用这一堆!但是代码变得非常复杂,所以我故意让这个 post 简单。
翻转功能
即使在我的 class 中,我仍然需要使用 flip()
来更新屏幕内容。这是因为 .clear()
如您所料清除了 window,这也会触发场景的重绘。
bg.draw()
我可以如果数据足够大或发生其他情况,某些情况下会触发重绘,例如您移动 window.
但是调用 .flip()
会告诉 GL 后端强制重绘。
进一步优化
有一种叫做批量渲染的东西,基本上图形卡被设计成可以获取大量数据并一次性渲染,所以在多个调用.draw()
项目只会在 GPU 有机会发光之前堵塞 CPU。阅读更多关于 Batched rendering and graphics 的信息!它将为您节省大量帧速率。
另一件事是在 render()
循环中保留尽可能少的功能,并使用事件触发器作为编码风格的主要来源。
Pyglet 在速度方面做得很好,尤其是当您只在事件驱动的任务上做事时。
尽量避免使用计时器,但如果您确实确实需要使用时间,例如在一定时间后删除 cat.png
,请使用 clock/time event 调用一个函数来删除那只猫。不要尝试使用您自己的 t = time()
风格的代码,除非您知道将它放在哪里以及为什么。有一个很好的计时器,我很少使用它。但是如果你刚开始,你应该这样做。
这是一大堆文字,我希望它能对您在图形和其他东西的生活中有所启发。坚持下去,进入这种东西是一个障碍,但是一旦你掌握了它就会非常有益(我还没有):)