簡體   English   中英

在一個Pyglet窗口中有多個精靈

[英]Having multiple sprites in one Pyglet Window

我有下面的代碼,當前在空白的Pyglet窗口上輸出圖像,但是僅輸出一個圖像。 我真的需要每兩秒鍾添加一個新圖像,並且以前的圖像也保持不變。 例如,添加一個圖像,兩秒鍾后添加另一個圖像,並在添加另一個圖像后兩秒鍾。 我已經添加了隨機庫,因此可以添加隨機圖像。

我下面的代碼僅顯示一個圖像-我覺得這卡在繪制部分的某處。

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

您提供的任何幫助或建議,將不勝感激。

您的代碼有一些問題/建議。 首先,以下代碼是多余的:

while True:
     images = ["Image 1.png", "Image 2.png", "Image 3.png"]
     ....
     pyglet.app.run()

因為pyglet.app.run()是一個阻塞調用,也就是說,該循環永遠不會循環-因為pyglet.app.run()本身就是一個循環(稍后會詳細介紹)。
除非您的應用程序崩潰了,但您不處理這些異常,因此即使在那種情況下,也不會重新運行/循環代碼。

其次,永遠不要在循環內定義數組/列表或任何其他內容。 循環通常用於邏輯操作,而不是創建事物。 在大多數情況下,在循環內創建事物很有用,但有時它們通常不帶有if語句。

資源對於計算機而言,設置和內存/硬盤驅動器等均耗費大量資源。因此,建議盡早在任何循環之外創建列表。 例如:

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中。 至少與所有其他邏輯保持分離,因此它不在“添加隨機圖像”和“循環”之間。

現在,您的代碼失敗的主要原因是您認為這會循環,但不會。 再次, 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循環中擴展為另一個, while True循環永遠不會中斷。 有點簡化了 ,但這實際上就是發生的事情。

因此,您的sprite = pyglet.sprite.Sprite(rawImage)永遠不會重新觸發。
然后,要解決第二個大問題,為什么此代碼將永遠無法工作:

你在做:

def on_draw():
    sprite.draw()

但是每個循環都可以通過執行sprite = pyglet.sprite.Sprite(rawImage)用一個新的sprite對象替換舊的sprite對象。 因此,您想要做的是將列表/字典與所有可見圖像保持在循環外部,然后添加到列表/字典中,僅渲染添加的圖像。

很像這樣:

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

現在,這僅在您按下一個鍵或在窗口內按下鼠標時有效。 這是因為這是事件被調度的唯一時間。 而且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中觸發事件,它將為您處理事件並按計划執行。

接下來,是我的部分小優化。 這是一種獎勵,並且會加快處理速度。 這稱為批處理渲染 ,當您渲染大量圖像和精靈時,您當前一次將一張圖像發送到圖形卡。 這非常占用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_imageget_random_image進行了一些更改,主要是為了使您可以知道圖像在函數中應該位於什么位置,因為pyglet.sprite.Spritepyglet.sprite.Sprite其他兩個參數xy 因此,在創建精靈后更改xy毫無意義,除非以后要移動它們(例如,在pyglet.clock.schedule_interval調用中)。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM