我目前正在尝试在 python/pygame(来自 java,来自编码训练视频的 p5:https://www.youtube.com/watch?v=BZUdGqeOD0w)中复制水波纹效果。
我无法使其工作,屏幕保持空白(黑色:背景色)或者我给出错误:
Traceback (most recent call last): File "/Users/vicki/Documents/Atom Test/Water Ripple.py", line 39, in <module> screen.fill((current[i][j],current[i][j],current[i][j]),(i,j,1,1)) TypeError: invalid color argument
我实际上发现这是正常的,因为我们从 0 开始计算一个正数来获取像素的颜色:
current[i][j] = (previous[i-1][j]+previous[i+1][j]+ previous[i][j+1]+ previous[i][j-1])/ 2 - current[i][j]
:上一个[i-1][j]
、上一个[i+1][j]
、上一个[i][j-1 ]
、previous[i][j+1]
均等于 0,而 current[I][j]
为正数,因此从逻辑上讲,它应该给出负颜色值。
也许这与 Python 和 Java 中数组的工作方式有关?
如需更多帮助,请访问coding train 用于制作视频的网站:https://web.archive.org/web/20160418004149/http://freespace.virgin.net/hugo.elias/graphics/x_water.htm
这是我的代码:
import pygame
pygame.init()
width = 200
height = 200
cols = width
rows = height
#dampening = how fast the ripple effect stops.
dampening = 0.999
#Arrays that hold the colors of the screen.
current = [[0]*cols]*rows
previous = [[0]*cols]*rows
screen = pygame.display.set_mode((width,height))
#Sets the initial background to black
screen.fill((0,0,0))
#Mainloop
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
exit()
if event.type == pygame.MOUSEBUTTONDOWN:
#if the user clicks the screen, it sets the pixel the user clicked upon white
mouse_pos = pygame.mouse.get_pos()
current[mouse_pos[0]][mouse_pos[1]] = 255
#This part seems to be the problem
for i in range(1,rows-1):
for j in range(1,cols-1):
#This computation is weird: we are substracting positive/zero number from a null number which should normally give us a negative number. A color value (RGB) cannot be a negative number!
current[i][j] = (previous[i-1][j]+previous[i+1][j]+ previous[i][j+1]+ previous[i][j-1])/ 2 - current[i][j]
current[i][j] *= dampening
#This line is where it gives me the error
screen.fill((current[i][j],current[i][j],current[i][j]),(i,j,1,1))
#Switching the arrays, have also tried: previous, current = current, previous (doesn't work either)
temp = previous
previous = current
current = temp
pygame.display.flip()
这里是编码火车的代码(在java P5中): (注意:他使用了 mousedraft 而不是 mouseTouch(在视频中他使用了 mousetouch),但这并不重要。)
// Daniel Shiffman
// http://codingtra.in
// http://patreon.com/codingtrain
// 2D Water Ripples
// Video: https://youtu.be/BZUdGqeOD0w
// Algorithm: https://web.archive.org/web/20160418004149/http://freespace.virgin.net/hugo.elias/graphics/x_water.htm
int cols;
int rows;
float[][] current;// = new float[cols][rows];
float[][] previous;// = new float[cols][rows];
float dampening = 0.99;
void setup() {
size(600, 400);
cols = width;
rows = height;
current = new float[cols][rows];
previous = new float[cols][rows];
}
void mouseDragged() {
previous[mouseX][mouseY] = 500;
}
void draw() {
background(0);
loadPixels();
for (int i = 1; i < cols-1; i++) {
for (int j = 1; j < rows-1; j++) {
current[i][j] = (
previous[i-1][j] +
previous[i+1][j] +
previous[i][j-1] +
previous[i][j+1]) / 2 -
current[i][j];
current[i][j] = current[i][j] * dampening;
int index = i + j * cols;
pixels[index] = color(current[i][j]);
}
}
updatePixels();
float[][] temp = previous;
previous = current;
current = temp;
}
最佳答案
首先
current = [[0]*cols]*rows previous = [[0]*cols]*rows
不是二维数组。它生成一个列表,然后生成一个外部列表,其中每个元素引用相同的内部列表。
如果你想生成一个列表,其中每个元素都是一个单独的列表,那么你必须:
(内部维度应该是行,例如 current[col][row]
/current[x][y]
)
current = [[0]*rows for col in range(cols)]
previous = [[0]*rows for col in range(cols)]
在 pygame 中,颜色 channel 的值必须是 [0, 255] 范围内的整数值:
val = min(255, max(0, round(current[i][j])))
screen.fill((val, val, val), (i,j,1,1))
此外,MOUSEBUTTONDOWN
事件仅在按下鼠标按钮时发生一次。你必须评估鼠标的当前状态是否被 pygame.mouse.get_pressed()
按下:
while True:
# [...]
if any(pygame.mouse.get_pressed()):
mouse_pos = pygame.mouse.get_pos()
previous[mouse_pos[0]][mouse_pos[1]] = 500
我建议使用pygame.Surface
分别pygame.PixelArray
以提高性能。类似loadPixels()
/updatePixels()
在 Processing :
img = pygame.Surface((width, height))
while True:
# [...]
pixelArray = pygame.PixelArray(img)
for i in range(1,cols-1):
for j in range(1,rows-1):
current[i][j] = (
previous[i-1][j] +
previous[i+1][j] +
previous[i][j-1] +
previous[i][j+1]) / 2 - current[i][j]
current[i][j] *= dampening
val = min(255, max(0, round(current[i][j])))
pixelArray[i, j] = (val, val, val)
pixelArray.close()
screen.blit(img, (0, 0))
遗憾的是,一切仍然非常缓慢。完整示例:
import pygame
pygame.init()
width = 300
height = 200
cols = width
rows = height
#dampening = how fast the ripple effect stops.
dampening = 0.999
#Arrays that hold the colors of the screen.
current = [[0]*rows for col in range(cols)]
previous = [[0]*rows for col in range(cols)]
print(current[0][0], current[199][199])
screen = pygame.display.set_mode((width,height))
#Sets the initial background to black
screen.fill((0,0,0))
img = pygame.Surface((width, height))
#Mainloop
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
exit()
if any(pygame.mouse.get_pressed()):
mouse_pos = pygame.mouse.get_pos()
previous[mouse_pos[0]][mouse_pos[1]] = 500
#This part seems to be the problem
pixelArray = pygame.PixelArray(img)
for i in range(1,cols-1):
for j in range(1,rows-1):
current[i][j] = (
previous[i-1][j] +
previous[i+1][j] +
previous[i][j-1] +
previous[i][j+1]) / 2 - current[i][j]
current[i][j] *= dampening
val = min(255, max(0, round(current[i][j])))
pixelArray[i, j] = (val, val, val)
pixelArray.close()
# Switching the arrays
previous, current = current, previous
screen.blit(img, (0, 0))
pygame.display.flip()
为了获得真正好的性能,您必须使用 NumPy 。在下面的示例中,我使用 pygame.time.Clock.tick
限制了每秒帧数:
import numpy
import pygame
import scipy
pygame.init()
window = pygame.display.set_mode((300, 300))
clock = pygame.time.Clock()
size = window.get_size()
dampening = 0.999
current = numpy.zeros(size, numpy.float32)
previous = numpy.zeros(size, numpy.float32)
kernel = numpy.array([[0.0, 0.5, 0], [0.5, 0, 0.5], [0, 0.5, 0]])
run = True
while run:
clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
if any(pygame.mouse.get_pressed()):
mouse_pos = pygame.mouse.get_pos()
previous[mouse_pos] = 1000
# either:
# current = (scipy.ndimage.convolve(previous, kernel) - current) * dampening
# or:
current[1:size[0]-1, 1:size[1]-1] = (
(previous[0:size[0]-2, 0:size[1]-2] +
previous[2:size[0], 0:size[1]-2] +
previous[0:size[0]-2, 2:size[1]] +
previous[2:size[0], 2:size[1]]) / 2 -
current[1:size[0]-1, 1:size[1]-1]) * dampening
array = numpy.transpose(255 - numpy.around(numpy.clip(current, 0, 255)))
array = numpy.repeat(array.reshape(*size, 1).astype('uint8'), 3, axis = 2)
image = pygame.image.frombuffer(array.flatten(), size, 'RGB')
previous, current = current, previous
window.blit(image, (0, 0))
pygame.display.flip()
pygame.quit()
exit()
关于python - 水波纹效果 Python 和 Pygame,来自编码火车视频,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60336688/