Comparaison binaire des n premiers caractères
Apprenez à utiliser Python, Pygame et PyMunk pour créer une simulation de physique interactive. Découvrez comment modéliser des objets, des joints, des contraintes et des interactions physiques dans ce tutoriel
Dans Pymunk, une contrainte est une relation entre deux corps rigides qui limite leur mouvement l'un par rapport à l'autre. Les contraintes sont utilisées pour créer des joints, tels que des joints pivotants, des joints coulissants et des joints de glissement, ainsi que pour appliquer des forces et des moments aux corps.
Les contraintes sont essentielles pour créer des simulations physiques réalistes. Elles permettent de s'assurer que les corps se comportent de manière cohérente avec les lois de la physique. Par exemple, une contrainte pivotante permet à deux corps de tourner l'un autour de l'autre, tandis qu'une contrainte coulissante permet à deux corps de se déplacer l'un par rapport à l'autre le long d'une ligne.
Pymunk fournit une variété de contraintes différentes, chacune avec ses propres propriétés et fonctionnalités. Les contraintes peuvent être personnalisées pour répondre aux besoins spécifiques d'une simulation.
Comme nous avons fait dans les chapitres précédents, nous allons adopter l’approche orientée objet. Pour cela, nous allons créer un module avec des classes que nous pouvons utiliser chaque fois que nous en avons besoin.
Découvrez comment créer des joints d'articulation (PinJoint), des segments, des cercles et des rectangles dans une simulation Pymunk.
Exemple : Copier le code
# Importation des bibliothèques nécessaires import pymunk from pymunk.pygame_util import * from pymunk.vec2d import Vec2d import pygame from pygame.locals import * import math from PIL import Image # Crée un espace de simulation PyMunk space = pymunk.Space() space.gravity = 0, -900 b0 = space.static_body # Corps statique pour créer le sol # Définit la taille de la fenêtre Pygame size = w, h = 400, 200 fps = 30 # Images par seconde pour la simulation steps = 10 # Nombre d'étapes de simulation par trame BLACK = (0, 0, 0) GRAY = (220, 220, 220) WHITE = (255, 255, 255) # Classe pour créer un joint d'articulation (PinJoint) entre deux corps class PinJoint: def __init__(self, b, b2, a=(0, 0), a2=(0, 0)): # Crée un joint d'articulation PinJoint entre deux corps b et b2 joint = pymunk.PinJoint(b, b2, a, a2) space.add(joint) # Ajoute le joint à l'espace de simulation # Classe pour créer un segment dans la simulation class Segment: def __init__(self, p0, v, radius=10): # Crée un corps pour le segment self.body = pymunk.Body() self.body.position = p0 # Positionne le corps au point p0 # Crée un segment attaché au corps shape = pymunk.Segment(self.body, (0, 0), v, radius) shape.density = 0.1 shape.elasticity = 0.5 shape.filter = pymunk.ShapeFilter(group=1) shape.color = (0, 255, 0, 0) # Couleur du segment # Ajoute le corps et la forme à l'espace space.add(self.body, shape) # Classe pour créer un cercle dans la simulation class Circle: def __init__(self, pos, radius=20): # Crée un corps pour le cercle self.body = pymunk.Body() self.body.position = pos # Positionne le corps au point pos # Crée un cercle attaché au corps shape = pymunk.Circle(self.body, radius) shape.density = 0.01 shape.friction = 0.5 shape.elasticity = 1 # Ajoute le corps et la forme à l'espac space.add(self.body, shape)e # Classe pour créer un rectangle dans la simulation class Rectangle: def __init__(self, pos, size=(80, 50): # Crée un corps pour le rectangle self.body = pymunk.Body() self.body.position = pos # Positionne le corps au point pos # Crée un rectangle attaché au corps shape = pymunk.Poly.create_box(self.body, size) shape.density = 0.1 shape.elasticity = 1 shape.friction = 1 # Ajoute le corps et la forme à l'espace space.add(self.body, shape) # Classe principale de l'application class App: def __init__(self): pygame.init() self.clock = pygame.time.Clock() self.screen = pygame.display.set_mode(size) self.draw_options = DrawOptions(self.screen) self.running = True self.gif = 0 self.images = [] def run(self): while self.running: for event in pygame.event.get(): self.do_event(event) self.draw() self.clock.tick(fps) for i in range(steps): space.step(1/fps/steps) pygame.quit() def do_event(self, event): if event.type == QUIT: self.running = False if event.type == KEYDOWN: if event.key in (K_q, K_ESCAPE): self.running = False elif event.key == K_p: pygame.image.save(self.screen, 'joint.png') elif event.key == K_g: self.gif = 60 def draw(self): self.screen.fill(GRAY) space.debug_draw(self.draw_options) pygame.display.update() text = f'fpg: {self.clock.get_fps():.1f}' pygame.display.set_caption(text) self.make_gif() def make_gif(self): if self.gif > 0: strFormat = 'RGBA' raw_str = pygame.image.tostring(self.screen, strFormat, False) image = Image.frombytes( strFormat, self.screen.get_size(), raw_str) self.images.append(image) self.gif -= 1 if self.gif == 0: self.images[0].save('joint.gif', save_all=True, append_images=self.images[1:], optimize=True, duration=1000//fps, loop=0) self.images = [] if __name__ == '__main__': # Crée un objet de type Box pour définir le sol de la simulation Box()
Ce code est un programme Python utilisant les bibliothèques Pygame et PyMunk (un wrapper pour le moteur physique Chipmunk). Il contient des classes pour créer une simulation de physique. Voici un résumé des principales fonctionnalités de ce programme :
En exécutant ce code, une simulation physique sera générée, et vous pourrez interagir avec la simulation via la fenêtre Pygame. La simulation comprend des objets physiques représentés par des classes telles que des segments, des cercles, des polygones et des rectangles, ainsi que des joints pour les connecter.
Dans la classe App nous avons défini une fonction make_gif() qui va nous permettre d’enregistrer des images de notre application en utilisant la bibliothèque PIL pour enregistrer des images.
Nous avons aussi défini un gestionnaire d’événement do_event() qui permet :~
En tapant les touches :
p pour enregistrer une image statique PNG et g pour une image GIF.
q ou ESCAP pour quitter l’application.
Une articulation à broche (ou Pin Joint) en Pymunk est une contrainte entre deux corps rigides qui les maintient alignés. Les articulations à broche sont utilisées pour créer des liens rigides entre des objets, tels que les pièces d'une machine ou les articulations d'un personnage.
Pour créer une articulation à broche en Pymunk, vous utilisez la méthode PinJoint(). La méthode PinJoint() prend les arguments suivants :
Pour cet exemple, nous avons créé (dans le fichier ‘articulation.py’) une classe PinJoint qui relie les deux corps b et b2 à leurs points d'ancrage a et a2 via un pin joint. En ajoutant le nouveau joint directement à l'espace, nous économisons une ligne de code.
Exemple : Copier le code
classe PinJoint : def __init__(self, b, b2, a=(0, 0), a2=(0, 0)) : joint = pymunk.PinJoint(b, b2, a, a2) space.add(joint)
Nous définissons le corps statique b0 qui sera utilisé pour tout ce qui est statique :
b0 = space.static_body
Nous nommons les points de position p et vecteurs avec v. Le point de suspension du pendule est p et le vecteur initial de l'axe est v :
Nous définissons la gravité de notre espace.En inversant la direction de la gravité en utilisant space.gravity = 0, 900 pour que les pendules soient correctement suspendues vers le bas.
p = Vec2d(200, 10) v = Vec2d(80, 0) space.gravity = 0, 900
Nous pouvons maintenant définir le premier corps circulaire c et l'attacher au corps statique b0 à la position p à l'aide d'une articulation :
c = Circle(p+v) PinJoint(b0, c.body, p)
Un deuxième corps circulaire c2 est placé à deux fois la distance vectorielle :
c2 = Circle(p+2*v) PinJoint(b0, c2.body, p)
Les deux pendules oscillent à des fréquences différentes.
Voici le code final :
Exemple : Copier le code
from articulation import * p = Vec2d(200, 10) v = Vec2d(80, 0) space.gravity = 0, 900 c = Circle(p+v) PinJoint(b0, c.body, p) c2 = Circle(p+2*v) PinJoint(b0, c2.body, p) App().run()
Une articulation pivotante (ou Pivot joint) en Pymunk est une articulation qui permet à deux corps de pivoter autour d'un point commun. Cette articulation est souvent utilisée pour simuler des articulations dans les jeux vidéo, comme les articulations entre les bras et les jambes d'un personnage.
Pour créer une articulation pivotante en Pymunk, on utilise la méthode PivotJoint. Cette méthode prend en entrée les deux corps à connecter, ainsi que les coordonnées du point commun.
Nous définissons dans notrê module de classes 'articulation.py' une nouvelle classe PivotJoint qui relie les deux corps b et b2 à leurs points d'ancrage a et a2 via une articulation pivot. En ajoutant la nouvelle articulation directement à l'espace, nous économisons une ligne de code.
Exemple : Copier le code
class PivotJoint: def __init__(self, b, b2, a=(0, 0), a2=(0, 0), collide=True): joint = pymunk.constraint.PinJoint(b, b2, a, a2) joint.collide_bodies = collide space.add(joint)
Nous définissons le premier segment avec son point de position p et son vecteur de direction v. Ensuite, nous définissons une articulation de pivot dans le corps statique b0 situé à la position p:
segment = Segment(p, v) PivotJoint(b0, segment.body, p)
Un peu à droite, nous créons un autre segment, deux fois plus long que le premier :
segment = Segment(p+3*v, 2*v) PivotJoint(b0, segment.body, p+3*v)
À ce segment plus long, nous attachons un segment plus court pour créer un double pendule :
segment2 = Segment(p+5*v, v) PivotJoint(segment.body, segment2.body, 2*v)
Cela décrit la création de segments et d'articulations de pivot pour former un double pendule.
Et voici le code de notre articülation
Exemple : Copier le code
# pivot point from articulation import * space.gravity = 0, 900 p = Vec2d(70, 190) v = Vec2d(60, 0) segment = Segment(p, v) PivotJoint(b0, segment.body, p) segment = Segment(p+3*v, 2*v) PivotJoint(b0, segment.body, p+3*v) segment2 = Segment(p+5*v, v) PivotJoint(segment.body, segment2.body, 2*v) App().run()
En Pymunk, un moteur est un composant qui permet d'appliquer une force continue à une articulation, créant ainsi un mouvement motorisé dans une simulation physique.
Pour créer un moteur en Pymunk, nous utilisons la classe SimpleMotor. Voici la syntaxe de base :
joint = SimpleMotor(body1, body2, rate)
body1 et body2 sont les corps (objets) entre lesquels vous souhaitez créer le moteur.
rate est la vitesse angulaire en radians par seconde à laquelle vous souhaitez faire tourner le moteur. Un nombre positif fera tourner les objets dans le sens des aiguilles d'une montre, tandis qu'un nombre négatif les fera tourner dans le sens contraire des aiguilles d'une montre.
Voici un exemple pratique qui utilise le module 'articulation'que nous avons créé précédemment pour créer des moteurs en Pymunk et affiche la simulation à l'aide de Pygame :
Mais avant, il faut ajouter une nouvelle classe 'PivotJoint' dans le, fichier 'articulation.py'. Cette classe est définie pour relier les deux corps b et b2 à leurs points d'ancrage a et a2 par l'intermédiaire d'une articulation pivot. puis il suffit d'ajouter la nouvelle articulation directement à l'espace pour économiser une ligne de code.
Voici le code de la nouvelle classe :
Exemple : Copier le code
class SimpleMotor: def __init__(self, b, b2, rate): joint = pymunk.constraint.SimpleMotor(b, b2, rate) space.add(joint)
Dans cet exemple, nous avons créé trois objets (deux segments 'arm et bras et un carré) avec des moteurs attachés à l'aide de la classe SimpleMotor. Les moteurs appliquent des vitesses angulaires spécifiques à chaque objet, créant ainsi un mouvement motorisé dans la simulation. La classe App est utilisée pour afficher la simulation en utilisant Pygame.
Exemple : Copier le code
from articulation import * Box() # Créez un bras avec un moteur p = 90, 100 v = 50, 10 arm = Segment(p, v) PivotJoint(b0, arm.body, p) SimpleMotor(b0, arm.body, 1) # Créez un carré avec un moteur p1 = 200, 120 carre = Rectangle(p1, (60, 60)) PivotJoint(b0, carre.body, p1) SimpleMotor(b0, carre.body, 5) # Créez un second bras avec un moteur p2 = 300, 120 bras = Segment(p2, (55, 30)) SimpleMotor(b0, bras.body, 10) # Utilisez la classe App du module pour afficher la simulation App().run()
Dans cet exemple, nous avons utilisé SimpleMotor pour appliquer des moteurs aux objets arm et carre. Ces moteurs feront tourner ces objets avec les vitesses angulaires spécifiées (1 radian/seconde pour arm, 5 radians/seconde pour carré et 10radian/seconde por le bras).
Lorsque vous exécutez ce code, vous verrez les objets arm et carre tourner en fonction des vitesses angulaires spécifiées, créant ainsi des mouvements motorisés dans la simulation.
Une articulation à glissière, ou SlideJoint en anglais, est une articulation qui permet à deux corps de glisser l'un contre l'autre le long d'une ligne de joint. La ligne de joint est définie par un point et une direction.
SlideJoint(body1, body2, anchor1, anchor2, min, max, limit_enabled=True)
En premier ajoutons la classe ‘SlideJoint’ au fichier ‘articulation.py’ :
Exemple : Copier le code
class SlideJoint: def __init__(self, b, b2, a=(0, 0), a2=(0, 0), min=50, max=100, collide=True): joint = pymunk.SlideJoint(b, b2, a, a2, min, max) joint.collide_bodies = collide space.add(joint)
Exemple : Copier le code
# Importation des modules nécessaires from articulation import * # Configuration de la gravité dans l'espace space.gravity = 0, 500 # Création d'une boîte Box() # Définition des paramètres : p = Vec2d(200, 100) # Position initiale v = Vec2d(80, 0) # Direction initiale r = 15 # Rayon de la balle # Définition des paramètres de contraintes pour le bras min, max = 30, 90 # Plage d'angle autorisée pour le bras # Création d'un segment (bras) avec le point d'ancrage # initial (p) et la direction (v) bras = Segment(p, v).body # Création d'une jointure pivot entre la boîte (b0) # et le bras au point d'ancrage initial (p) PivotJoint(b0, bras, p) #SimpleMotor(space.static_body, bras, 1) # Création d'une jointure coulissante (SlideJoint) # entre le bras et la balle # Le bras est attaché à la balle avec une plage # de mouvement limitée entre min et max # Le dernier argument (True) permet d'activer la jointure. balle = Circle(p + v + (40, 0), r).body SlideJoint(bras, balle, v, (-r, 0), min, max, True) # Démarrage de l'application de simulation App().run()
Pour avoir une articulation à glissière avec moteur, il suffit de décommenter la ligne :
#SimpleMotor(space.static_body, bras, 1)
Une articulation à rainure, ou Groove joint en anglais, est une articulation qui permet à deux corps de se déplacer les uns par rapport aux autres le long d'une ligne commune. Cette articulation est souvent utilisée pour simuler des portes, des fenêtres, ou des objets qui doivent pouvoir coulisser le long d'un rail.
Une articulation à rainure est définie par les points d'attache des deux corps, ainsi que par la direction de la rainure. La direction de la rainure est définie par un vecteur, qui indique la direction dans laquelle les deux corps peuvent se déplacer.
Pour créer une articulation à rainure, on utilise la fonction GrooveJoint(). La syntaxe de cette fonction est la suivante :
GrooveJoint(bodyA, bodyB, anchorA, anchorB, axis)
bodyA et bodyB sont les corps qui seront joints par l'articulation.
anchorA et anchorB sont les points d'attache des deux corps.
axis est le vecteur qui définit la direction de la rainure.
Pour cet exemple on doit ajouter la classe 'GrooveJoint dans notre module 'articulation.py'.
Voici le code de la classe :
class GrooveJoint: def __init__(self, a, b, groove_a, groove_b, anchor_b): joint = pymunk.GrooveJoint( a, b, groove_a, groove_b, anchor_b) joint.collide_bodies = False space.add(joint)
Et voici le code de l'exemple :
Exemple : Copier le code
from articulation import * Box() p0 = Vec2d(100, 100) v = Vec2d(60, 0) arm = Segment(p0, v) PivotJoint(b0, arm.body, p0) SimpleMotor(b0, arm.body, 1) ball = Circle(p0+v, 20) GrooveJoint(arm.body, ball.body, (0, 0), v, (0, 0)) p0 = Vec2d(300, 100) arm = Segment(p0, v) PivotJoint(b0, arm.body, p0) SimpleMotor(b0, arm.body, 2) ball = Circle(p0+v, 20) GrooveJoint(arm.body, ball.body, (0, 0), 2*v, (0, 0)) App().run()
Un joint à ressort rotatif amorti est un type de joint qui permet à deux corps de tourner l'un autour de l'autre. Le joint est amorti, ce qui signifie qu'il existe une force de résistance qui s'oppose à la rotation. Cette force de résistance est proportionnelle à la vitesse de rotation.
Pour créer un joint à ressort rotatif amorti, vous pouvez utiliser la classe pymunk.constraint.DampedRotarySpring(). Cette classe prend les arguments suivants :
Comme nous avons pris l'habitude, nous ajoutons une classe dont voici le code:
class DampedRotarySpring: def __init__(self, b, b2, angle, stiffness, damping): joint = pymunk.constraint.DampedRotarySpring( b, b2, angle, stiffness, damping) space.add(joint)Exemple Ressort rotatif amorti
Exemple : Copier le code
# Import des modules nécessaires from articulation import b0, App, Segment, DampedRotarySpring, PivotJoint, RotaryLimitJoint, SimpleMotor, Vec2d # Définition de la position initiale et de la direction du premier segment p0 = Vec2d(200, 110) v = Vec2d(50, 0) # Création du premier segment arm = Segment(p0, v) # Création d'un joint pivot entre le segment et le point d'ancrage b0 PivotJoint(b0, arm.body, p0) # Création d'un moteur simple pour le segment SimpleMotor(b0, arm.body, 1) # Création du deuxième segment connecté au premier segment avec un ressort rotatif amorti arm2 = Segment(p0+v, v) # Création d'un joint pivot entre le premier et le deuxième segment PivotJoint(arm.body, arm2.body, v, (0, 0)) # Création d'un ressort rotatif amorti entre les deux segments DampedRotarySpring(arm.body, arm2.body, 0, 10000000, 10000) # Lancement de l'application App().run()
Un joint à limite rotative est un type de joint qui limite la rotation entre deux corps. Il est défini par deux angles, un angle minimal et un angle maximal. La rotation des deux corps est limitée à un angle compris entre ces deux valeurs.
Pour créer un joint à limite rotative, on utilise la fonction pymunk.RotaryLimitJoint(). Cette fonction prend les paramètres suivants :
Pour simplifier, nous définissons une classe que nous ajoutons à notre module 'articulation.py'.
class RotaryLimitJoint: def __init__(self, b, b2, min, max, collide=True): joint = pymunk.RotaryLimitJoint(b, b2, min, max) joint.collide_bodies = collide space.add(joint)
Code de l'exemple :
Exemple : Copier le code
from articulation import b0, App, Segment, DampedRotarySpring, PivotJoint, RotaryLimitJoint, SimpleMotor, Vec2d # Joint rotatif limite p0 = Vec2d(200, 110) arm = Segment(p0, v) PivotJoint(b0, arm.body, p0) SimpleMotor(b0, arm.body, 1) arm2 = Segment(p0+v, v) PivotJoint(arm.body, arm2.body, v, (0, 0)) # Création d'une limite rotative entre les deux segments, limitant leur rotation entre -1 et 1 RotaryLimitJoint(arm.body, arm2.body, -1, 1) # Lancement de l'application App().run()
Un joint à engrenage est un type de joint qui permet à deux corps de tourner l'un autour de l'autre à une vitesse proportionnelle. La vitesse de rotation de chaque corps est liée par un rapport de vitesse, qui peut être positif ou négatif.
La syntaxe pour créer un joint à engrenage en Pymunk est représentée par la classe suivante que nous devons ajouter au module 'articulation.py':
class GearJoint: def __init__(self, b, b2, phase, ratio): joint = pymunk.GearJoint(b, b2, phase, ratio) space.add(joint)
Exemple : Copier le code
# gear joint from articulation import * p0 = Vec2d(100, 100) r1, r2 = 40, 80 v = Vec2d(r1+r2, 0) roue1 = Circle(p0, r1) roue2 = Circle(p0+v, r2) SimpleMotor(b0, roue1.body, 4) PivotJoint(b0, roue1.body, p0) PivotJoint(b0, roue2.body, p0+v) GearJoint(roue1.body, roue2.body, 0, -r2/r1) App().run()
Pour des roues tournant dans le même sens remplacer "-r2/r1" par "r2/r1"
Télécharger le fichier "articulation.py"