Bonjour à tous!
Voilà, je suis débutant en programmation et en Processing. Il y a quelque temps, j'ai décidé de réaliser un petit programme visuel inspiré du plug-in Plexus. Mon premier sketch m'a fait surchauffé les neurones, mais à force d'essais-erreurs, de patience et de détermination, je suis finalement parvenu à un premier résultat +/- satisfaisant. Tout fière de ma réussite, je le présente à une personne qui m'explique que j'aurais du utiliser les classes... (hein, c'est quoi?) J'ai donc regardé quelques tutoriels sur la POO et ai effectivement recréer mon programme de A à Z de manière beaucoup plus simple. Mais voilà, il s'agit de mon premier programme avec un objet et j'aurais aimé savoir si je l'ai correctement designé... En réalité, je préssens intuitivement que la réponse est négative car maintenant que je suis arrivé au premier stade souhaité, je sens que je suis bloqué pour le faire évoluer. J'aimerais, par exemple, faire en sorte que les balles rebondissent entre elles si elles se percutent. Hors, je pressens que pour ce faire, je vais retombé dans des complications qu'est sensé m'éviter une programmation orientée objet... Pourriez-vous me donner vos avis, conseils, suggestions, etc... ? Elles seront vraiment la bienvenues et je vous en remercie d'avance
Explication du programme:
- Une fenêtre blanche apparait.
- Chaque fois que l'on clique dedans avec la souris, une particule apparaît à l'endroit cliqué.
- Les particules partent dans une direction aléatoire et rebondissent contre les bords de la fenêtre.
- En fonction des distances inter-particules, un lien de connexion gris relie les particules entre elles. Plus elles sont proches, plus le lien est foncé; plus elles sont éloignées, plus le lien s'éclaircit.
Voici mon code de départ (celui qui m'a fait saigner des oreilles ):
int NbC; // nbre clicks int NbL1; // nbre elements ds liste1 int c1; // compteur1 int c2; // compteur2: 0=x, 1=y, 2=vit int[] liste1=new int[4]; // coordonnees de chaque point int[] liste2=new int[4]; // permet de sauvegarder les valeurs precedentes int[] coord=new int[4]; float distMax=200; float a=(-256)/distMax; // y = a * distMax + 256 void setup(){ size(800,600,P3D); smooth(); liste1[0]=0; // initialisation liste1[1]=0; // des liste1[2]=0; // listes liste1[3]=0; liste2=liste1; } void draw(){ background(256,256,256); particule(); } void particule(){ for(int i=0; i<NbC; i++){ for(int i2=0; i2<i; i2++){ plexus(i,i2); } stroke(0,0,0); strokeWeight(5); if(coord[4*i+1]+coord[4*i+2]>height||coord[4*i+1]+coord[4*i+2]<0){ coord[4*i+2] = -coord[4*i+2]; } if(coord[4*i]+coord[4*i+3]>width||coord[4*i]+coord[4*i+3]<0){ coord[4*i+3] = -coord[4*i+3]; } coord[4*i+1] = coord[4*i+1] + coord[4*i+2]; // y = y + vity coord[4*i] = coord[4*i] + coord[4*i+3]; // x = x + vitx point( coord[4*i] , coord[4*i+1] ); } } void plexus(int i,int i2){ float distance=sqrt(pow(abs((coord[4*i2])-(coord[4*i])),2)+pow(abs((coord[4*i2+1])-(coord[4*i+1])),2)); float gris=(a*distance)+256; if(gris<0){ gris=0; } gris=256-gris; stroke(gris,gris,gris); strokeWeight(1); line( (coord[4*i]) , (coord[4*i+1]) , (coord[4*i2]), (coord[4*i2+1])); } void mouseClicked(){ NbC=NbC+1; ajouter(mouseX,mouseY); } void ajouter(int x, int y){ while(NbL1<NbC){ liste1=new int[4*(NbL1+1)]; c1=0; while(c1<liste1.length){ if(liste2.length>c1){ liste1[c1]=liste2[c1]; } else{ if(c2==0){ liste1[c1]=x; } if(c2==1){ liste1[c1]=y; } if(c2==2){ liste1[c1]=int(random(-5,5)); } if(c2==3){ liste1[c1]=int(random(-5,5)); } c2=c2+1; if(c2==4){ c2=0; } } //print(liste1[c1]+" "); c1=c1+1; } liste2=liste1; NbL1=NbL1+1; } //print(" "); coord=liste1; }
Voici celui écrit en orienté objet:
ArrayList Balls, Pos; float distMax=256; float a=(-256)/distMax; void setup(){ size(800,600,P3D); ellipseMode(CENTER); Balls = new ArrayList(); Pos = new ArrayList(); } void draw(){ PVector v1, v2; v1 = new PVector(); v2 = new PVector(); background(255); for(int i=0; i<Balls.size(); i++){ Ball balle = (Ball) Balls.get(i); balle.update(); Pos.add( balle.getPos() ); } for(int j=0; j<Pos.size(); j++){ for(int k=j; k<Pos.size(); k++){ v1=(PVector)Pos.get(j); v2=(PVector)Pos.get(k); float distance = v1.dist(v2); float gris=(a*distance)+256; if(gris<0){ gris=0; } gris=256-gris; stroke(gris); line( v1.x, v1.y, v2.x, v2.y ); } } Pos.clear(); } void mouseClicked(){ PVector position, vitesse; position = new PVector( mouseX, mouseY ); vitesse = new PVector( random(-5,5), random(-5,5) ); Balls.add( new Ball( position, vitesse ) ); } class Ball{ PVector position, vitesse; int rayon; Ball( PVector pos, PVector vit ){ position = new PVector(); vitesse = new PVector(); position = pos.get(); vitesse = vit.get(); rayon = 4; } void update(){ position.add(vitesse); if( position.x > width-(rayon/2) || position.x < rayon/2 ){ vitesse.x = -vitesse.x; } if( position.y > height-(rayon/2) || position.y < rayon/2 ){ vitesse.y = -vitesse.y; } fill(0); ellipse( position.x, position.y, rayon, rayon ); } PVector getPos(){ return position; } }
Voici un dernier équivalent au précédent mais avec une fonctionnalité en plus (curseur). Nécessite la librairie "controlP5" pour fonctionner.
A noter également que j'ai tenté ici d'améliorer mon code en créant une fonction plexus(). Mais peut-être s'agit-il d'une fausse bonne idée???
import controlP5.*; ControlP5 cp5; ArrayList Balls, Pos; float distMax=256; void setup(){ size(800,600,P3D); ellipseMode(CENTER); Balls = new ArrayList(); Pos = new ArrayList(); cp5 = new ControlP5(this); cp5.addSlider("distMax") .setPosition(width-205,height-25) .setSize(200,20) .setRange(128,2048) .setValue(256) ; } void draw(){ background(255); for(int i=0; i<Balls.size(); i++){ Ball balle = (Ball) Balls.get(i); balle.update(); Pos.add( balle.getPos() ); } plexus(); Pos.clear(); } void plexus(){ float a=(-256)/distMax; PVector v1, v2; v1 = new PVector(); v2 = new PVector(); for(int j=0; j<Pos.size(); j++){ for(int k=j; k<Pos.size(); k++){ v1=(PVector)Pos.get(j); v2=(PVector)Pos.get(k); float distance = v1.dist(v2); float gris=(a*distance)+256; if(gris<0){ gris=0; } gris=256-gris; stroke(gris); line( v1.x, v1.y, v2.x, v2.y ); } } } void mouseClicked(){ PVector position, vitesse; position = new PVector( mouseX, mouseY ); vitesse = new PVector( random(-5,5), random(-5,5) ); Balls.add( new Ball( position, vitesse ) ); } class Ball{ PVector position, vitesse; int rayon; Ball( PVector pos, PVector vit ){ position = new PVector(); vitesse = new PVector(); position = pos.get(); vitesse = vit.get(); rayon = 4; } void update(){ position.add(vitesse); if( position.x > width-(rayon/2) || position.x < rayon/2 ){ vitesse.x = -vitesse.x; } if( position.y > height-(rayon/2) || position.y < rayon/2 ){ vitesse.y = -vitesse.y; } fill(0); ellipse( position.x, position.y, rayon, rayon ); } PVector getPos(){ return position; } }
AQ
Hors ligne
Bonsoir,
La construction de la classe est bien faite, c'est dans la gestion de ton tableau que tu aurais pu être plus simple. Les ArrayList ne sont pas typées. Mais on peut indiquer le type entre signe < >. Cela évite des transtypages fastidieux. Par convention, on n'utilise pas de majuscule dans les noms des variables.
ArrayList<Ball> balles; void setup() { size(800, 600, P3D); ellipseMode(CENTER); balles = new ArrayList<Ball>(); } void draw() { background(255); for (Ball balle : balles) { balle.update(); } for (int j = 0; j < balles.size(); j++) { for (int k = j; k < balles.size(); k++) { float distance = PVector.dist(balles.get(j).getPos(), balles.get(k).getPos()); if (distance < 255) { stroke(distance); line(balles.get(j).position.x, balles.get(j).position.y, balles.get(k).position.x, balles.get(k).position.y ); } } } } void mouseClicked() { PVector position, vitesse; position = new PVector( mouseX, mouseY ); vitesse = new PVector( random(-5, 5), random(-5, 5) ); balles.add( new Ball( position, vitesse ) ); } class Ball { PVector position, vitesse; int rayon; Ball( PVector pos, PVector vit ) { position = pos.get(); vitesse = vit.get(); rayon = 4; } void update() { position.add(vitesse); if ( position.x > width-(rayon/2) || position.x < rayon/2 ) { vitesse.x = -vitesse.x; } if ( position.y > height-(rayon/2) || position.y < rayon/2 ) { vitesse.y = -vitesse.y; } fill(0); ellipse( position.x, position.y, rayon, rayon ); } PVector getPos() { return position; } }
Hors ligne
Merci pour ta réponse aussi rapide!
Oulala typage et transtypage, c'est encore un peu chinois pour moi tout ça, mais je vais creuser ça! Je pense néanmoins avoir +/- compris ce que tu as fait (même si je doute pouvoir le faire par moi-même...)
Quelques petites questions:
A la ligne 15, aurais-tu pu écrire
for (Ball j : balles) { for (Ball k : balles) { float distance = PVector.dist(j.getPos(), k.getPos()); if (distance < 255) { stroke(distance); line(j.position.x, j.position.y, k.position.x, k.position.y ); } } }
??? ( => oui, je viens d'essayer lol )
Mais penses-tu vraiment que je puisse continuer à développer le programme sous cette forme? Comment puis-je faire par exemple si je souhaite se faire entrechoquer les balles si elles se touchent?
Je viens d'essayer quelque chose, mais:
1) Dans les tutoriels sur la poo que j'ai vu, il était déconseiller de changer des valeurs d'une classe directement comme je le fais; il vaut mieux passer par un setter.
2) Je ne comprends pas le résultat: comment se fait-il que les DEUX boules qui se touchent réagissent, alors que j'ai grisé "k" dans mon code??? (si je dégrise "k", les boules ne réagissent pas lorsqu'elles se touchent...)
(J'ai grossi volontairement les boules afin qu'elles aient plus de chance de se croiser)
ArrayList<Ball> balles; void setup() { size(800, 600, P3D); ellipseMode(CENTER); balles = new ArrayList<Ball>(); } void draw() { background(255); for (Ball balle : balles) { balle.update(); } for (Ball j : balles) { for (Ball k : balles) { float distance = PVector.dist(j.getPos(), k.getPos()); if (distance < 255) { stroke(distance); line(j.position.x, j.position.y, k.position.x, k.position.y ); } if (distance <= 40 && distance != 0){ j.vitesse.x= -j.vitesse.x; j.vitesse.y= -j.vitesse.y; //k.vitesse.x= -k.vitesse.x; //k.vitesse.y= -k.vitesse.y; } } } } void mouseClicked() { PVector position, vitesse; position = new PVector( mouseX, mouseY ); vitesse = new PVector( random(-5, 5), random(-5, 5) ); balles.add( new Ball( position, vitesse ) ); } class Ball { PVector position, vitesse; int rayon; Ball( PVector pos, PVector vit ) { position = pos.get(); vitesse = vit.get(); rayon = 40; } void update() { position.add(vitesse); if ( position.x > width-(rayon/2) || position.x < rayon/2 ) { vitesse.x = -vitesse.x; } if ( position.y > height-(rayon/2) || position.y < rayon/2 ) { vitesse.y = -vitesse.y; } fill(0); ellipse( position.x, position.y, rayon, rayon ); } PVector getPos() { return position; } }
En tout cas, je suis impressionné par la l'amélioration que tu effectué sur mon code: tu as su le lire et le ré-écrire si rapidement! C'est fou, j'espère parvenir à ce que ça soit aussi simple pour moi un jour héhé!
Un tout grand merci, j'ai appris de nouvelles techniques de travail grâce à toi!
AQ
Hors ligne
Non, ce n'est pas strictement équivalent. Dans ton code il y a des doublons. Tu traces une ligne entre la boule 2 et la boule 5 puis tu traces une ligne entre le boue 5 et la boule 2.
D'où l'utilisation des :
for (int j = 0; j < balles.size(); j++) { for (int k = j; k < balles.size(); k++) {
Cela évite les doublons.
Ce qui répond à ta question suivante, les doublons sont déjà calculés. Voici ton code affiné :
ArrayList<Ball> balles; void setup() { size(800, 600, P3D); ellipseMode(CENTER); balles = new ArrayList<Ball>(); } void draw() { background(255); for (Ball balle : balles) { balle.update(); } for (int j = 0; j < balles.size (); j++) { for (int k = j; k < balles.size (); k++) { Ball b1 = balles.get(j); Ball b2 = balles.get(k); float distance = PVector.dist(b1.getPos(), b2.getPos()); if (distance < 255) { stroke(distance); line(b1.position.x, b1.position.y, b2.position.x, b2.position.y ); } if (distance <= (b1.rayon + b2.rayon)) { b1.vitesse.x *= -1; b1.vitesse.y *= -1; b2.vitesse.x *= -1; b2.vitesse.y *= -1; } } } } void mouseClicked() { PVector position, vitesse; position = new PVector( mouseX, mouseY ); vitesse = new PVector( random(-5, 5), random(-5, 5) ); balles.add( new Ball( position, vitesse ) ); } class Ball { PVector position, vitesse; int rayon; Ball( PVector pos, PVector vit ) { position = pos.get(); vitesse = vit.get(); rayon = 20; } void update() { position.add(vitesse); if ( position.x > width-(rayon/2) || position.x < rayon/2 ) { vitesse.x *= -1; } if ( position.y > height-(rayon/2) || position.y < rayon/2 ) { vitesse.y *= -1; } fill(0); ellipse( position.x, position.y, 2 * rayon, 2 * rayon ); } PVector getPos() { return position; } }
Il reste à modifier le calculs des collisions qui devrait être des chocs élastiques :
http://fr.wikipedia.org/wiki/Choc_élastique
Concernant ta remarque sur la POO, oui, il est préférable de faire pour chaque variable susceptibles d'être modifiée des accesseurs et des mutateurs. Je ne respecte pas à chaque fois cette règle, mais comme tu débute il est préférable d'avoir de bonnes pratiques.
Hors ligne
Salut!
Alors je viens de me casser la tête pendant deux jours à essayer de comprendre le principe (et surtout le fonctionnement!) de ces chocs élastiques... Comme cela fait + de 10 ans que je n'ai plus eu de cours de math ou de physique, je dois avouer que je patauge un peu... J'adore les maths, mais je ne maitrise vraiment plus tous les concepts nécessaires ici...
J'ai recherché également des algorithmes existant afin de m'en inspiré:
https://processing.org/examples/bouncybubbles.html => je n'aime pas tro celui-ci car je ne comprends pas d'où viens cette valeur spring de 0.05 ? Je n'ai pas l'impression que le code corresponde vraiment aux lois mathématiques vue par exemple sur le wiki que tu m'as indiqué...
http://www.false.jp/motion/ch11/Billiard4/ => celui-ci en revanche correspond exactement à ce que je recherche, mais je ne pige RIEN au code! Le coup de cette classe objet par exemple:
class Object { Object() {} }
Hors je n'aime pas implanter dans mon code une partie de code extérieure que je ne maîtrise pas...
Du coup, j'ai cherché dans ma petite tête, avec un crayon et un bloc de feuille, un moyen d'y parvenir... Je me suis remis à la trigono et aux vecteurs comme je pouvais et voici ce que j'ai pondu (c'est un nouveau petit programme test). Je pense me rapprocher d'une solution, mais il y a encore quelques réactions des balles non réalistes...
Je désespère un peu, et viens (de nouveau) demander de l'aide... Je deviens obsessionnel (je rêve en vecteur la nuit! lol ) J'ai du taf à côté qui m'attend et j'aurais un peu moins de temps ces jours-ci à y consacrer, mais j'ai vraiment pas envie de lâcher... Le pire, c'est que ça m'a l'air assez basique comme programme j'imagine, de provoquer des collisions, et pourtant je galèèèèèèèère!
Bon j'arrête là, voici mon code, si quelqu'un veut bien venir à mon aide, please welcomez.....
Hors ligne
Voici, à quoi, j'étais arrivé de mon côté lorsque j'avais étudié les collisions :
ArrayList<Balle> balles; void setup() { size (600, 600); balles = new ArrayList<Balle>(); for (int i = 0; i < 10; i++) { balles.add(new Balle(i)); } noStroke(); } void draw() { background(255); for (Balle b : balles) { b.mettreAJour(); b.collision(balles); b.afficher(); } } class Balle { PVector position, vitesse; int id, vitesseMax, rayon; color couleur; float masse; boolean bing; Balle(int _id) { vitesseMax = 5; rayon = 20; position = new PVector(random(rayon, width - rayon), random(rayon, height - rayon)); vitesse = PVector.random2D(); vitesse.mult(random(vitesseMax / 3, vitesseMax)); couleur = color(0, 255, 0); masse = random(2, 10); id = _id; bing = false; } void mettreAJour() { //vitesse.limit(vitesseMax); position.add(vitesse); } void collision(ArrayList<Balle> autres) { // Balles for (int i = id + 1; i < autres.size (); i++) { Balle b = autres.get(i); if (position.dist(b.position) <= (b.rayon + rayon)) { PVector d = PVector.sub(b.position, position); d.setMag(rayon + b.rayon); d.add(position); d.sub(b.position); vitesse.sub(d); b.vitesse.add(d); bing = true; b.bing = true; } } // Murs if ((position.x < rayon) || (position.x > (width - rayon))) { vitesse.x *= - 1; } if ((position.y < rayon) || (position.y > (height - rayon))) { vitesse.y *= -1; } } void afficher() { if (bing) { fill(255, 0, 0); } else { fill(couleur); } ellipse(position.x, position.y, 2 * rayon, 2 * rayon); bing = false; } }
Hors ligne
Bonjour,
Juste pour vous faire partager les solutions que j'ai trouvées, au cas où quelqu'un, un jour, chercherait un moyen de faire percuter plusieurs balles et tomberait sur ce forum... Tout d'abord, voici un lien vers une vidéo où l'on explique comment faire. C'est en anglais, et ça ne fonctionne qu'avec des objets de même masse. En effet, la masse n'est pas impliquée dans le calcul. Mais cela pourrait suffire, je pense, à la conception d'un billard par exemple.
http://channel9.msdn.com/Series/Sketchb … d-Response
Pour celles et ceux qui désirent pousser plus loin le concept, voici un code que j'ai trouvé après recherches sur le net:
PVector posb1 = new PVector( random(50,(width-50)), random(50,(height-50)) ); // position initiale balle b1 PVector velb1 = new PVector( random(-5, 5), random(-5, 5) ); // velocite balle b1 float mb1 = 10; // masse balle b1 PVector posb2 = new PVector( (width-50), random(50,(height-50)) ); // position initiale balle b2 PVector velb2 = new PVector( random(-5, 5), random(-5, 5) ); // velocite balle b2 float mb2 = 1; Ball b1 = new Ball( posb1, velb1, mb1 ); Ball b2 = new Ball( posb2, velb2, mb2 ); Ball[] balles = { b1, b2 }; void setup() { ellipseMode(CENTER); size(200,200,P3D); } void draw() { background(255); for( int i = 0; i < balles.length; i++) { balles[i].checkWall(); if(i < balles.length-1){ balles[i].checkCollision(balles[i+1]); } balles[i].update(); balles[i].display(); } } class Ball { PVector position, velocite; float masse, diametre; Ball( PVector pos, PVector vel, float m ) { position = pos.get(); velocite = vel.get(); masse = m/100.; diametre = sqrt(masse)*40; } void update() { position.add(velocite); } void display() { fill(0); ellipse( position.x, position.y, diametre, diametre ); } void checkWall() { if( position.x >= width - (diametre/2) ) { position.x -= ( position.x + (diametre/2) ) - width; velocite.x = - velocite.x; } if( position.x <= diametre/2 ) { position.x = diametre/2 ; velocite.x = - velocite.x; } if( position.y >= height - (diametre/2) ) { position.y -= (position.y + (diametre/2)) - height; velocite.y = - velocite.y; } if( position.y <= diametre/2 ) { position.y = diametre/2 ; velocite.y = - velocite.y; } } void checkCollision(Ball other) { PVector bVect = PVector.sub(other.position, position); float bVectMag = bVect.mag(); if ( bVectMag < (diametre/2) + (other.diametre/2) ) { float theta = bVect.heading(); float sine = sin(theta); float cosine = cos(theta); PVector[] bTemp = { new PVector(), new PVector() }; bTemp[1].x = cosine * bVect.x + sine * bVect.y; bTemp[1].y = cosine * bVect.y - sine * bVect.x; PVector[] vTemp = { new PVector(), new PVector() }; vTemp[0].x = cosine * velocite.x + sine * velocite.y; vTemp[0].y = cosine * velocite.y - sine * velocite.x; vTemp[1].x = cosine * other.velocite.x + sine * other.velocite.y; vTemp[1].y = cosine * other.velocite.y - sine * other.velocite.x; PVector[] vFinal = { new PVector(), new PVector() }; vFinal[0].x = ((masse - other.masse) * vTemp[0].x + 2 * other.masse * vTemp[1].x) / (masse + other.masse); vFinal[0].y = vTemp[0].y; vFinal[1].x = ((other.masse - masse) * vTemp[1].x + 2 * masse * vTemp[0].x) / (masse + other.masse); vFinal[1].y = vTemp[1].y; bTemp[0].x += vFinal[0].x; bTemp[1].x += vFinal[1].x; PVector[] bFinal = { new PVector(), new PVector() }; bFinal[0].x = cosine * bTemp[0].x - sine * bTemp[0].y; bFinal[0].y = cosine * bTemp[0].y + sine * bTemp[0].x; bFinal[1].x = cosine * bTemp[1].x - sine * bTemp[1].y; bFinal[1].y = cosine * bTemp[1].y + sine * bTemp[1].x; other.position.x = position.x + bFinal[1].x; other.position.y = position.y + bFinal[1].y; position.add(bFinal[0]); velocite.x = cosine * vFinal[0].x - sine * vFinal[0].y; velocite.y = cosine * vFinal[0].y + sine * vFinal[0].x; other.velocite.x = cosine * vFinal[1].x - sine * vFinal[1].y; other.velocite.y = cosine * vFinal[1].y + sine * vFinal[1].x; } } }
Cela fonctionne plutôt bien, mais je pense que ce code à ses limites. En effet, on pourrait imaginer que la vélocité d'une balle soit si élevée qu'elle pourrait "traverser" une autre balle se trouvant sur sa trajectoire. Je ne me suis pas posé le problème et je ne vois pas pas trop comment le résoudre, mais je pense que ce code pourrait néanmoins servir de base...
A+
Hors ligne
Sinon voici l'évolution de mes expérimentations... Après être parvenu à percuter les balles entres elles, j'ai voulu créer un cadre différent de celui de la fenêtre où évolueraient ces balles... J'ai donc décider de créer des formes, ici des polygones réguliers, que je peux également faire évoluer... Cette forme s'affiche au lancement du programme et lorsque je crée, en cliquant, des balles à l'intérieur de ma forme, ces balles vont rebondir sur les côtés de cette forme... J'ai fait beaucoup de tentatives différentes pour aborder le problème, mais j'ai toujours quelques "bugs", il arrive que des balles sortent de mon cadre pour une ou des raisons que j'ignore...
Voici le résultat le plus abouti auquel je suis parvenu... Si quelqu'un a des solutions, des remarques ou des conseils, il est toujours le bienvenu!
PS: est-ce aussi courrant dans processing de manipuler et jongler autant avec la trigono!?
A+
Hors ligne