在一个 Pyglet 中包含多个精灵 Window
Having multiple sprites in one Pyglet Window
我有下面的代码,当前在空白 Pyglet window 上输出图像,但是只输出了一个图像。我真的需要每两秒添加一个新图像,并且之前的图像也保持不变。例如,添加一张图片,两秒后添加另一张图片,两秒后添加另一张图片。我添加了随机库,因此可以添加随机图像。
我的代码如下,它只显示一张图片 - 我觉得它卡在了绘图部分的某个地方。
import pyglet
import time
import random
window = pyglet.window.Window()
while True:
images = ["Image 1.png", "Image 2.png", "Image 3.png"]
choice = images[random.randint(0,2)]
rawImage = pyglet.resource.image(choice)
sprite = pyglet.sprite.Sprite(rawImage)
@window.event
def on_draw():
window.clear()
sprite.draw()
time.sleep(2)
pyglet.app.run()
如果您能为此提供任何帮助或建议,我们将不胜感激。
一些 issues/suggestions 与您的代码。
首先,以下代码是多余的:
while True:
images = ["Image 1.png", "Image 2.png", "Image 3.png"]
....
pyglet.app.run()
因为 pyglet.app.run()
是一个阻塞调用,也就是说,循环永远不会循环 - 因为 pyglet.app.run()
本身就是一个循环(稍后会详细介绍)。
除非你的应用程序崩溃,但你不处理这些异常,所以即使在那种情况下,代码也不会是 re-run/looped.
其次,你永远不应该定义 arrays/lists 或任何真正在循环中的东西。循环通常用于逻辑操作,而不是创建事物。最满意的是,有时在循环内创建内容很有用,但那些时候它们通常伴随着 if
语句。
计算机的资源成本很高,包括设置和 memory/hard 驱动器等。因此建议尽可能早地创建您的列表,并在任何循环之外。例如:
window = pyglet.window.Window()
images = ["Image 1.png", "Image 2.png", "Image 3.png"]
while True:
choice = images[random.randint(0,2)]
本来是一个更好的选择,如果 - 再一次 - 循环实际上循环了。在这种情况下,只需整理一下即可。
另外,这段代码:
@window.event
def on_draw():
window.clear()
sprite.draw()
也不应在循环中创建,它旨在替换您的 window
变量 on_draw
函数。因此,应该将其移出并尽早放入您的逻辑 IMO 中。至少与所有其他逻辑保持分离,因此它不在 "adding random image" 之间和 "loop".
内部
现在,您的代码失败的主要原因是您认为这会循环,但事实并非如此。同样,pyglet.app.run()
会将您的代码执行锁定在该行,因为它在该函数调用中是一个永无止境的循环。
您可以扩展您的代码并从 pyglet.py
的源代码中复制粘贴代码,它看起来像这样(只是为了让您了解正在发生的事情):
window = pyglet.window.Window()
while True:
images = ["Image 1.png", "Image 2.png", "Image 3.png"]
choice = images[random.randint(0,2)]
rawImage = pyglet.resource.image(choice)
sprite = pyglet.sprite.Sprite(rawImage)
@window.event
def on_draw():
window.clear()
sprite.draw()
time.sleep(2)
def run(self):
while True:
event = self.dispatch_events()
if event:
self.on_draw()
请注意 pyglet.app.run()
是如何扩展到另一个 while True
循环的,它永远不会真正中断。 有点过于简单化了,但实际上就是这样。
所以你的sprite = pyglet.sprite.Sprite(rawImage)
永远不会被重新触发。
然后,对于您的第二大问题,为什么此代码永远无法工作:
你在做:
def on_draw():
sprite.draw()
但是在每个循环中,您都会通过 sprite = pyglet.sprite.Sprite(rawImage)
将旧的 sprite
对象替换为新的对象。所以你想要做的是,在所有可见图像的循环之外保留一个 list/dictionary,然后添加到它并且只渲染添加的图像。
很像这样:
import pyglet
import time
import random
width, height = 800, 600
window = pyglet.window.Window(width, height)
## Image options are the options we have,
## while `images` are the visible images, this is where we add images
## so that they can be rendered later
image_options = ["Image 1.png", "Image 2.png", "Image 3.png"]
images = {}
## Keep a timer of when we last added a image
last_add = time.time()
## Just a helper-function to generate a random image and return it
## as a sprite object (good idea to use sprites, more on that later)
def get_random_image():
choice = image_options[random.randint(0, len(image_options)-1)]
return pyglet.sprite.Sprite(pyglet.image.load(choice))
## Here, we define the `on_draw` replacement for `window.on_draw`,
## and it's here we'll check if we should add a nother image or not
## depending on how much time has passed.
@window.event
def on_draw():
window.clear()
## If two seconds have passed, and the ammount of images added are less/equal
## to how many images we have in our "database", aka `image_options`, then we'll
## add another image somewhere randomly in the window.
if time.time() - last_add > 2 and len(images) < len(image_options):
last_add = time.time()
image = get_random_image()
image.x = random.randint(0, width)
image.y = random.randint(0, height)
images[len(images)] = image
## Then here, is where we loop over all our added images,
## and render them one by one.
for _id_ in images:
images[_id_].draw()
## Ok, lets start off by adding one image.
image = get_random_image()
image.x = random.randint(0, width)
image.y = random.randint(0, height)
images[len(images)] = image
## And then enter the never ending render loop.
pyglet.app.run()
现在,只有当您在 window 中按下一个键或按下鼠标时,这才有效。这是因为那是事件将被分派的唯一时间。 Pyglet 只会在有事件被触发时渲染。有两种方法可以解决这个问题,我现在将跳过的硬核 OOP 方法。
第二种方法是使用所谓的 Pyglet 时钟,您可以在其中安排每隔一段时间发生的事情。我不太擅长这部分,因为我倾向于使用自己的调度程序等。
但这是它的要点:
def add_image():
images[len(images)] = get_random_image()
pyglet.clock.schedule_interval(add_image, 2) # Every two seconds
这比 if time.time() - last_add > 2
.
干净多了
结果应如下所示:
import pyglet
import time
import random
width, height = 800, 600
window = pyglet.window.Window(width, height)
## Image options are the options we have,
## while `images` are the visible images, this is where we add images
## so that they can be rendered later
image_options = ["Image 1.png", "Image 2.png", "Image 3.png"]
images = {}
## Just a helper-function to generate a random image and return it
## as a sprite object (good idea to use sprites, more on that later)
def get_random_image():
choice = image_options[random.randint(0, len(image_options)-1)]
return pyglet.sprite.Sprite(pyglet.image.load(choice))
def add_image(actual_time_passed_since_last_clock_tick):
image = get_random_image()
image.x = random.randint(0, width)
image.y = random.randint(0, height)
images[len(images)] = image
## Here, we define the `on_draw` replacement for `window.on_draw`,
## and it's here we'll check if we should add a nother image or not
## depending on how much time has passed.
@window.event
def on_draw():
window.clear()
## Then here, is where we loop over all our added ima ges,
## and render them one by one.
for _id_ in images:
images[_id_].draw()
## Ok, lets start off by adding one image.
image = get_random_image()
image.x = random.randint(0, width)
image.y = random.randint(0, height)
images[len(images)] = image
## Add the schedule interval of adding a image every two seconds.
pyglet.clock.schedule_interval(add_image, 2)
## And then enter the never ending render loop.
pyglet.app.run()
这样,您将不需要按任何键或鼠标来触发 Pyglet 中的事件,它会为您处理并执行您安排的事情。
接下来,是我的一个小优化。这是一个奖励,并且会加快速度。它称为 批量渲染 ,当您渲染大量图像和 sprite 时,您目前一次将一张图像发送到显卡。这是非常 CPU 密集的。您要做的是将工作放在 GPU 上。因为毕竟,您是在处理图形,对吗?
因此,在这种情况下,批量渲染非常简单。每次调用 pyglet.sprite.Sprite
时,它都有一个名为 batch=None
的参数(默认)。如果您向 sprite 对象添加一个批次,则可以通过调用 batch.draw()
而不是每个单独的 sprite.draw()
.
来渲染整个批次
解决方案看起来像这样:
import pyglet
import time
from random import randint
width, height = 800, 600
window = pyglet.window.Window(width, height)
main_batch = pyglet.graphics.Batch()
## Image options are the options we have,
## while `images` are the visible images, this is where we add images
## so that they can be rendered later
image_options = ["Image 1.png", "Image 2.png", "Image 3.png"]
images = {}
## Just a helper-function to generate a random image and return it
## as a sprite object (good idea to use sprites, more on that later)
def get_random_image(x=0, y=0):
choice = image_options[randint(0, len(image_options)-1)]
return pyglet.sprite.Sprite(pyglet.image.load(choice), x=x, y=y, batch=main_batch)
def add_image(actual_time_passed_since_last_clock_tick=0):
image = get_random_image(x=randint(0, width), y=randint(0, height))
images[len(images)] = image
## Here, we define the `on_draw` replacement for `window.on_draw`,
## and it's here we'll check if we should add a nother image or not
## depending on how much time has passed.
@window.event
def on_draw():
window.clear()
## Instead of looping over each image in `images`,
## just do:
main_batch.draw()
## Ok, lets start off by adding one image.
## Instead of doing it manually, use the function add_image.
add_image()
## Add the schedule interval of adding a image every two seconds.
pyglet.clock.schedule_interval(add_image, 2)
## And then enter the never ending render loop.
pyglet.app.run()
我还对add_image
和get_random_image
做了一些修改,主要是为了让你知道图像在函数中应该在什么位置,因为pyglet.sprite.Sprite
还需要另外两个参数,x
和 y
。所以在你创建精灵之后改变 x
和 y
是没有意义的,除非你想在之后移动它们(例如,在 pyglet.clock.schedule_interval
调用中)。
我有下面的代码,当前在空白 Pyglet window 上输出图像,但是只输出了一个图像。我真的需要每两秒添加一个新图像,并且之前的图像也保持不变。例如,添加一张图片,两秒后添加另一张图片,两秒后添加另一张图片。我添加了随机库,因此可以添加随机图像。
我的代码如下,它只显示一张图片 - 我觉得它卡在了绘图部分的某个地方。
import pyglet
import time
import random
window = pyglet.window.Window()
while True:
images = ["Image 1.png", "Image 2.png", "Image 3.png"]
choice = images[random.randint(0,2)]
rawImage = pyglet.resource.image(choice)
sprite = pyglet.sprite.Sprite(rawImage)
@window.event
def on_draw():
window.clear()
sprite.draw()
time.sleep(2)
pyglet.app.run()
如果您能为此提供任何帮助或建议,我们将不胜感激。
一些 issues/suggestions 与您的代码。 首先,以下代码是多余的:
while True:
images = ["Image 1.png", "Image 2.png", "Image 3.png"]
....
pyglet.app.run()
因为 pyglet.app.run()
是一个阻塞调用,也就是说,循环永远不会循环 - 因为 pyglet.app.run()
本身就是一个循环(稍后会详细介绍)。
除非你的应用程序崩溃,但你不处理这些异常,所以即使在那种情况下,代码也不会是 re-run/looped.
其次,你永远不应该定义 arrays/lists 或任何真正在循环中的东西。循环通常用于逻辑操作,而不是创建事物。最满意的是,有时在循环内创建内容很有用,但那些时候它们通常伴随着 if
语句。
计算机的资源成本很高,包括设置和 memory/hard 驱动器等。因此建议尽可能早地创建您的列表,并在任何循环之外。例如:
window = pyglet.window.Window()
images = ["Image 1.png", "Image 2.png", "Image 3.png"]
while True:
choice = images[random.randint(0,2)]
本来是一个更好的选择,如果 - 再一次 - 循环实际上循环了。在这种情况下,只需整理一下即可。
另外,这段代码:
@window.event
def on_draw():
window.clear()
sprite.draw()
也不应在循环中创建,它旨在替换您的 window
变量 on_draw
函数。因此,应该将其移出并尽早放入您的逻辑 IMO 中。至少与所有其他逻辑保持分离,因此它不在 "adding random image" 之间和 "loop".
现在,您的代码失败的主要原因是您认为这会循环,但事实并非如此。同样,pyglet.app.run()
会将您的代码执行锁定在该行,因为它在该函数调用中是一个永无止境的循环。
您可以扩展您的代码并从 pyglet.py
的源代码中复制粘贴代码,它看起来像这样(只是为了让您了解正在发生的事情):
window = pyglet.window.Window()
while True:
images = ["Image 1.png", "Image 2.png", "Image 3.png"]
choice = images[random.randint(0,2)]
rawImage = pyglet.resource.image(choice)
sprite = pyglet.sprite.Sprite(rawImage)
@window.event
def on_draw():
window.clear()
sprite.draw()
time.sleep(2)
def run(self):
while True:
event = self.dispatch_events()
if event:
self.on_draw()
请注意 pyglet.app.run()
是如何扩展到另一个 while True
循环的,它永远不会真正中断。 有点过于简单化了,但实际上就是这样。
所以你的sprite = pyglet.sprite.Sprite(rawImage)
永远不会被重新触发。
然后,对于您的第二大问题,为什么此代码永远无法工作:
你在做:
def on_draw():
sprite.draw()
但是在每个循环中,您都会通过 sprite = pyglet.sprite.Sprite(rawImage)
将旧的 sprite
对象替换为新的对象。所以你想要做的是,在所有可见图像的循环之外保留一个 list/dictionary,然后添加到它并且只渲染添加的图像。
很像这样:
import pyglet
import time
import random
width, height = 800, 600
window = pyglet.window.Window(width, height)
## Image options are the options we have,
## while `images` are the visible images, this is where we add images
## so that they can be rendered later
image_options = ["Image 1.png", "Image 2.png", "Image 3.png"]
images = {}
## Keep a timer of when we last added a image
last_add = time.time()
## Just a helper-function to generate a random image and return it
## as a sprite object (good idea to use sprites, more on that later)
def get_random_image():
choice = image_options[random.randint(0, len(image_options)-1)]
return pyglet.sprite.Sprite(pyglet.image.load(choice))
## Here, we define the `on_draw` replacement for `window.on_draw`,
## and it's here we'll check if we should add a nother image or not
## depending on how much time has passed.
@window.event
def on_draw():
window.clear()
## If two seconds have passed, and the ammount of images added are less/equal
## to how many images we have in our "database", aka `image_options`, then we'll
## add another image somewhere randomly in the window.
if time.time() - last_add > 2 and len(images) < len(image_options):
last_add = time.time()
image = get_random_image()
image.x = random.randint(0, width)
image.y = random.randint(0, height)
images[len(images)] = image
## Then here, is where we loop over all our added images,
## and render them one by one.
for _id_ in images:
images[_id_].draw()
## Ok, lets start off by adding one image.
image = get_random_image()
image.x = random.randint(0, width)
image.y = random.randint(0, height)
images[len(images)] = image
## And then enter the never ending render loop.
pyglet.app.run()
现在,只有当您在 window 中按下一个键或按下鼠标时,这才有效。这是因为那是事件将被分派的唯一时间。 Pyglet 只会在有事件被触发时渲染。有两种方法可以解决这个问题,我现在将跳过的硬核 OOP 方法。
第二种方法是使用所谓的 Pyglet 时钟,您可以在其中安排每隔一段时间发生的事情。我不太擅长这部分,因为我倾向于使用自己的调度程序等。
但这是它的要点:
def add_image():
images[len(images)] = get_random_image()
pyglet.clock.schedule_interval(add_image, 2) # Every two seconds
这比 if time.time() - last_add > 2
.
干净多了
结果应如下所示:
import pyglet
import time
import random
width, height = 800, 600
window = pyglet.window.Window(width, height)
## Image options are the options we have,
## while `images` are the visible images, this is where we add images
## so that they can be rendered later
image_options = ["Image 1.png", "Image 2.png", "Image 3.png"]
images = {}
## Just a helper-function to generate a random image and return it
## as a sprite object (good idea to use sprites, more on that later)
def get_random_image():
choice = image_options[random.randint(0, len(image_options)-1)]
return pyglet.sprite.Sprite(pyglet.image.load(choice))
def add_image(actual_time_passed_since_last_clock_tick):
image = get_random_image()
image.x = random.randint(0, width)
image.y = random.randint(0, height)
images[len(images)] = image
## Here, we define the `on_draw` replacement for `window.on_draw`,
## and it's here we'll check if we should add a nother image or not
## depending on how much time has passed.
@window.event
def on_draw():
window.clear()
## Then here, is where we loop over all our added ima ges,
## and render them one by one.
for _id_ in images:
images[_id_].draw()
## Ok, lets start off by adding one image.
image = get_random_image()
image.x = random.randint(0, width)
image.y = random.randint(0, height)
images[len(images)] = image
## Add the schedule interval of adding a image every two seconds.
pyglet.clock.schedule_interval(add_image, 2)
## And then enter the never ending render loop.
pyglet.app.run()
这样,您将不需要按任何键或鼠标来触发 Pyglet 中的事件,它会为您处理并执行您安排的事情。
接下来,是我的一个小优化。这是一个奖励,并且会加快速度。它称为 批量渲染 ,当您渲染大量图像和 sprite 时,您目前一次将一张图像发送到显卡。这是非常 CPU 密集的。您要做的是将工作放在 GPU 上。因为毕竟,您是在处理图形,对吗?
因此,在这种情况下,批量渲染非常简单。每次调用 pyglet.sprite.Sprite
时,它都有一个名为 batch=None
的参数(默认)。如果您向 sprite 对象添加一个批次,则可以通过调用 batch.draw()
而不是每个单独的 sprite.draw()
.
解决方案看起来像这样:
import pyglet
import time
from random import randint
width, height = 800, 600
window = pyglet.window.Window(width, height)
main_batch = pyglet.graphics.Batch()
## Image options are the options we have,
## while `images` are the visible images, this is where we add images
## so that they can be rendered later
image_options = ["Image 1.png", "Image 2.png", "Image 3.png"]
images = {}
## Just a helper-function to generate a random image and return it
## as a sprite object (good idea to use sprites, more on that later)
def get_random_image(x=0, y=0):
choice = image_options[randint(0, len(image_options)-1)]
return pyglet.sprite.Sprite(pyglet.image.load(choice), x=x, y=y, batch=main_batch)
def add_image(actual_time_passed_since_last_clock_tick=0):
image = get_random_image(x=randint(0, width), y=randint(0, height))
images[len(images)] = image
## Here, we define the `on_draw` replacement for `window.on_draw`,
## and it's here we'll check if we should add a nother image or not
## depending on how much time has passed.
@window.event
def on_draw():
window.clear()
## Instead of looping over each image in `images`,
## just do:
main_batch.draw()
## Ok, lets start off by adding one image.
## Instead of doing it manually, use the function add_image.
add_image()
## Add the schedule interval of adding a image every two seconds.
pyglet.clock.schedule_interval(add_image, 2)
## And then enter the never ending render loop.
pyglet.app.run()
我还对add_image
和get_random_image
做了一些修改,主要是为了让你知道图像在函数中应该在什么位置,因为pyglet.sprite.Sprite
还需要另外两个参数,x
和 y
。所以在你创建精灵之后改变 x
和 y
是没有意义的,除非你想在之后移动它们(例如,在 pyglet.clock.schedule_interval
调用中)。