在 pygame 中绘制像素艺术的有效方法

efficient way to draw pixel art in pygame

我正在 pygame 中制作一个非常简单的像素艺术软件。我的逻辑是创建一个网格 class,它有一个包含 0 的二维列表。当我单击时,网格近似于所选的行和列,并用与颜色对应的数字标记单元格。为简单起见,假设为“1”。

代码运行正常,但速度很慢。如果行数和列数小于或等于 10,它工作完美,但如果它更多,它非常滞后。

我认为问题在于我每次都在更新整个屏幕,而且由于程序必须检查每个单元格,它无法处理更大的列表

import pygame
from grid import Grid
from pincel import Pincel
from debugger import Debugger
from display import Display
from pygame.locals import *

pygame.init()

pygame.mixer_music.load("musica/musica1.wav")
pygame.mixer_music.play(-1)

width = 1300
height = 1300
screen = pygame.display.set_mode((1366, 768), pygame.RESIZABLE)
pygame.display.set_caption("SquareDraw")

#Grid Creator
numberOfRows = 25
numberOfColumns = 25
grid = Grid(numberOfRows, numberOfColumns)

# Medidas
basicX = width / numberOfColumns
basicY = height / numberOfRows

#Tool Creator
pincel = Pincel(2)


#variáveis de controle
running = 1

#Initial values
grid.equipTool(pincel)

#variáveis de controle de desenho
clicking = 0


def drawScreen(screen, grid, rows, columns, basicX, basicY):
    for i in range(rows):
        for j in range(columns):
            if grid[i][j]:
                print('yes')
                print(i, j)
                pygame.draw.rect(screen, (0, 0, 0), (j * basicX, i * basicY, basicX, basicY))



while running:

    screen.fill((255, 255, 255))
    Display.drawScreen(screen, grid.board, grid.getRows(), grid.getColumns(), basicX, basicY)
    pygame.display.flip()


    events = pygame.event.get()
    for event in events:
        if (event.type == pygame.QUIT) or (event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE):
            running = 0
        if event.type == pygame.MOUSEBUTTONDOWN or clicking:
            clicking = 1
            x, y = pygame.mouse.get_pos()
            Debugger.printArray2D(grid.board)
            print('')
            xInGrid = int(x / basicX)
            yInGrid = int(y / basicY)
            grid.ferramenta.draw(grid.board, xInGrid, yInGrid)
            Debugger.printArray2D(grid.board)
            print('')
        if event.type == pygame.MOUSEBUTTONUP:
            clicking = 0
        if event.type == pygame.VIDEORESIZE:
            width = event.w
            height = event.h
            basicX = width / numberOfColumns
            basicY = height / numberOfRows
            print(width, height)


pygame.quit()

class 网格包含二维列表。 class“Pincel”标记单元格,class“调试器”仅用于打印列表或与调试相关的任何内容。

有没有办法只更新被更改的屏幕部分?如果是这样,我如何在我的逻辑中应用它?

提前致谢:)

几件事:

  • 使用网格数组存储屏幕的on\off块。只有在调整屏幕大小时需要完全重绘时才会读取它
  • 当一个新的矩形被打开时,直接在事件处理程序中绘制矩形并更新网格数组。这里不需要重新绘制整个屏幕。
  • 在调整大小事件中,将屏幕模式重置为新大小,然后使用网格数组重绘整个屏幕。这是您唯一需要进行完全重绘的时间。

这是更新后的代码:

import pygame
#from grid import Grid
#from pincel import Pincel
#from debugger import Debugger
#from display import Display
from pygame.locals import *

pygame.init()

#pygame.mixer_music.load("musica/musica1.wav")
#pygame.mixer_music.play(-1)

width = 1000
height = 1000
screen = pygame.display.set_mode((width, height), pygame.RESIZABLE)
pygame.display.set_caption("SquareDraw")

#Grid Creator
numberOfRows = 250  
numberOfColumns = 250
#grid = Grid(numberOfRows, numberOfColumns)
grid = [[0 for x in range(numberOfRows)] for y in range(numberOfColumns)]  # use array for grid: 0=white, 1=black

# Medidas
basicX = width / numberOfColumns
basicY = height / numberOfRows

#Tool Creator
#pincel = Pincel(2)

#xx
running = 1

#Initial values
#grid.equipTool(pincel)

#xx
clicking = 0

def drawScreen(screen, grid, basicX, basicY):  # draw rectangles from grid array
    for i in range(numberOfColumns):
        for j in range(numberOfRows):
            if grid[i][j]:
                #print('yes')
                #print(i, j)
                pygame.draw.rect(screen, (0, 0, 0), (j * basicX, i * basicY, basicX, basicY))

screen.fill((255, 255, 255))  # start screen

while running:
    events = pygame.event.get()
    for event in events:
        if (event.type == pygame.QUIT) or (event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE):
            running = 0
        if event.type == pygame.MOUSEBUTTONDOWN or clicking:  # mouse button down
            clicking = 1
            x, y = pygame.mouse.get_pos()
            #Debugger.printArray2D(grid.board)
            #print('')
            xInGrid = int(x / basicX)
            yInGrid = int(y / basicY)
            grid[yInGrid][xInGrid] = 1  # save this point = 1, for screen redraw (if resize)
            pygame.draw.rect(screen, (0, 0, 0), (xInGrid * basicX, yInGrid * basicY, basicX, basicY))  # draw rectangle
            #grid.ferramenta.draw(grid.board, xInGrid, yInGrid)
            #Debugger.printArray2D(grid.board)
            #print('')
            pygame.display.flip()  # update screen
        if event.type == pygame.MOUSEBUTTONUP:
            clicking = 0
        if event.type == pygame.VIDEORESIZE:  # screen resized, must adjust grid height, width
            width = event.w
            height = event.h
            basicX = width / numberOfColumns
            basicY = height / numberOfRows
            #print(width, height)
            screen = pygame.display.set_mode((width, height), pygame.RESIZABLE)  # reset screen with new height, width
            screen.fill((255, 255, 255))  # clear screen
            drawScreen(screen, grid, basicX, basicY)  # redraw rectangles
            pygame.display.flip()  # update screen


pygame.quit()