La fonction première du mouse Joint est de diriger la position d’un objet avec la souris. Le problème en définissant la position d’un objet arbitrairement serait de casser les lois de la physique dans la simulation. Box2D cependant permet de créer un String, une sorte de corde qui permet de traîner un objet dans un environnement. Dans la connexion avec le String, l’un des deux body est le “ground”. C’est-à-dire qu’on crée une connexion avec la fenêtre. La cible de cette interaction sera continuellement changée pour matcher avec la position de la souris. Ou d’une autre cible, l’exercice dans NOC étant de matcher la position de la cible avec du bruit Perlin.
- Mouse Joint
https://github.com/Thibautomaton/MouseJointWithParticles/tree/main
ground_body = world.CreateStaticBody(
position=(0, 0)
)
spring = Spring(ground_body, world)
spring.bind(scalarPixelToWorld(WIDTH/2), scalarPixelToWorld(HEIGHT/2),boxes[0])
Dans main on initialise Spring tout en passant ground_body en argument et world. Ensuite on bind l’objet Box qu’on a créé et sauvegardé dans la liste boxes avec le milieu de l’écran. Ceci permet de commencer au centre et non pas à la première localisation que pygame.mouse.get_pos() aura.
Ensuite, main est un peu plus étoffé que dans les autres programmes car on va traquer les commandes de l’utilisateur.
La première façon est d’attendre une action qui va apparaître dans event. C’est ce qui va se produire qu’une seule fois, lorsque l’utilisateur click avec la souris une première fois c’est MOUSEBUTTONDOWN qui est appelé, puis quand le click est finis c’est MOUSEBUTTONUP. De même on a KEYDOWN lorsqu’une touche est enclenché. Ici on traque l’enclenchement de la touche “p” qui donnera comme ordre au cube de s’orienter selon les oscillations d’un bruit Perlin.
On utilise deux fonctions: bind et update. Bind crée un nouveau mouseJoint et ne devrait donc être appelé qu’ une fois, lorsque l’on clique notamment. Update définit simplement la cible du mouseJoint existant.
On voit que lorsque MOUSEBUTTONUP est appelé, on détruit le mouseJoint. Il n’y a plus de connexion entre l’objet et la souris.
Avec cette ligne on récupère les touches qui sont pressées en ce moment :
keys = pygame.key.get_pressed()
precedemment,
if event.type==pygame.KEYDOWN:
if event.key==pygame.K_p:
noise_x = random.uniform(0, 10)
ce code n’est appelé qu’une fois lorsque la touche est enclenchée, tandis que pygame.key.get_pressed() prend en compte les touches qui sont actuellement pressées. Ce qui nous donne le code suivant qui prend en paramètre des coordonnées calculées avec un bruit Perlin :
if keys[pygame.K_p]:
y_pix = noise.get(noise_x) + HEIGHT / 2
noise_x += 2
x_pix = WIDTH / 2 # or whatever x you want
x_world, y_world = pixelsToWorld((x_pix, y_pix))
if not bind:
spring.bind(x_world, y_world, boxes[0])
bind = True
else:
spring.update(x_world, y_world)
C’est le code suivant qui permet de suivre les coordonnées x et y de la souris, lorsqu’un mouseJoint existe :
if spring.mouse_joint and not keys[pygame.K_p]:
mx, my = pygame.mouse.get_pos()
mx, my = pixelsToWorld((mx, my))
spring.update(mx, my)
Afin de comprendre comment sont implémenté bind et update, allons à la classe Spring :
class Spring():
def __init__(self, ground_body, world):
self.world =world
self.ground_body = ground_body
def bind(self, x, y, box):
mj_def = b2MouseJointDef()
mj_def.bodyA = self.ground_body
mj_def.bodyB = box.body
mj_def.maxForce = 1000 * box.body.mass
mj_def.frequencyHz = 5
mj_def.dampingRatio = 0.9
mj_def.target = (x,y)
self.mouse_joint = self.world.CreateJoint(mj_def)
box.body.awake = True
def update(self, mouseX, mouseY):
if self.mouse_joint is not None:
self.mouse_joint.target = (mouseX, mouseY)
Le code de update est beaucoup plus restreint que celui de bind, on change simplement la target. Dans bind on définit un mouseJoint en partant d’un objet MouseJointDef() auquel on passe tous les paramètres. Ensuite on crée le joint avec CreateJoint() et on réveille l’objet attaché.
Box a la même initialisation que dans le code sur l’objet bordure. On pourrait varier les settings de densité et de restitution pour voir un effet.
- Kinematic
https://github.com/Thibautomaton/KinematicBox
La définition d’un objet kinematic pour suivre la position de la souris ressemble d’avantage à ce qu’on avait l’habitude de faire lorsqu’on implementait des systèmes de particules avec leur propre vélocité et accélération.
Ici dans main on crée un objet Kinematic qui se situe au centre de l’ecran avec ses dimensions :
K = Kinematic(WIDTH/2, HEIGHT/2, 50, 50, world)
Dans la boucle while on appelle simplement la fonction update_target:
x, y = pygame.mouse.get_pos()
K.update_target(x, y)
K.display()
Etant donné que K est un objet Box il n’y a pas beaucoup de doute de ce à quoi ressemble la fonction display. Voyons ce qui se cache derrière update_target.
Tout d’abord l’initialisation de K se fait en appelant la fonction CreateKinematicBody()
def __init__(self, x, y, w, h, world):
self.x = x
self.y = y
self.w = w
self.h = h
self.display_surface = pygame.display.get_surface()
self.body = world.CreateKinematicBody(
position = pixelsToWorld((self.x, self.y))
)
box2DW = scalarPixelsToWorld(self.w/2)
box2DH = scalarPixelsToWorld(self.h/2)
ps = polygonShape(
box = (box2DW, box2DH)
)
self.body.CreateFixture(
shape= ps,
friction = 0.3,
density = 0.5,
restitution = 0.5
)
Dans update_target on peut changer directement la vélocité de K :
def update_target(self, xt, yt):
pos = self.body.worldCenter
target = vec2(pixelsToWorld((xt, yt)))
v = target-pos
self.body.linearVelocity = v
self.body.angularVelocity += 0.1
if self.body.angularVelocity>10:
self.body.angularVelocity=0
On crée un vecteur qui pointe dans la direction de target en effectuant une soustraction de vecteurs. On donne ensuite ce vecteur à linearVelocity. On aurait pu prendre la peine de normaliser ce vecteur mais le laisser tel quel permet d’avoir une vitesse plus grande plus l’objet est loin. On influe aussi sur angularVelocity pour avoir un effet plus naturel de l’attraction, mais ce paramètre aurait pu être laissé tel quel.
CONCLUSION
Cela nous a permis de nous éloigner un petit peu de la fonctionnalité de base de processing pour nous intéresser un petit peu au jeu vidéo et à l’interaction utilisateur.
Ensuite nous allons revenir vers des notions fondamentales avant de s’intéresser de plus près aux jeux vidéo et de voir ce qu’il est possible de faire avec les notions vues dans processing dans un moteur comme pygame.
