Entrada destacada

Como usar enums en Android, kotlin

Desplazar hacia las coordenadas del mouse - Eventos de ratón en pyagame

Si bien podemos hacer que los objetos se muevan en la pantalla cambiando sus coordenadas y que su dirección de movimiento sea cambiada al chocar con los limites de la pantalla u otros objetos ¿Como alteramos estos movimientos mediante la interacción con el usuario?


Podemos interactuar y controlar lo que sucede en la pantalla mediante el ratón o el teclado. En este tutorial nos enfocaremos a revisar como utilizar los eventos del ratón, que a la vez es el dispositivo de entrada más fácil de implementar en pygame.



Para comenzar creamos la estructura base del programa y una función para dibujar el objeto que moveremos por la pantalla.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
import pygame
import random

COLOR_FONDO = (10, 4, 32)
COLORES_CELULA = [(70, 12, 46), (4, 66, 36)]

pygame.init()

dimensiones = [1000, 800]
pantalla = pygame.display.set_mode(dimensiones)
pygame.display.set_caption("Cell Mov")

def dibujarCelula(centro, radio, color):
    pygame.draw.circle(pantalla, color[0], centro, radio, 0)
    pygame.draw.circle(pantalla, color[1], centro, radio, 1)

def main():
    game_over = False
    clock = pygame.time.Clock()
    centro = [100, 100]
    while not game_over:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                game_over = True

        pantalla.fill(COLOR_FONDO)

        dibujarCelula(centro, 30, COLORES_CELULA)

        pygame.display.flip()
        clock.tick(60)
    pygame.quit()

if __name__ == '__main__':
    main()

Al ejecutar el código anterior se mostrará una pantalla oscura con un circulo con un borde que en los siguientes ejemplos llamaremos célula.

Obtener coordenadas de ratón

Podemos obtener las coordenadas del ratón con la siguiente línea de código.

pos = pygame.mouse.get_pos()

La variable pos es una tupla que almacena las coordenadas en las que se encuentra el puntero del ratón. En el índice 0 se encuentra la coordenada "x" y en el índice 1 la coordenada "y" por lo tanto podemos pasarla como el parámetro centro de la función dibujarCelula(centro, radio, color) .

28
29
        pos = pygame.mouse.get_pos()
        dibujarCelula(pos, 60, COLORES_CELULA)


Como se aprecia en el vídeo el objeto sigue exactamente la poción del puntero del ratón, más adelante veremos como mover la célula hasta cierta posición de la pantalla en la que se haya pulsado con el ratón.
Si deseamos ocultar el puntero del ratón podemos usar el siguiente código antes del bucle principal o antes de llamar a la función main().

pygame.mouse.set_visible(False)

Detectar eventos de los botones del ratón

Existen distintas funciones del módulo mouse con el cual podemos obtener el estado del ratón por ejemplo la función get_pressed que obtiene el estado de los botones del ratón.

34
35
36
37
38
39
40
        mouse = pygame.mouse.get_pressed()
        if(mouse[0] == 1):
            pos = pygame.mouse.get_pos()
            dibujarCelula(pos, 60, COLORES_CELULA)
        if(mouse[2] == 1):
            COLORES_CELULA[0] = colorAleatorio()
            COLORES_CELULA[1] = colorAleatorio()

La variable mouse también es una tupla pero con tres elementos mouse[0] representa el botón izquierdo, mouse[1] representa el botón intermedio y mouse[2] representa el botón derecho. Los valores que puede contener la tupla sólo son 0 ó 1, por ejemplo:

Si el resultado es (0, 0, 1), específica que sólo el botón derecho ha sido pulsado.

Con las modificaciones realizadas ahora se dibuja la célula sólo si se mantiene pulsado el botón izquierdo del ratón y al presionar el botón derecho se generan dos colores aleatorios que se asignan al la lista de colores con los que se dibuja la célula.

La función para crear colores pseudo aleatorios la escribimos antes de la función dibujarCelula.

13
14
15
16
17
def colorAleatorio():
    r = random.randrange(155)
    g = random.randrange(55)
    b = random.randrange(75)
    return (r, g, b)


Si bien el proceso anterior es totalmente funcional la forma más eficiente de gestionar los eventos del ratón es monitoreando la lista de eventos tal como se hace para detectar el cierre de la ventana.

23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
def main():
    game_over = False
    clock = pygame.time.Clock()
    centro = [100, 100]
    press = False
    while not game_over:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                game_over = True
            elif event.type == pygame.MOUSEBUTTONDOWN:
                if event.button == 1:
                    press = True
                if event.button == 3:
                    COLORES_CELULA[0] = colorAleatorio()
                    COLORES_CELULA[1] = colorAleatorio()
            elif event.type == pygame.MOUSEBUTTONUP:
                if event.button == 1:
                    press = False            

        pantalla.fill(COLOR_FONDO)

        if press:
            pos = pygame.mouse.get_pos()
            dibujarCelula(pos, 60, COLORES_CELULA)

        pygame.display.flip()
        clock.tick(60)
    pygame.quit()

Una diferencia de esta forma de implementar los eventos es que MOUSEBUTTONDOWN sólo se va detectar una ves (cuando se presiono el botón) pero si se mantiene presionado ya no será detectado, recordemos que pygame.mouse.get_pressed() devuelve el estado de los botones en todo momento.

Para replicar el mismo comportamiento  nos apoyamos de variables booleanas para guardar el estado y así saber si el botón es pulsado y en que momento es liberado MOUSEBUTTONUP. De esta manera reducimos el procesamiento para que las posiciones del ratón no se obtengan en cada ciclo. 

Ejemplo final

A continuación,  se muestra un ejemplo en el que usamos eventos de ratón para mover la célula a la coordenada en que se hizo click con el ratón y que al hacer click derecho se generen múltiples mini células que se muevan en direcciones aleatorias.


Como se puede apreciar en la imagen y el vídeo anterior, es un ejemplo diferente al del principio de este tutorial ya que en dicho ejemplo el objeto se movía junto al cursor del ratón y en el actual ejemplo el objeto permanece inmóvil hasta que se hace click con el ratón en una nueva posición en la pantalla.

Código fuente CelulaClick.py

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
import pygame
import random

COLOR_FONDO = (10, 4, 32)
COLORES_CELULAS = [(70, 12, 46), (4, 66, 36)]

pygame.init()

dimensiones = [1000, 800]
pantalla = pygame.display.set_mode(dimensiones)
pygame.display.set_caption("Cell drag")


def colorAleatorio():
    r = random.randrange(155)
    g = random.randrange(55)
    b = random.randrange(75)
    return (r, g, b)


def dibujarCelula(centro, radio, color):
    pygame.draw.circle(pantalla, color[0], centro, radio, 0)
    pygame.draw.circle(pantalla, color[1], centro, radio, 1)


def crearCelulas(cantidad, centro):
    lista_celulas = []
    for i in range(0, cantidad):
        lista_celulas.append([[centro[0], centro[1]], random.randrange(4, 12),
                              [random.randrange(-4, 4, 2), random.randrange(-4, 4, 2)],
                              [colorAleatorio(), colorAleatorio()]])
    return lista_celulas

def main():
    game_over = False
    clock = pygame.time.Clock()
    centro = [100, 100]
    objetivo = [200, 300]
    avance = [1, 1]
    lista_celulas = []
    while not game_over:
        for event in pygame.event.get():
            if event.type == pygame.QUIT:
                game_over = True
            elif event.type == pygame.MOUSEBUTTONDOWN:
                if event.button == 1:
                    objetivo = event.pos
                    if objetivo[0] - centro[0] < 0:
                        avance[0] = -1
                    elif objetivo[0] - centro[0] > 0:
                        avance[0] = 1
                    if objetivo[1] - centro[1] < 0:
                        avance[1] = -1
                    elif objetivo[1] - centro[1] > 0:
                        avance[1] = 1
                if event.button == 3:
                    lista_celulas = crearCelulas(10, [centro[0], centro[1]])

        pantalla.fill(COLOR_FONDO)
        dibujarCelula(centro, 30, COLORES_CELULAS)

        if abs(objetivo[0] - centro[0]) != 0:
            centro[0] += avance[0]
        if abs(objetivo[1] - centro[1]) != 0:
            centro[1] += avance[1]

        for cell in lista_celulas:
            dibujarCelula(cell[0], cell[1], cell[3])
            cell[0][0] += cell[2][0]
            cell[0][1] += cell[2][1]

        pygame.display.flip()
        clock.tick(120)
    pygame.quit()

if __name__ == '__main__':
    main()


Explicación del código

Hemos agregado una nueva función crearCelulas(cantidad, centro) la cual crea las mini células con colores distintos las cuales se dibujan inicialmente en la posición que se encuentra la célula principal y como las direcciones en que se moverán se generan aleatoriamente las mini células se moverán en distintas direcciones (arriba, abajo, izquierda, derecha, arriba a la izquierda, etc.).

26
27
28
29
30
31
32
def crearCelulas(cantidad, centro):
    lista_celulas = []
    for i in range(0, cantidad):
        lista_celulas.append([[centro[0], centro[1]], random.randrange(4, 12),
                              [random.randrange(-4, 4, 2), random.randrange(-4, 4, 2)],
                              [colorAleatorio(), colorAleatorio()]])
    return lista_celulas

La variable centro es una lista de dos elementos que representan las coordenadas del centro de la célula, la variable objetivo también es una lista de dos elementos que son las coordenadas a las que la célula se moverá. La lista avance contiene los valores de aumento para la célula y la lista vacía lista_celulas será la que almacene temporalmente la mini células que se creen al hacer click derecho.

37
38
39
40
    centro = [100, 100]
    objetivo = [200, 300]
    avance = [1, 1]
    lista_celulas = []

En la detección de eventos, si el botón izquierdo es presionado, obtenemos la diferencia entre las coordenadas del centro de la célula y las coordenadas del objetivo. Si el resultado es positivo para la diferencia entre las coordenadas 'x' avanzamos a la derecha, si el resultado es negativo entonces avanzamos a la izquierda.
Se hace lo mismo en el caso de las coordenadas en 'y' para saber si avanzar arriba o abajo.

45
46
47
48
49
50
51
52
53
54
55
56
57
            elif event.type == pygame.MOUSEBUTTONDOWN:
                if event.button == 1:
                    objetivo = event.pos
                    if objetivo[0] - centro[0] < 0:
                        avance[0] = -1
                    elif objetivo[0] - centro[0] > 0:
                        avance[0] = 1
                    if objetivo[1] - centro[1] < 0:
                        avance[1] = -1
                    elif objetivo[1] - centro[1] > 0:
                        avance[1] = 1
                if event.button == 3:
                    lista_celulas = crearCelulas(10, [centro[0], centro[1]])

Si el botón derecho del mouse es pulsado entonces lista_celulas es rellenada llamando a la función crearCelulas indicándole que se crearan 10 mini células y que el centro de esas 10 células será la posición actual del mouse.

Después de dibujar la célula, procedemos a calcular la distancia que hay entre el objetivo y la célula. Si el resultado de calcular la distancia es igual a cero quiere decir que la célula ha llegado al objetivo, en el caso contrario hacemos que la célula siga avanzado, la comprobación se hace para ambos ejes (x, y).

-----------------------------------------------------
NOTA: este algoritmos funcionara sólo si el avance es de uno en uno ya que si cambiamos la cantidad seria muy poco probable que la célula se detenga, una forma de solucionar esto es calcular la distancia total y dividirla por un numero dado y que el resultado de está división sea el valor de avance. 
-----------------------------------------------------

60
61
62
63
64
65
        dibujarCelula(centro, 30, COLORES_CELULAS)

        if abs(objetivo[0] - centro[0]) != 0:
            centro[0] += avance[0]
        if abs(objetivo[1] - centro[1]) != 0:
            centro[1] += avance[1]

Finalmente con la ayuda de un bucle for hacemos que las mini células se dispersen por la pantalla.

67
68
69
70
        for cell in lista_celulas:
            dibujarCelula(cell[0], cell[1], cell[3])
            cell[0][0] += cell[2][0]
            cell[0][1] += cell[2][1]


Comenta tus dudas en la sección de comentarios.

Comentarios

  1. porque me dice al ejecutar el ejemplo que la variable 'lusta_celulas' referenced before assignament

    ResponderEliminar
  2. ok, se me arregló solito!!
    gracias por el artículo.

    lo estoy siguiendo, porque estoy intentando algo parecido,como hacer click con el mouse y dejar imprimido en pantalla ls coordenadas del mouse

    ResponderEliminar
    Respuestas
    1. Hola! que bueno que se arreglo el problema. Espero que si te sea de gran ayuda, cualquier duda, puedes dejarla acá. Suerte!

      Eliminar

Publicar un comentario