oujood.com

Pymunk - Bibliothèque Python pour la simulation de la physique 2D

Dans ce tutoriel, nous explorerons comment développer des applications en adoptant une approche orientée objet avec le framework de simulation physique 2D Pymunk

Introduction : la biblioteque Pymunk

Pymunk, une bibliothèque Python pour la simulation de la physique 2D, offre une facilité d'utilisation remarquable. Elle se révèle particulièrement utile chaque fois que vous avez besoin de simuler des objets rigides en deux dimensions dans votre projet Python, que ce soit un jeu, une démonstration ou une simulation. Cette bibliothèque repose solidement sur Chipmunk, une bibliothèque de physique 2D puissante.

Depuis sa première version lancée en 2007, Pymunk continue d'évoluer activement et est maintenu jusqu'à aujourd'hui, cumulant ainsi plus de 15 années de développement continu.

Pymunk a déjà fait ses preuves dans de nombreux projets, qu'ils soient de grande ou petite envergure. Par exemple, elle a été utilisée par les créateurs de trois jeux lauréats du concours Pyweek, par des dizaines de chercheurs ayant publié des articles scientifiques, et même dans le cadre d'une simulation de voiture autonome ! Vous pouvez consulter la section "Vitrines" sur la page Web de Pymunk pour découvrir quelques exemples concrets de son utilisation.

Installation

Pour l'installation, dans la plupart des cas, vous pouvez simplement utiliser pip pour récupérer Pymunk depuis PyPI :

 
pip install pymunk

Si vous préférez utiliser conda, Pymunk est également disponible sur le canal conda-forge et peut être installé ainsi :

 
conda install -c conda-forge pymunk

À propos de la désignation des variables

Avant de commencer, familiarisez-vous avec certaines conventions utilisées dans ce tutoriel. Afin de rendre les programmes simples et courts, nous utiliserons des noms de variables courts.

  • b signifie Corps
  • c signifie Contrainte
  • s signifie Forme

Une classe importante est la classe Vec2d, qui indique soit la position absolue d'un point dans l'espace, soit le vecteur de direction entre deux points.

  • p signifie position
  • v signifie vecteur

Nous pourrions définir un vecteur comme la différence entre deux points dans l'espace :

 
v = p1 - p0

Enfin, s sert de marqueur au pluriel.

  • bs est une liste de corps
  • ps est une liste de positions
  • vs est une liste de vecteurs

Nous pouvons utiliser le marqueur au pluriel dans une structure de boucle comme ceci :

 
for b in bs:
print(b)

Le corps statique est fréquemment utilisé, donc nous lui donnons le nom court b0

 
b0 = space.static_body

À propos de la désignation des variables

Avant de commencer, familiarisez-vous avec certaines conventions utilisées dans ce tutoriel. Afin de rendre les programmes simples et courts, nous utiliserons des noms de variables courts.

  • b signifie Corps
  • c signifie Contrainte
  • s signifie Forme

Une classe importante est la classe Vec2d, qui indique soit la position absolue d'un point dans l'espace, soit le vecteur de direction entre deux points.

  • p signifie position
  • v signifie vecteur

Nous pourrions définir un vecteur comme la différence entre deux points dans l'espace :

 
v = p1 - p0

Enfin, s sert de marqueur au pluriel.

  • bs est une liste de corps
  • ps est une liste de positions
  • vs est une liste de vecteurs

Nous pouvons utiliser le marqueur au pluriel dans une structure de boucle comme ceci :

 
for b in bs:
print(b)

Le corps statique est fréquemment utilisé, donc nous lui donnons le nom court b0

 
b0 = space.static_body

Le corps abstrait

La classe Body décrit les aspects physiques d'un objet. Ces aspects ne peuvent pas être vus, mais décrivent comment il se déplace. Six propriétés décrivent l'état d'un corps :

  • mass - la masse du corps
  • moment - sa résistance à la rotation
  • position - sa localisation spatiale
  • angle - l'orientation actuelle
  • velocity - à quelle vitesse et dans quelle direction il se déplace
  • angular_velocity - à quelle vitesse et dans quelle direction il tourne
  • force La force appliqué au corps
  • torque Le moment de force appliqué au corps

Exemples

Une balle rebondissante

Nous commençons ce tutoriel avec une simple simulation de balle rebondissante. La première chose que nous devons faire est d'importer les modules pymunk et pygame :

 
import pymunk
import pymunk.pygame_util
import pygame

Ensuite, nous initialisons le module Pygame et définissons la surface de l'écran où nous allons dessiner le résultat de la simulation. Pymunk propose une option de dessin simple qui peut être utilisée pour un prototypage rapide :

 
pygame.init()
size = 640, 240
screen = pygame.display.set_mode(size)
draw_options = pymunk.pygame_util.DrawOptions(screen)

La simulation de physique en 2D se déroule dans un objet Space. Nous définissons l'espace comme une variable globale et lui attribuons un vecteur de gravité :

 
space = pymunk.Space()
space.gravity = 0, -900

Pour créer un sol fixe pour notre objet, nous créons une forme Segment attachée au corps statique b0. Afin de faire rebondir la balle, nous lui donnons une élasticité de 1 :

 
b0 = space.static_body
segment = pymunk.Segment(b0, (0, 0), (640, 0), 4)
segment.elasticity = 1

Ensuite, nous créons un corps dynamique et lui attribuons une masse, un moment et une position :

 
body = pymunk.Body(mass=1, moment=10)
body.position = 100, 200

Ensuite, nous créons une forme de cercle et l'attachons au corps :

 
circle = pymunk.Circle(body, radius=20)
circle.elasticity = 0.95

Enfin, nous ajoutons le corps, le cercle et le segment à l'espace. Maintenant, nous sommes prêts pour la simulation :

 
space.add(body, circle, segment

Dans la dernière partie, nous démarrons la boucle d'événements Pygame. Le seul événement que nous allons détecter est l'événement QUIT :

 
running = True
while running:
    for event in pygame.event.get():
        if event.type == pygame.QUIT:
            running = False

Dans la dernière partie de la boucle d'événements, nous dessinons les objets. Tout d'abord, nous remplissons l'écran avec une couleur de fond grise. Ensuite, nous dessinons les deux objets avec la fonction space.debug_draw(), appelons la fonction de mise à jour de l'affichage, et enfin, faisons progresser la simulation de 0,01 unités de temps :

 
    screen.fill(GRAY)
    space.debug_draw(draw_options)
    pygame.display.update()
    space.step(0.01)
balle rebondissante

pygame.quit()

Création d'une classe App

Pour simplifier les exemples du tutoriel, nous allons créer une classe App réutilisable qui exécutera la simulation. Cette classe effectuera les opérations suivantes :

  • Initialisation de Pygame
  • Création d'un objet écran
  • Création d'un objet espace
  • Définition de l'option de dessin
  • Exécution de la boucle d'événements
  • Dessin des objets à l'écran

Import des bibliothèques nécessaires

 
import pymunk  # PyMunk pour la simulation physique
import pymunk.pygame_util  # Module d'intégration PyMunk avec Pygame
import pygame  # Pygame pour la création de la fenêtre d'affichage
import math  # Module math pour les calculs mathématiques

# Couleurs utilisées

GRAY = (220, 220, 220)  # Couleur grise pour le fond de la fenêtre

# Initialisation de l'espace de simulation

space = pymunk.Space()  # Crée un espace de simulation PyMunk
space.gravity = 0, 900  # Définit la gravité (accélération verticale)

Voici la définition de la classe avec la méthode du constructeur :

 
# Création de la fenêtre d'affichage
class App:
    size = 700, 240  # Taille de la fenêtre (largeur x hauteur)

    def __init__(self):
        pygame.init()  # Initialise le module Pygame
        self.screen = pygame.display.set_mode(self.size)  # Crée la fenêtre d'affichage
        self.draw_options = pymunk.pygame_util.DrawOptions(self.screen)  # Options de dessin pour PyMunk
        self.running = True  # Variable pour contrôler l'exécution
        self.clock = pygame.time.Clock()  # Crée une horloge pour limiter le FPS

La classe App possède une méthode run() qui exécute la boucle d'événements de Pygame :

 
    def run(self):
        while self.running:  # Boucle principale
            for event in pygame.event.get():  # Gère les événements Pygame
                if event.type == pygame.QUIT:  # Si l'utilisateur ferme la fenêtre
                    self.running = False  # Met fin à la boucle                    

            self.screen.fill(GRAY)  # Remplit l'écran avec la couleur grise
            space.debug_draw(self.draw_options)  # Affiche la simulation PyMunk
            pygame.display.update()  # Met à jour l'affichage
            space.step(1.0 / 60.0)  # Avance la simulation d'un pas de temps (60 FPS)
            self.clock.tick(60)  # Limite le FPS à 60 images par seconde

        pygame.quit()  # Ferme Pygame à la fin de la boucle

Une balle qui roule en descendant sur un plan incliné

Nous avons enregistrer la classe dans un fichier python "app_class.py" dans le même répértoite que le fichier de notre application.

Nous pouvons maintenant importer pymunk, space et la classe App :

Bloc de code Python pour l'importation de la classe

 
from app_class import *

Définissons maintenant un segment incliné et attribuons-lui du frottement :

Bloc de code Python pour la définition du segment

 
# Création du plan incliné
b0 = space.static_body
segment = pymunk.Segment(b0, (100, 100), (600, 200), 4)
segment.elasticity = 0.5
segment.friction = 0.5
space.add(segment)

La forme du cercle a également besoin de frottement pour pouvoir rouler. Sans frottement, il glisserait simplement sur la pente :

Bloc de code Python pour la définition du cercle

 
# Création du corps de la balle et position initiale
ball_body = pymunk.Body(mass=1, moment=10)
ball_body.position = (110, 90)  # Position initiale en haut du plan incliné

# Création de la balle attachée au corps
ball = pymunk.Circle(ball_body, radius=20)
ball.elasticity = 0.5
ball.friction = 0.5
space.add(ball_body, ball)

Enfin, nous instancions l'application et appelons la méthode run() :

Bloc de code Python pour l'instanciation de l'application

 
App().run()

Un bloc glissant sur une pente

La classe dispose d’une méthode pour créer des formes de boîte. Sans élasticité, il glisse le long de la pente:Poly

Exemple :       Copier le code

# Importer les modules nécessaires depuis le fichier app_class.py
from app_class import pymunk, space, App

# Créer un segment statique (un mur) avec deux points de départ (20, 120) et d'arrêt (400, 50)
segment = pymunk.Segment(space.static_body, (20, 120), (400, 50), 1)

# Créer un corps (body) avec une masse de 1 et un moment d'inertie de 10
body = pymunk.Body(mass=1, moment=10)

# Positionner le corps à la position (350, 50) dans l'espace
body.position = (350, 50)

# Créer un polygone en forme de boîte (box) attaché au corps (body) avec une taille de (40, 40)
box = pymunk.Poly.create_box(body, (40, 40))

# Ajouter le corps (body), le polygone (box) et le segment (mur) à l'espace (space)
space.add(body, box, segment)

# Créer une instance de la classe App et exécuter l'application
App().run()

Un bloc dévalant une pente

Si l'on ajoute maintenant de l'élasticité à la forme de la boîte, celle-ci dévale la pente.

Exemple :       Copier le code

# Importation des modules et des classes nécessaires depuis le fichier app_class.py
from app_class import pymunk, space, App

# Création d'un segment statique (mur ou sol) avec des points de début (20, 200) et de fin (400, 100), et une épaisseur de 5
segment = pymunk.Segment(space.static_body, (20, 200), (400, 100), 5)
segment.elasticity = 0.95  # Coefficient d'élasticité du segment, ici 0.95

# Création d'un corps physique avec une masse de 1 et un moment d'inertie de 10
body = pymunk.Body(mass=1, moment=10)
body.position = (350, 50)  # Position initiale du corps

# Création d'une boîte attachée au corps avec une taille de (40, 40)
box = pymunk.Poly.create_box(body, (40, 40))
box.elasticity = 0.95  # Coefficient d'élasticité de la boîte, ici 0.95

# Ajout du corps et de la boîte au monde physique (espace)
space.add(body, box, segment)

# Initialisation et exécution de l'application (peut-être une fenêtre de simulation)
App().run()

Une balle dans une boîte

Pour dessiner une boîte fermée dans laquelle des objets peuvent rebondir, nous devons déterminer les 4 points d'angle. À partir de ceux-ci, nous pouvons créer 4 segments. Nous leur donnons une élasticité de 0,999 car une valeur de 1 ou plus peut conduire à un système instable :

Exemple :       Copier le code

 
# Définition des points pour créer un polygone en forme de rectangle
pts = [(10, 10), (690, 10), (690, 230), (10, 230)]

# Boucle pour créer les segments du polygone
for i in range(4):
    # Création d'un segment statique avec des points de début et de fin
    seg = pymunk.Segment(space.static_body, pts[i], pts[(i+1)%4], 2)
    seg.elasticity = 0.999  # Coefficient d'élasticité du segment, ici 0.999
    space.add(seg)  # Ajout du segment à l'espace physique

# Définition de la gravité, ici nulle (les objets ne tombent pas)
space.gravity = 0, 0

Afin de donner à la balle un mouvement latéral initial, nous lui appliquons un vecteur d'impulsion de (100, 0) à l'initialisation :

Exemple :       Copier le code

 
# Boucle pour créer des corps dynamiques avec des cercles
for i in range(40):
    # Création d'un corps physique avec une masse de 1 et un moment d'inertie de 10
    body = pymunk.Body(mass=1, moment=10)
    # Positionnement aléatoire du corps dans une zone définie
    body.position = randint(40, 660), randint(40, 200)
    # Application d'une impulsion aléatoire au corps
    impulse = randint(-100, 100), randint(-100, 100)
    body.apply_impulse_at_local_point(impulse)
    
    # Création d'un cercle attaché au corps avec un rayon de 5
    circle = pymunk.Circle(body, radius=5)
    circle.elasticity = 0.999  # Coefficient d'élasticité du cercle, ici 0.999
    circle.friction = 0.5  # Coefficient de frottement du cercle, ici 0.5
    space.add(body, circle)  # Ajout du corps et du cercle à l'espace physique

# Initialisation et exécution de l'application (peut-être une fenêtre de simulation)
App().run()

Plusieurs particules dans une boîte

Pour simuler un grand nombre de particules dans une boîte, nous commençons par désactiver la gravité. Ensuite, nous créons un grand nombre de particules à des endroits aléatoires et nous leur donnons des impulsions aléatoires comme mouvement de départ

Exemple :       Copier le code

# Importation des modules et des classes nécessaires depuis le fichier app_class.py
from app_class import pymunk, space, App
from random import randint

# Définition des points pour créer un polygone en forme de rectangle
pts = [(10, 10), (690, 10), (690, 230), (10, 230)]

# Boucle pour créer les segments du polygone
for i in range(4):
    # Création d'un segment statique avec des points de début et de fin
    seg = pymunk.Segment(space.static_body, pts[i], pts[(i+1)%4], 2)
    seg.elasticity = 0.999  # Coefficient d'élasticité du segment, ici 0.999
    space.add(seg)  # Ajout du segment à l'espace physique

# Définition de la gravité, ici nulle (les objets ne tombent pas)
space.gravity = 0, 0

# Boucle pour créer des corps dynamiques avec des cercles
for i in range(40):
    # Création d'un corps physique avec une masse de 1 et un moment d'inertie de 10
    body = pymunk.Body(mass=1, moment=10)
    # Positionnement aléatoire du corps dans une zone définie
    body.position = randint(40, 660), randint(40, 200)
    # Application d'une impulsion aléatoire au corps
    impulse = randint(-100, 100), randint(-100, 100)
    body.apply_impulse_at_local_point(impulse)
    
    # Création d'un cercle attaché au corps avec un rayon de 5
    circle = pymunk.Circle(body, radius=5)
    circle.elasticity = 0.999  # Coefficient d'élasticité du cercle, ici 0.999
    circle.friction = 0.5  # Coefficient de frottement du cercle, ici 0.5
    space.add(body, circle)  # Ajout du corps et du cercle à l'espace physique

# Initialisation et exécution de l'application (peut-être une fenêtre de simulation)
App().run()

Articulation à broche: pendule

Une articulation relie deux corps à l'aide d'un lien solide ou d'une goupille. Pour tous les points d'attache statiques, nous utilisons le même qui a sa position par défaut à (0, 0):PinJointspace.static_body
 
b0 = espace.space.static_body

Pour le corps dynamique, nous plaçons une sphère à (100, 10) :

 
body = pymunk.Body(mass=1, moment=10)
body.position = (100, 100)
circle = pymunk.Circle(body, radius=20)

La méthode prend 2 corps et leurs positions locales en argument. Nous plaçons l'ancrage du corps statique à (200, 200) et laissons le corps dynamique à son ancrage par défaut de (0, 0). Cela crée une épingle entre le point statique (200, 200) et le point dynamique (100, 100):PinJointb0body

 
joint = pymunk.constraint.PinJoint(b0, body, (200, 20))

Sous l'effet de la gravité, le pendule commence à se balancer.

Et voici le code complet:

Exemple :       Copier le code

from app_class import pymunk, space, App

b0 = space.static_body


body = pymunk.Body(mass=1, moment=10)
body.position = (100, 10)
circle = pymunk.Circle(body, radius=20)

joint = pymunk.PinJoint(b0, body, (200, 20))
space.add(body, circle, joint)

App().run()

Bienvenue dans ce tutoriel sur Pymunk

Contenu :


Voir aussi nos tutoriel :

fonction substr_count, substr_count

Compte le nombre d'occurrences de segments dans une chaîne

fonction substr_compare, substr_compare

Compare deux chaînes depuis un offset jusqu' une longueur en caractères

fonction chop, chop

Alias de rtrim