oujood.com

Comment utiliser les boutons de la souri et les touches du clavier pour interagir avec les formes en Pymunk

Dans ce tutoriel, nous allons apprendre comment utiliser la souris et le clavier en Pymunk pour interagir avec les formes et les corps. Vous pouvez créer des objets, les déplacer, les supprimer, et même tirer des balles avec la souris.

Dans ce tutoriel, nous allons apprendre comment utiliser la souris et le clavier en Pymunk pour interagir avec les formes et les corps. Vous pouvez créer des objets, les déplacer, les supprimer, et même tirer des balles avec la souris.

Le fichier de départ

Avant d’attaquer l’utilisation de la souris et du clavier, notre point de départ sera :

Nous créons donc un fichier python, nommez-le comme vous le souhaitez, par exemple, "souri_clavier.py". Il contient les classes suivantes :

La classe Box

Exemple :       Copier le code

import pymunk
from pymunk.pygame_util import *
from pymunk.vec2d import Vec2d

import pygame
from pygame.locals import *
import random

space = pymunk.Space()
b0 = space.static_body
size = w, h = 700, 300

GRAY = (220, 220, 220)
RED = (255, 0, 0)

class Box:
    def __init__(self, p0=(0, 0), p1=(w, h), d=4):
        x0, y0 = p0
        x1, y1 = p1
        ps = [(x0, y0), (x1, y0), (x1, y1), (x0, y1)]
        for i in range(4):
            segment = pymunk.Segment(b0, ps[i], ps[(i+1) % 4], d)
            segment.elasticity = 1
            segment.friction = 1
            space.add(segment)

Le programme répondra aux touches :

  • QUIT pour fermer la fenêtre (la touche X au coin supérieur droit de la fenêtre)
  • Q et ESCAPE pour quitter l'application
  • La touche P pour enregistrer une capture d'écran au format « souri.png ».

La classe App

Exemple :       Copier le code

class App:
    def __init__(self):
        pygame.init()
        self.screen = pygame.display.set_mode(size)
        self.draw_options = DrawOptions(self.screen)
        self.running = True

    def run(self):
        while self.running:
            for event in pygame.event.get():
                self.do_event(event)
            self.draw()
            space.step(0.01)

        pygame.quit()

    def do_event(self, event):
        if event.type == QUIT:
            self.running = False

        elif event.type == KEYDOWN:
            if event.key in (K_q, K_ESCAPE):
                self.running = False

            if event.key == K_p:
                pygame.image.save(self.screen, 'souri.png')

    def draw(self):
        self.screen.fill(GRAY)
        space.debug_draw(self.draw_options)
        pygame.display.update()

On aura le code complet de notre fichier comme suit :

Exemple :       Copier le code

import pymunk
from pymunk.pygame_util import *
from pymunk.vec2d import Vec2d

import pygame
from pygame.locals import *
import random

space = pymunk.Space()
b0 = space.static_body
size = w, h = 700, 300

GRAY = (220, 220, 220)
RED = (255, 0, 0)

class Box:
    def __init__(self, p0=(0, 0), p1=(w, h), d=4):
        x0, y0 = p0
        x1, y1 = p1
        ps = [(x0, y0), (x1, y0), (x1, y1), (x0, y1)]
        for i in range(4):
            segment = pymunk.Segment(b0, ps[i], ps[(i+1) % 4], d)
            segment.elasticity = 1
            segment.friction = 1
            space.add(segment)
class App:
    def __init__(self):
        pygame.init()
        self.screen = pygame.display.set_mode(size)
        self.draw_options = DrawOptions(self.screen)
        self.running = True

    def run(self):
        while self.running:
            for event in pygame.event.get():
                self.do_event(event)
            self.draw()
            space.step(0.01)

        pygame.quit()

    def do_event(self, event):
        if event.type == QUIT:
            self.running = False

        elif event.type == KEYDOWN:
            if event.key in (K_q, K_ESCAPE):
                self.running = False

            if event.key == K_p:
                pygame.image.save(self.screen, 'souri.png')

    def draw(self):
        self.screen.fill(GRAY)
        space.debug_draw(self.draw_options)
        pygame.display.update()
if __name__ == '__main__':
    Box()
    App().run()

La méthode run() de votre code est la boucle principale de l'application. Elle est responsable de gérer les événements, mettre à jour la simulation et dessiner la simulation à l'écran.

Voici une analyse plus détaillée de la méthode run() :

La méthode run() de votre code est la boucle principale de l'application. Elle est responsable de gérer les événements, mettre à jour la simulation et dessiner la simulation à l'écran.

Voici une analyse plus détaillée de la méthode run() :

  • La boucle while permet de répéter l'exécution du code jusqu'à ce que la variable self.running soit définie à False. Cette variable est définie à False lorsque l'utilisateur quitte le programme ou appuie sur une touche spécifique (par exemple, Q ou Esc).
  • La boucle for parcourt tous les événements qui ont été reçus par Pygame. Pour chaque événement, la méthode self.do_event() est appelée pour gérer l'événement.
  • La méthode self.draw() est appelée pour dessiner la simulation à l'écran.
  • La méthode space.step(0.01) de Pymunk est appelée pour mettre à jour la simulation. Le paramètre 0.01 spécifie le pas de temps en secondes.
  • La méthode pygame.quit() est appelée pour quitter Pygame lorsque la boucle principale se termine.

La méthode run() est appelée lorsque l'objet App est créé. Cela permet de démarrer la boucle principale de l'application immédiatement.

La méthode draw()

La méthode draw() de votre code est responsable du dessin de la simulation à l'écran.

Voici une analyse plus détaillée de la méthode draw() :

  • La première ligne remplit l'écran avec la couleur gris.
  • La deuxième ligne utilise la méthode debug_draw() de Pymunk pour dessiner la simulation à l'écran. La variable self.draw_options contient des options de dessin qui peuvent être utilisées pour contrôler l'apparence de la simulation.
  • La troisième ligne met à jour l'écran pour afficher les modifications apportées à l'écran.

Voici quelques exemples d'options de dessin qui peuvent être utilisées avec la méthode debug_draw():

  • DrawOptions.DRAW_SHAPES: Dessine les formes des objets dans la simulation.
  • DrawOptions.DRAW_JOINTS: Dessine les articulations entre les objets dans la simulation.
  • DrawOptions.DRAW_COLLISION_POINTS: Dessine les points de collision entre les objets dans la simulation.

Vous pouvez combiner ces options pour dessiner la simulation de la manière qui vous convient le mieux.

Le code Python "if __name__ == '__main__':"

Le code Python if __name__ == '__main__': Box() App().run() exécute les étapes suivantes :

  1. Vérifie la valeur de la variable spéciale __name__. Si elle est égale à la chaîne '__main__', alors exécute le code à l'intérieur de l'instruction if.
  2. Crée une nouvelle instance de la classe Box.
  3. Appelle la méthode run() sur l'objet App().

Ce code est généralement utilisé pour créer un programme Python autonome. La classe Box pourrait être utilisée pour représenter la fenêtre principale du programme, et la classe App() pourrait être utilisée pour gérer la boucle principale du programme.

Ce qui affiche :

Image de la boîte

Création de balles à des endroits aléatoires

Maintenant que notre boîte est prête, nous pouvons y ajouter ce que l'on veut pour créer notre simulation.

Pour pouvoir créer des cercles, nous définissons la classe Circle que nous ajoutons dans le fichier "souri_clavier.py". La classe Circle est une classe simple qui définit un cercle comme un objet Pymunk.

Code Python :

Exemple :       Copier le code

class Circle:
    def __init__(self, pos, radius=20):
        self.body = pymunk.Body()
        self.body.position = pos
        shape = pymunk.Circle(self.body, radius)
        shape.density = 0.01
        shape.friction = 0.9
        shape.elasticity = 1
        space.add(self.body, shape)
    

Voici une analyse plus détaillée de la classe Circle :

  • Le constructeur de la classe Circle prend deux arguments : la position du cercle et son rayon. Le constructeur crée un nouveau corps Pymunk et le positionne à la position spécifiée. Il crée également une nouvelle forme de cercle Pymunk et l'attache au corps. La densité, la friction et l'élasticité de la forme sont définies aux valeurs spécifiées. Enfin, le corps et la forme sont ajoutés à l'espace Pymunk.
  • Les méthodes et propriétés de la classe Circle peuvent être utilisées pour contrôler le comportement du cercle dans la simulation. Par exemple, vous pouvez utiliser la méthode body.position pour obtenir ou définir la position du cercle. Vous pouvez également utiliser la méthode shape.elasticity pour définir l'élasticité du cercle.

Ensuite, nous ajoutons 8 balles à des endroits aléatoires.

Code Python :

Exemple :       Copier le code

if __name__ == '__main__':
    Box()

    r = 20
    for i in range(8):
        x = random.randint(r, w-r)
        y = random.randint(r, h-r)
        Circle((x, y), r)
    App().run()
    

La ligne "if __name__ == '__main__':" vérifie si le fichier est exécuté directement. Si c'est le cas, alors le code à l'intérieur de l'instruction "if" est exécuté.

La ligne "Box()" crée un nouvel objet Box. Cet objet représente la boîte dans laquelle la simulation va se dérouler.

La boucle "for" crée huit objets Circle de rayons 20 à des positions (x, y) aléatoires. Chaque objet Circle est créé à une position aléatoire dans la simulation.

La ligne "App().run()" démarre la boucle principale de l'application. La boucle principale est responsable de gérer les événements, mettre à jour la simulation et dessiner la simulation à l'écran.

Lorsque vous exécuterez ce code, vous verrez une fenêtre avec une boîte et huit cercles aléatoires.

Image de la boîte et des cercles

Vous pouvez modifier le code pour ajouter plus de cercles à la simulation, ou pour modifier le comportement des cercles.

Voici quelques exemples de modifications que vous pouvez apporter au code :

  • Vous pouvez modifier le rayon des cercles en modifiant la valeur de la variable "r".
  • Vous pouvez modifier le nombre de cercles créés en modifiant la valeur du paramètre "8".
  • Vous pouvez modifier la gravité de la simulation en ajoutant la valeur de la propriété "space.gravity".
  • Vous pouvez ajouter des murs à la simulation en créant des instances de la classe "pymunk.Segment".
  • Vous pouvez ajouter des articulations entre les cercles en créant des instances de la classe "pymunk.PivotJoint".

Vous pouvez également utiliser les événements de Pygame pour contrôler la simulation. Par exemple, vous pouvez utiliser l'événement "KEYDOWN" pour arrêter la simulation lorsque l'utilisateur appuie sur une touche spécifique.

Sélectionner une balle avec la souris

Utilisons maintenant un événement "MOUSEBUTTONDOWN" pour sélectionner une forme active par un clic de souris. Les deux fonctions "from_pygame" et "to_pygame" nous permettent de passer d'une coordonnée pygame à une autre.

Donc, nous allons modifier le code de la classe "App()" pour gérer l'événement de clic de souris comme suit :

Code Python :

Exemple :       Copier le code

class App:
    def __init__(self):
        pygame.init()
        self.screen = pygame.display.set_mode(size)
        self.draw_options = DrawOptions(self.screen)
        self.active_shape = None
        self.selected_shapes = []
        self.running = True

    def run(self):
        while self.running:
            for event in pygame.event.get():
                self.do_event(event)
            self.draw()
            space.step(0.01)

        pygame.quit()

    def do_event(self, event):
        if event.type == QUIT:
            self.running = False

        elif event.type == KEYDOWN:
            if event.key in (K_q, K_ESCAPE):
                self.running = False

            if event.key == K_p:
                pygame.image.save(self.screen, 'boite_et_cercle.png')

        elif event.type == MOUSEBUTTONDOWN:
            p = from_pygame(event.pos, self.screen)
            self.active_shape = None
            for s in space.shapes:
                dist = s.point_query(p)
                if dist.distance < 0:
                    self active_shape = s
    

Voici une analyse du code modifié :

  • La fonction "from_pygame()" convertit les coordonnées de la souris de coordonnées Pygame en coordonnées Pymunk.
  • La méthode "point_query()" renvoie la distance entre le point et la forme, ou None si le point n'est pas à l'intérieur de toute forme.
  • La condition modifiée dist.distance < 0 vérifie si la distance est négative ou si la distance est None.
  • Si la condition est vraie, la variable active_shape est définie sur la forme.

On doit aussi modifier le code de la fonction "draw()" qui dessine la simulation Pymunk sur l'écran et met en évidence la forme active actuelle comme suit :

Code Python :

Exemple :       Copier le code

def draw(self):
    self.screen.fill(GRAY)
    space.debug_draw(self.draw_options)
    if self.active_shape != None:
        s = self.active_shape
        r = int(s.radius)
        p = to_pygame(s.body.position, self.screen)
        pygame.draw.circle(self.screen, RED, p, r, 3)
    pygame.display.update()
    

Voici une analyse de la fonction :

  • self.screen.fill(GRAY) : Remplit l'écran avec la couleur gris.
  • space.debug_draw(self.draw_options) : Dessine la simulation Pymunk sur l'écran en utilisant les options de dessin spécifiées.
  • if self.active_shape != None : Si la forme active actuelle n'est pas None, alors :
    • s = self.active_shape : Obtient la forme active actuelle.
    • r = int(s.radius) : Obtient le rayon de la forme active actuelle.
    • p = to_pygame(s.body position, self.screen) : Convertit la position de la forme active actuelle des coordonnées Pymunk aux coordonnées Pygame.
    • pygame.draw.circle(self.screen, RED, p, r, 3) : Dessine un cercle rouge autour de la forme active actuelle.
  • pygame.display.update() : Met à jour l'affichage Pygame.

La fonction "to_pygame()" convertit les coordonnées Pymunk en coordonnées Pygame. Cela est nécessaire car Pymunk utilise un système de coordonnées différent de Pygame.

Image de la boîte et des cercles

Déplacer la forme active avec les touches

Maintenant qu’une balle est sélectionnée. Nous allons utiliser les touches fléchées du clavier pour la déplacer. Pour cela, nous définissons un dictionnaire dans lequel nous associons les 4 vecteurs unitaires de direction aux 4 touches fléchées. Si la touche pressée est une touche fléchée, nous déplaçons la forme active de 30 pixels dans cette direction :

Voici le code à ajouter après :

Code Python :

Exemple :       Copier le code

            elif event.key == K_p:
                pygame.image.save(self screen, 'mouse.png')
et avant : elif event.type == MOUSEBUTTONDOWN:
    

Code Python :

Exemple :       Copier le code

            keys = {K_LEFT: (-1, 0), K_RIGHT: (1, 0), K_UP: (0, 1), K_DOWN: (0, -1)}
            if event.key in keys:
                v = Vec2d(keys[event.key][0] * 30, keys[event.key][1] * 30)
                if self.active_shape != None:
                    self.active_shape.body.position += v
    

Tirer une balle avec la souris

Nous allons ajouter un code qui lorsque l’on clique sur une balle et on déplace la souris tout en gardant le bouton de la souris enfoncé, ce qui trace une ligne rouge entre la position de la souris et la position de la balle. En relâchant le bouton de la souris, nous relevons la position de la souris et appliquons à la balle une impulsion proportionnelle à la ligne rouge tracée avec la souris, p0 étant la position de l'objet et p1 la position de la souris :

Voici le code Python pour gérer cet événement (MOUSEBUTTONUP) et ajouter MOUSEMOTION :

Code Python :

Exemple :       Copier le code

         elif event.type == MOUSEMOTION:
            self.p = event.pos

        elif event.type == MOUSEBUTTONUP:
            if self.pulling:
                self.pulling = False
                b = self.active_shape.body
                p0 = Vec2d(b.position.x, b.position.y)
                p1 = from_pygame(event.pos, self.screen)
                impulse = 100 * Vec2d(p0[0] - p1[0], p0[1] - p1[1]).rotated(-b.angle)
                b.apply_impulse_at_local_point(impulse)
    

et le code pour dessiner la ligne rouge :

Code Python :

Exemple :       Copier le code

    def draw(self):
        self.screen.fill(GRAY)
        space.debug_draw(self.draw_options)
        if self.active_shape != None:
            s = self.active_shape
            r = int(s.radius)
            p = to_pygame(s.body.position, self screen)
            pygame.draw.circle(self screen, RED, p, r, 3)
            if self.pulling:
                pygame.draw.line(self screen, RED, p, self.p, 3)
                pygame.draw.circle(self screen, RED, self.p, r, 3) 
    

Modifier aussi le code MOUSEBUTTONDOWN comme suit :

Code Python :

Exemple :       Copier le code

        elif event.type == MOUSEBUTTONDOWN:
            p = from_pygame(event.pos, self screen)
            self.active_shape = None
            for s in space.shapes:
                query_info = s.point_query(p)
                shape = query_info.shape
                dist = query_info.distance
                if dist < 0:
                    self.active_shape = s
                    self.pulling = True
    

Ce qui donne le résultat suivant :

Balle tirée

Ajout de nouveaux objets (balles) à la position de la souris ou suppression d’un objet existant

Nous proposons d’utiliser les touches du clavier pour ajouter un nouveau objet (balle) à la position de la souris ou supprimer un objet existant.

Pour ajouter un objet en utilisant la touche « n », Dans la section "do_event()", nous ajoutons le code suivant, dans la section "do_event()":

Code Python :

Exemple :       Copier le code

            elif event.key == K_n:
                p = from_pygame(pygame.mouse.get_pos(), self.screen)
                Circle(p, radius=20)
    

Placer la souris là où vous voulez ajouter une nouvelle balle et taper "n".

Pour supprimer un objet (balle) sélectionner l’objet en question et taper la touche "backspace".

Voici le code de la suppression à ajouter dans la section "do_event()":

Code Python :

Exemple :       Copier le code

            elif event.key == K_BACKSPACE:
                s = self.active_shape
                if s != None:
                    space.remove(s, s.body)
                    self.active_shape = None
    

Voilà, maintenant avec ce tutoriel, vous pouvez utiliser la souris et les touches du clavier pour créer des applications fascinantes.

Télécharger ici le code complet de l’exemple



Voir aussi nos tutoriel :

top

Définit le bord de la marge supérieure pour une boîte placée

Syntaxe JSON

Syntaxe JSON

propriété css background-clip

Indique la zone de l'arrière-plan qui sera colorée