Solución:
Usar un sueño no es una buena idea en este tipo de situación, ya que ralentiza todo el hilo (que es el programa completo en un modelo de un solo hilo).
Es mejor mantener algún tipo de información de estado sobre la línea y, basándose en tiempos en tiempo real (por ejemplo: milisegundos transcurridos), progrese el “crecimiento” de la línea, segundo a segundo.
Esto significa que la línea debe dividirse en segmentos y el segmento de línea más pequeño es un solo píxel. El uso del algoritmo de línea de punto medio es una forma eficaz de determinar todos los píxeles que se encuentran en una línea. Una vez que se han determinado todas las “partes de la línea”, es posible simplemente actualizar el punto final de la línea en función del tiempo transcurrido.
Aquí hay un código que escribí anteriormente que, dado un par de puntos, devuelve una lista de píxeles.
midpoint.py
:
def __plotLineLow( x0,y0, x1,y1 ):
points = []
dx = x1 - x0
dy = y1 - y0
yi = 1
if dy < 0:
yi = -1
dy = -dy
D = 2*dy - dx
y = y0
for x in range( x0, x1 ):
points.append( (x,y) )
if D > 0:
y = y + yi
D = D - 2*dx
D = D + 2*dy
return points
def __plotLineHigh( x0,y0, x1,y1 ):
points = []
dx = x1 - x0
dy = y1 - y0
xi = 1
if dx < 0:
xi = -1
dx = -dx
D = 2*dx - dy
x = x0
for y in range( y0, y1 ):
points.append( (x,y) )
if D > 0:
x = x + xi
D = D - 2*dy
D = D + 2*dx
return points
def linePoints( pointA, pointB ):
""" Generate a list of integer points on the line pointA -> pointB """
x0, y0 = pointA
x1, y1 = pointB
points = []
if ( abs(y1 - y0) < abs(x1 - x0) ):
if ( x0 > x1 ):
points += __plotLineLow( x1, y1, x0, y0 )
else:
points += __plotLineLow( x0, y0, x1, y1 )
else:
if ( y0 > y1 ):
points += __plotLineHigh( x1, y1, x0, y0 )
else:
points += __plotLineHigh( x0, y0, x1, y1 )
return points
if __name__ == "__main__":
#midPoint( (597, 337), (553, 337) )
print( str( linePoints( (135, 295), (135, 304) ) ) )
Y algún código de demostración que implementa un SlowLine
clase.
import pygame
import random
import time
import sys
from midpoint import linePoints # Midpoint line algorithm
# Window size
WINDOW_WIDTH = 400
WINDOW_HEIGHT = 400
SKY_BLUE = ( 30, 30, 30)
SKY_RED = (200, 212, 14)
# Global millisecond count since start
NOW_MS = 0
class SlowLine():
def __init__( self, pixels_per_second, x0,y0, x1,y1, colour=SKY_RED ):
self.points = linePoints( ( x0, y0 ), ( x1, y1 ) )
self.pixel_count = len( self.points )
self.speed = pixels_per_second
self.start_point = self.points[0] # start with a single-pixel line
self.end_point = self.points[0]
self.pixel_cursor = 0 # The current end-pixel
self.last_update = 0 # Last time we updated
self.colour = colour
self.fully_drawn = False
def update(self):
global NOW_MS
if ( self.fully_drawn == True ):
# nothing to do
pass
else:
# How many milliseconds since the last update() call?
if ( self.last_update == 0 ):
self.last_update = NOW_MS
time_delta = 0
else:
time_delta = NOW_MS - self.last_update
self.last_udpate = NOW_MS
# New pixels to add => speed * time
new_pixel_count = time_delta * self.speed / 1000 # this may loose precision with very small speeds
if ( new_pixel_count + self.pixel_cursor > self.pixel_count ):
# We're out of pixels
self.end_point = self.points[-1]
self.full_drawn = True
else:
# Grow the line by <new_pixel_count> pixels
self.pixel_cursor += new_pixel_count
self.end_point = self.points[ int( self.pixel_cursor ) ]
def draw( self, screen ):
pygame.draw.line( screen, self.colour, self.start_point, self.end_point )
### MAIN
pygame.init()
SURFACE = pygame.HWSURFACE|pygame.DOUBLEBUF|pygame.RESIZABLE
WINDOW = pygame.display.set_mode( ( WINDOW_WIDTH, WINDOW_HEIGHT ), SURFACE )
pygame.display.set_caption("Slow Line Movement")
# Create some random lines
lines = []
for i in range( 20 ):
rand_speed = random.randint( 1, 50 )
rand_x0 = random.randint( 0, WINDOW_WIDTH )
rand_y0 = random.randint( 0, WINDOW_HEIGHT )
rand_x1 = random.randint( 0, WINDOW_WIDTH )
rand_y1 = random.randint( 0, WINDOW_HEIGHT )
lines.append( SlowLine( rand_speed, rand_x0, rand_y0, rand_x1, rand_y1 ) )
# Main event loop
clock = pygame.time.Clock()
done = False
while not done:
NOW_MS = pygame.time.get_ticks()
# Update the line lengths
for l in lines:
l.update()
# Handle user-input
for event in pygame.event.get():
if ( event.type == pygame.QUIT ):
done = True
# Movement keys
keys = pygame.key.get_pressed()
if ( keys[pygame.K_UP] ):
print("up")
elif ( keys[pygame.K_DOWN] ):
print("down")
elif ( keys[pygame.K_LEFT] ):
print("left")
elif ( keys[pygame.K_RIGHT] ):
print("right")
elif ( keys[pygame.K_q] and ( keys[pygame.K_RCTRL] or keys[pygame.K_LCTRL] ) ):
print("^Q")
done = True
# Update the window, but not more than 60fps
WINDOW.fill( SKY_BLUE )
for l in lines:
l.draw( WINDOW )
pygame.display.flip()
# Clamp FPS
clock.tick_busy_loop(60)
pygame.quit()
En esta animación, el progreso es un poco desigual, pero esa es la animación, no la demostración.
Usted debe display.flip()
para actualizar la pantalla y Deje que los eventos de la ventana se procesen usando event.get()
:
def draw_red_line(i):
y = 0
while y < 300:
pygame.draw.line(screen, RED, (i*100+50, 0), (i*100+50, y))
pygame.display.flip()
pygame.event.get()
y+=1
Si desea que un dibujo sea visible en la pantalla, debe actualizar la pantalla (p. Ej. pygame.display.flip()
), y tienes que manejar los eventos por pygame.event.get()
o pygame.event.pump()
.
También tenga en cuenta que los parámetros para pygame.draw.line
debe ser integral. Usar round
para convertir un valor de coma flotante en un valor integral.
Dibujar la línea en un bucle y actualizar la pantalla no hace lo que desea porque la línea se dibuja sin demora. No recomiendo crear animaciones en un ciclo separado dentro del ciclo principal de la aplicación. Utilice el bucle principal de la aplicación para trazar la línea.
Cree una función que pueda dibujar una línea desde un start
apuntar a un end
punto, dependiente de un valor p en rango [0.0, 1.0]. Si el valor es 0, no se traza ninguna línea. Si el valor es 1, se traza la línea completa. De lo contrario, se dibujará parte de la línea:
def draw_red_line(surf, color, start, end, w):
xe = start[0] * (1-w) + end[0] * w
ye = start[1] * (1-w) + end[1] * w
pygame.draw.line(surf, color, start, (round(xe), round(ye)))
Utilice esta función en el bucle principal de la aplicación:
w = 0
while True:
# [...]
draw_red_line(window, (255, 0, 0), line_start[i], line_end[i], w)
if w < 1:
w += 0.01
Véase también Forma y contorno.
Ejemplo mínimo:
import pygame
pygame.init()
window = pygame.display.set_mode((300,300))
clock = pygame.time.Clock()
line_start = [(100, 0), (200, 0), (0, 100), (0, 200)]
line_end = [(100, 300), (200, 300), (300, 100), (300, 200)]
def draw_red_line(surf, color, start, end, w):
xe = start[0] * (1-w) + end[0] * w
ye = start[1] * (1-w) + end[1] * w
pygame.draw.line(surf, color, start, (round(xe), round(ye)))
count=0
run = True
while run:
clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
window.fill(0)
for i in range(int(count)):
draw_red_line(window, (255, 255, 255), line_start[i], line_end[i], 1)
if count < 4:
i = int(count)
draw_red_line(window, (255, 0, 0), line_start[i], line_end[i], count-i)
count += 0.01
else:
count = 0
pygame.display.flip()
pygame.quit()
exit()