我正在关注 The Coding Train 的最新编码挑战。在他的视频中,他使用 p5.js 构建了一个落沙模拟器,我决定使用 pyglet 尝试挑战。
我做了一些测试,但我注意到与his相比,我的程序渲染速度非常慢。我知道我在每一帧都重新绘制网格,但我的代码逻辑与编码火车的逻辑类似,并且他没有遇到同样的问题。
我确信有更好的方法来处理沙子掉落,但由于我刚刚开始学习 pyglet,我不知道如何继续。
这是代码:
import random
import math
# pyglet libraries
import pyglet
from pyglet import shapes
from pyglet.window import mouse
SCREEN_WIDTH = 400
SCREEN_HEIGHT = 400
SAND_SIZE = 10
def make2DArray(rows, cols):
arr = [[0 for y in range(cols)] for x in range(rows)]
for row in range(rows):
for col in range(cols):
arr[row][col] = 0
return arr
window = pyglet.window.Window(SCREEN_WIDTH, SCREEN_HEIGHT)
rows = int(SCREEN_WIDTH / SAND_SIZE)
cols = int(SCREEN_HEIGHT / SAND_SIZE)
grid = make2DArray(rows, cols)
grid[30][20] = 1
@window.event
def on_mouse_drag(x, y, dx, dy, buttons, modifiers):
row = math.floor(x / SAND_SIZE)
col = math.floor(y / SAND_SIZE)
grid[row][col] = 1
@window.event
def on_draw():
global grid
for row in range(rows):
for col in range(cols):
if grid[row][col] == 0:
rectangle = shapes.Rectangle(row*10, col*10, 10, 10, color=(0, 0, 0))
else:
rectangle = shapes.Rectangle(row*10, col*10, 10, 10, color=(255, 255, 255))
rectangle.draw()
# Generating New State
next_state_grid = make2DArray(rows, cols)
for row in range(rows):
for col in range(cols):
current_state = grid[row][col]
if current_state == 1:
below = grid[row][col - 1]
if below == 0 and col != 0:
next_state_grid[row][col - 1] = 1
else:
next_state_grid[row][col] = 1
grid = next_state_grid
pyglet.app.run()
Pyglet 使用硬件加速(OpenGL)来绘制图像。当您调用
rectangle.draw()
时,您正在对 GPU 进行绘制调用,速度非常慢。您应该尽可能减少绘制调用,因为 CPU 和 GPU 之间的通信通常需要时间。
文档明确劝阻您不要使用此函数,而是建议使用batches。 批次将收集您想要绘制的所有内容,然后优化绘制调用的数量。这是文档中的示例,其中他们将所有形状添加到
batch 并使用 batch.draw()
:
import pyglet
from pyglet import shapes
window = pyglet.window.Window(960, 540)
batch = pyglet.graphics.Batch()
circle = shapes.Circle(700, 150, 100, color=(50, 225, 30), batch=batch)
square = shapes.Rectangle(200, 200, 200, 200, color=(55, 55, 255), batch=batch)
rectangle = shapes.Rectangle(250, 300, 400, 200, color=(255, 22, 20), batch=batch)
rectangle.opacity = 128
rectangle.rotation = 33
line = shapes.Line(100, 100, 100, 200, width=19, batch=batch)
line2 = shapes.Line(150, 150, 444, 111, width=4, color=(200, 20, 20), batch=batch)
star = shapes.Star(800, 400, 60, 40, num_spikes=20, color=(255, 255, 0), batch=batch)
@window.event
def on_draw():
window.clear()
batch.draw()
pyglet.app.run()