logo oujood
🔍

Pymunk : Guide Complet de la Bibliothèque Python pour la Simulation Physique 2D

Maîtrisez Pymunk, le moteur physique 2D le plus utilisé en Python. De l'installation aux simulations avancées avec bodies, shapes et joints.

OUJOOD.COM

Introduction : Découvrez la puissance de Pymunk

Pymunk est une bibliothèque Python de simulation physique 2D qui se distingue par sa facilité d'utilisation et sa puissance. Idéale pour créer des jeux vidéo, des simulations scientifiques ou des démonstrations éducatives, elle permet de simuler des objets rigides en deux dimensions avec un réalisme impressionnant. Pymunk s'appuie sur Chipmunk, un moteur physique 2D reconnu pour ses performances et sa stabilité.

Lancée en 2007, Pymunk bénéficie d'un développement actif continu depuis plus de 18 ans, garantissant compatibilité, stabilité et fonctionnalités modernes. Cette longévité témoigne de la robustesse du projet et de la confiance de la communauté.

Pymunk a fait ses preuves dans des projets de toutes envergures. Elle a notamment été utilisée par les lauréats de trois éditions du concours Pyweek, adoptée par des dizaines de chercheurs pour leurs publications scientifiques, et même intégrée dans des simulations de véhicules autonomes pour l'apprentissage par renforcement. Consultez la section "Showcases" sur le site officiel de Pymunk pour découvrir des cas d'usage inspirants et des démonstrations interactives.

Installation de Pymunk : Méthodes recommandées

L'installation de Pymunk est simple et rapide. Dans la majorité des cas, pip (le gestionnaire de paquets Python) suffit pour récupérer la dernière version stable depuis PyPI :

 
pip install pymunk

Si vous utilisez Anaconda ou Miniconda pour gérer vos environnements Python, Pymunk est également disponible sur le canal conda-forge. Cette méthode est particulièrement recommandée pour les projets scientifiques nécessitant des dépendances complexes :

 
conda install -c conda-forge pymunk

Note importante : Assurez-vous d'avoir Python 3.7 ou supérieur installé sur votre système. Pour vérifier votre version Python, utilisez la commande python --version dans votre terminal.

Conventions de nommage des variables dans ce tutoriel

Pour rendre les exemples de code concis et lisibles, nous adoptons des conventions de nommage courtes inspirées des standards de la communauté Pymunk. Cette approche facilite la compréhension rapide du code tout en respectant les bonnes pratiques.

  • b = Body (Corps) - représente un objet physique avec masse et vélocité
  • c = Constraint (Contrainte) - définit les relations entre les corps
  • s = Shape (Forme) - détermine la géométrie visible et les collisions

La classe Vec2d est fondamentale dans Pymunk. Elle représente soit une position absolue dans l'espace 2D, soit un vecteur directionnel entre deux points :

  • p = position (point dans l'espace)
  • v = vector (vecteur de direction)

Un vecteur se définit mathématiquement comme la différence entre deux positions :

 
v = p1 - p0  # Vecteur allant du point p0 vers le point p1

Pour les collections d'objets, nous ajoutons un s pour indiquer le pluriel :

  • bs = liste de Bodies (corps)
  • ps = liste de positions
  • vs = liste de vecteurs

Ces conventions s'intègrent naturellement dans les boucles d'itération Python :

 
for b in bs:
    print(b.position, b.velocity)  # Affiche position et vitesse de chaque corps

Le corps statique (static body) étant omniprésent dans les simulations Pymunk, nous lui attribuons l'identifiant court b0 :

 
b0 = space.static_body  # Corps statique de l'espace (immobile et de masse infinie)

Le Body : Fondement de la simulation physique

La classe Body (corps) encapsule les propriétés physiques d'un objet dans la simulation. Contrairement aux formes (shapes) qui définissent l'apparence visuelle, le Body décrit comment l'objet se comporte sous l'effet des forces et du mouvement.

Un Body possède huit propriétés essentielles qui définissent son état physique :

  • mass - la masse du corps en kilogrammes (affecte l'inertie)
  • moment - le moment d'inertie (résistance à la rotation angulaire)
  • position - coordonnées (x, y) dans l'espace de simulation
  • angle - orientation angulaire en radians
  • velocity - vecteur de vitesse linéaire (direction et magnitude)
  • angular_velocity - vitesse de rotation angulaire (rad/s)
  • force - force résultante appliquée au centre de masse
  • torque - couple (moment de force) causant la rotation

Astuce importante : Le moment d'inertie doit être calculé en fonction de la forme et de la masse. Pymunk fournit des fonctions helper comme pymunk.moment_for_circle() pour faciliter ces calculs.

Exemples pratiques : Apprenez par la pratique

Première simulation : Une balle rebondissante

Commençons par un exemple simple mais complet : une balle qui tombe et rebondit sur le sol. Cette simulation illustre les concepts fondamentaux de Pymunk.

Étape 1 : Importation des modules

 
import pymunk              # Moteur physique
import pymunk.pygame_util  # Intégration avec Pygame
import pygame              # Bibliothèque graphique

Étape 2 : Initialisation de Pygame et création de la surface d'affichage

Pymunk propose un système de rendu intégré via DrawOptions, idéal pour le prototypage rapide :

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

Étape 3 : Création de l'espace de simulation

L'objet Space est le conteneur de toute simulation physique 2D. Il gère les collisions, applique la gravité et fait évoluer le système dans le temps :

 
space = pymunk.Space()
space.gravity = 0, -900  # Gravité dirigée vers le bas (900 pixels/s²)

Étape 4 : Création du sol statique

Un segment statique sert de sol. L'élasticité de 1.0 produit un rebond parfait sans perte d'énergie :

 
b0 = space.static_body
segment = pymunk.Segment(b0, (0, 0), (640, 0), 4)  # De (0,0) à (640,0), épaisseur 4
segment.elasticity = 1  # Rebond parfait

Étape 5 : Création d'un corps dynamique (la balle)

Un corps dynamique réagit aux forces. La masse et le moment d'inertie déterminent son comportement :

 
body = pymunk.Body(mass=1, moment=10)
body.position = 100, 200  # Position initiale en haut à gauche

Étape 6 : Ajout d'une forme circulaire

La forme (Shape) définit la géométrie visible et les zones de collision :

 
circle = pymunk.Circle(body, radius=20)
circle.elasticity = 0.95  # Légère perte d'énergie à chaque rebond

Étape 7 : Enregistrement dans l'espace

 
space.add(body, circle, segment)  # Ajoute tous les objets à la simulation

Étape 8 : Boucle d'événements principale

Cette boucle gère l'affichage et fait progresser la simulation :

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

    screen.fill(GRAY)              # Efface l'écran
    space.debug_draw(draw_options) # Dessine la simulation
    pygame.display.update()        # Rafraîchit l'affichage
    space.step(0.01)               # Avance de 0.01 seconde

pygame.quit()
Simulation balle rebondissante avec Pymunk

Création d'une classe App réutilisable

Pour éviter la répétition de code, créons une classe App générique qui encapsule toute la logique commune des simulations Pymunk. Cette approche favorise la réutilisabilité et la maintenabilité.

Fonctionnalités de la classe App :

  • Initialisation automatique de Pygame
  • Création de la fenêtre avec dimensions paramétrables
  • Configuration de l'espace physique Pymunk
  • Gestion des options de rendu (DrawOptions)
  • Boucle d'événements avec limitation FPS
  • Rendu automatique des objets physiques

Importation des dépendances

 
import pymunk                 # Moteur physique 2D
import pymunk.pygame_util     # Outils de rendu Pygame
import pygame                 # Bibliothèque graphique
import math                   # Calculs mathématiques

Définition des constantes

GRAY = (220, 220, 220)  # Couleur de fond (gris clair)

Initialisation de l'espace de simulation global

space = pymunk.Space()      # Espace physique partagé
space.gravity = 0, 900      # Gravité vers le bas (900 px/s²)

Définition de la classe App

 
class App:
    size = 700, 240  # Dimensions par défaut de la fenêtre

    def __init__(self):
        pygame.init()  # Initialise tous les modules Pygame
        self.screen = pygame.display.set_mode(self.size)
        self.draw_options = pymunk.pygame_util.DrawOptions(self.screen)
        self.running = True  # Flag de contrôle de la boucle
        self.clock = pygame.time.Clock()  # Horloge pour réguler les FPS

Méthode run() : Boucle principale de simulation

 
    def run(self):
        while self.running:
            for event in pygame.event.get():
                if event.type == pygame.QUIT:
                    self.running = False  # Arrêt propre de l'application

            self.screen.fill(GRAY)              # Nettoyage de l'écran
            space.debug_draw(self.draw_options) # Rendu de la physique
            pygame.display.update()             # Mise à jour de l'affichage
            space.step(1.0 / 60.0)              # Pas de temps fixe (60 FPS)
            self.clock.tick(60)                 # Limite à 60 images/seconde

        pygame.quit()  # Libération des ressources Pygame

Balle roulant sur un plan incliné

Cet exemple démontre l'importance du frottement (friction) dans les simulations physiques. Sans friction, la balle glisserait ; avec friction, elle roule naturellement.

Enregistrez d'abord la classe App dans un fichier app_class.py dans le même répertoire que votre script principal.

Importation de la classe App personnalisée

 
from app_class import *  # Importe pymunk, space, et App

Création du plan incliné avec friction

 
b0 = space.static_body
segment = pymunk.Segment(b0, (100, 100), (600, 200), 4)  # Plan incliné
segment.elasticity = 0.5  # Rebond modéré
segment.friction = 0.5    # Friction nécessaire pour le roulement
space.add(segment)

Création d'une balle avec propriétés de roulement

 
ball_body = pymunk.Body(mass=1, moment=10)
ball_body.position = (110, 90)  # Positionnée en haut du plan

ball = pymunk.Circle(ball_body, radius=20)
ball.elasticity = 0.5  # Légère perte d'énergie
ball.friction = 0.5    # ESSENTIEL : sans friction, la balle glisse
space.add(ball_body, ball)

Lancement de la simulation

 
App().run()

Un bloc glissant sur une pente

Contrairement aux cercles, les formes polygonales peuvent glisser sans rouler. La classe Poly permet de créer des boîtes rectangulaires facilement.

Exemple :     📋 Copier le code

from app_class import pymunk, space, App

# Création d'un segment statique (mur incliné)
segment = pymunk.Segment(space.static_body, (20, 120), (400, 50), 1)

# Corps dynamique : boîte rectangulaire
body = pymunk.Body(mass=1, moment=10)
body.position = (350, 50)

# Forme polygonale (boîte de 40x40 pixels)
box = pymunk.Poly.create_box(body, (40, 40))

# Ajout à l'espace
space.add(body, box, segment)

App().run()

Un bloc dévalant une pente avec rebonds

En ajoutant de l'élasticité au bloc, celui-ci rebondit spectaculairement en dévalant la pente. L'élasticité de 0.95 conserve 95% de l'énergie cinétique à chaque collision.

Exemple :     📋 Copier le code

from app_class import pymunk, space, App

# Segment statique avec haute élasticité
segment = pymunk.Segment(space.static_body, (20, 200), (400, 100), 5)
segment.elasticity = 0.95

# Corps et forme avec haute élasticité
body = pymunk.Body(mass=1, moment=10)
body.position = (350, 50)

box = pymunk.Poly.create_box(body, (40, 40))
box.elasticity = 0.95  # Rebonds énergiques

space.add(body, box, segment)

App().run()

Simulation de particules dans une boîte fermée

Cette simulation crée un conteneur fermé où plusieurs balles peuvent rebondir indéfiniment. L'élasticité proche de 1 (0.999) minimise la perte d'énergie, créant un mouvement quasi-perpétuel.

Création du conteneur rectangulaire

Exemple :     📋 Copier le code

 
# Définition des 4 coins de la boîte
pts = [(10, 10), (690, 10), (690, 230), (10, 230)]

# Création des 4 murs (segments statiques)
for i in range(4):
    seg = pymunk.Segment(space.static_body, pts[i], pts[(i+1)%4], 2)
    seg.elasticity = 0.999  # Quasi-parfait (évite l'instabilité de 1.0)
    space.add(seg)

# Désactivation de la gravité pour mouvement horizontal
space.gravity = 0, 0

Création d'une balle avec impulsion initiale

Exemple :     📋 Copier le code

 
from random import randint

# Création de 40 particules avec positions et vitesses aléatoires
for i in range(40):
    body = pymunk.Body(mass=1, moment=10)
    body.position = randint(40, 660), randint(40, 200)
    
    # Application d'une impulsion initiale aléatoire
    impulse = randint(-100, 100), randint(-100, 100)
    body.apply_impulse_at_local_point(impulse)
    
    circle = pymunk.Circle(body, radius=5)
    circle.elasticity = 0.999
    circle.friction = 0.5  # Légère friction pour interaction réaliste
    space.add(body, circle)

App().run()

Simulation avancée : Gaz de particules

Cette simulation modélise un gaz idéal avec de nombreuses particules en mouvement brownien. C'est un excellent exemple pour comprendre la thermodynamique computationnelle.

Exemple :     📋 Copier le code

from app_class import pymunk, space, App
from random import randint

# Construction du conteneur fermé
pts = [(10, 10), (690, 10), (690, 230), (10, 230)]

for i in range(4):
    seg = pymunk.Segment(space.static_body, pts[i], pts[(i+1)%4], 2)
    seg.elasticity = 0.999
    space.add(seg)

# Environnement sans gravité
space.gravity = 0, 0

# Génération de 40 particules avec vélocités aléatoires
for i in range(40):
    body = pymunk.Body(mass=1, moment=10)
    body.position = randint(40, 660), randint(40, 200)
    
    # Impulsion aléatoire pour mouvement chaotique
    impulse = randint(-100, 100), randint(-100, 100)
    body.apply_impulse_at_local_point(impulse)
    
    circle = pymunk.Circle(body, radius=5)
    circle.elasticity = 0.999
    circle.friction = 0.5
    space.add(body, circle)

App().run()

Contraintes et articulations : Le pendule simple

Les joints (articulations) permettent de relier des corps entre eux. Le PinJoint crée une liaison pivot, idéale pour simuler des pendules, des chaînes ou des mécanismes articulés.

Toutes les articulations statiques utilisent le corps statique de l'espace comme point d'ancrage fixe :

 
b0 = space.static_body  # Point d'ancrage immobile

Création du corps dynamique (masse du pendule)

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

Le PinJoint accepte deux corps et leurs positions locales d'ancrage. L'ancrage statique est placé à (200, 20), créant un pendule de longueur variable :

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

Sous l'effet de la gravité, le pendule oscille naturellement selon les lois de la mécanique.

Code complet du pendule simple

Exemple :     📋 Copier le code

from app_class import pymunk, space, App

# Point d'ancrage statique
b0 = space.static_body

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

# Articulation à broche (PinJoint)
joint = pymunk.PinJoint(b0, body, (200, 20))
space.add(body, circle, joint)

App().run()

Table des matières complète du tutoriel

Bienvenue dans ce guide complet sur Pymunk, le moteur physique 2D de référence en Python !

Contenu :

Par carabde | Mis à jour le 8 novembre 2024