Billard en javascript c'est possible !

Description

Hello tout le monde,
je reviens en force avec un petit jeu que je suis en train de terminer, un jeu de billard en full javascript !
-le programme est basé sur mon moteur de collision encore quelques tout petits bugs sur les chocs (cas extrêmes qui n'arrive pas souvent)
-la queue de billard est dessinée à partir de l'algorithme de Bresenham (tracage de segement) + une modif perso pour l'épaisseur de la droite

vous pouvez déjà commencer à voir un aperçu du futur jeu, même si il n'est pas encore finalisé!
Même si il reste une grosse partie à faire, j'ai programmé la partie la plus difficile !

20/04/09 : ajout d'une méthode ultra rapide qui détecte les croisements de segements, cela me permet de détecter les rebords + les trous, les rebonds se font désormés en fonction de l'angle du rebord ... :)
dans le jeux cela correspond à l'encadré en rouge (pour enlever l'affichage tu rectangle rouge, remplacer dans le source "var d=true;" par "var d=false;"

Source / Exemple :


var balles = new Array();
var tmp = null;
var d=false ; //debug = true
var minx=10,miny=30,maxx=440,maxy=230; //position absolu de la fenetre de jeux
var rad2deg=180/Math.PI; //conversion du radian au degres
var deg2rad=Math.PI/180; //conversion du degres au radian
var coefLin=0.005 ; //coeficient de ralentissement (décroissance liénaire)
var seuilStop=0.1; // stop le deplacement si vitesse<seuil
var	a_v1 = 0, v_v1 = 5;
var pause = true;
var col=/*j*/1,/**/2,1,/**/1,3,2,/**/2,1,2,1,/**/1,2,1,2,2;	
var src=['blanche','jaune','rouge','noir']
var fichier
//distance & force maxi de la queue
var dmin = 10, dmax = 70, fmax = 30;

//creation de la bille
function Balle(id,x,y,a,v,r,t)
{
	this.id = id; //identifiant
	this.x =x; //position x
	this.nx =x; // prochaine position -> pour les chocs
	this.y =y;
	this.ny =y;
//TODO: remplacer angle,vitesse par dx & dy (+rapide pr les calculs)
	this.a = a; //angle
	this.v = v; //vitesse
	this.m = 4; //masse
	this.r = r; //rayon
	this.t = t; //couleur/type de la balle
	this.bouge =true; //patch pour eviter les chevauchements
}

/*creation de la queue
angle : angle entre queue et boule 
deltaF : distance de la boule en fonction de la force
posX,posY : position de la boule

  • /
function dessinQueue(angle,deltaForce,posX,posY) { Lq = 200; // longueur de la queue pcos=Math.cos(angle); psin=Math.sin(angle); xmin= posX+(dmin+deltaForce)*pcos; ymin= posY+(dmin+deltaForce)*psin; xmoy= posX+(dmin+deltaForce+10)*pcos; ymoy= posY+(dmin+deltaForce+10)*psin; xmax= posX+(dmin+deltaForce+Lq)*pcos; ymax= posY+(dmin+deltaForce+Lq)*psin; htm=ligne2(xmin,ymin,xmoy,ymoy,6,'#c0c0c0');//embout htm+=ligne2(xmoy,ymoy,xmax,ymax,6,'brown'); return htm; } /* convertie position souris en angle + distance => force
  • /
function bougerQ(px,py) { //coordonnée de la balle blanche var posX = balles[0].x,posY = balles[0].y; py-=miny;px-=minx; a_v1= Math.atan2(py-posY,px-posX); deltaForce= Math.sqrt((py-posY)*(py-posY)+(px-posX)*(px-posX)); deltaForce=((deltaForce+dmin)>dmax) ? dmax : deltaForce; deltaForce=(deltaForce<dmin) ? dmin : deltaForce; v_v1 = deltaForce/10; balles[0].a = a_v1 * rad2deg + 180; balles[0].v = v_v1; _("ecran2").innerHTML=dessinQueue(a_v1,deltaForce,posX,posY); } /* capture deplacement de la souris (onmousemove)
  • /
function bougerQueue(event) { var ev = event || window.event; //getProp(ev); if (pause) { tmp=setTimeout("bougerQ("+(ev.pageX||ev.clientX)+","+(ev.pageY||ev.clientY)+")",10); } } /* capture clic de la souris (onclick)
  • /
function tirer() { pause=false; _("ecran2").innerHTML=""; boucle(); } //pr simplifier les appel des objets function _(idx) {return document.getElementById(idx);} function _v(idx) {return parseFloat(document.getElementById(idx).value,10);} //initialisation/reset function depart() { //Retourne l'air du plateau + onresize getPosition(); //efface tempo if (tmp!=null) {clearTimeout(tmp);} //lise des boules balles = new Array(); //coefficient de freinage //decroissance linéaire coefLin = 0.005; //ajout de la boule blanche balles.push(new Balle("b1",120,120,a_v1,v_v1,8.5,0)); blanche=balles[0]; //d=false; //ajout des boules + disposition en triangle n=0; for (x=0;x<6;x++) for (y=0;y<x;y++) { balles.push(new Balle("r"+n,270+x*20,150-y*20-(5-x)*10,0,0,9,col[n])); n++; } //affiche les boules a l'ecran ajoutBalle(); //associe les evenements document.onmousemove=bougerQueue; } function getPosition() { //position de l'air du plateau, ok sous IE & firefox minx=parseInt(_("ecran").offsetLeft,10); miny=parseInt(_("ecran").offsetTop,10); maxx=parseInt(_("ecran").offsetWidth,10); maxy=parseInt(_("ecran").offsetHeight,10); //ajuste le div de la queue _("ecran2").style.left=minx; _("ecran2").style.top=miny; _("ecran2").style.width=maxx; _("ecran2").style.height=maxy; } //retourne les proprietes d'un objet (debug) function getProp(x) { htm=""; for (e in x) htm+=e+":"+x[e]+"<br>"; _("debug_").innerHTML=htm; } //boucle de deplacement function boucle() { //deplace les boules bougeMoi(); //test les chocs contre les murs et entre les boules testChoc(); //replace les boules placeMoi(); //relance la fonction apres 10ms //a ajuster en fonction de la vitesse de la machine if (!pause) { tmp = setTimeout("boucle()",10); } } function testChoc() { for (e=0;e<balles.length;e++) { b=balles[e]; //collision avec les murs if (b.nx<b.r) {b.nx=b.r; b.a = 180-b.a;} if ((b.nx+b.r)>maxx) {b.nx=maxx-b.r; b.a = 180-b.a;} if (b.ny<b.r) {b.ny=b.r; b.a = -b.a;} if ((b.ny+b.r)>maxy) {b.ny=maxy-b.r; b.a = -b.a;} } for (e=0;e<balles.length;e++) { b=balles[e]; for (k=e+1;k<balles.length;k++) { bk=balles[k]; //distance entre 2 balles < sommes des 2 rayons //version light pr la vitesse d'execution distn=(b.nx-bk.nx)*(b.nx-bk.nx)+(b.ny-bk.ny)*(b.ny-bk.ny); if ((distn<=((b.r+bk.r)*(b.r+bk.r)))&&(b.v>0||bk.v>0)) { collision(b,bk); b.bouge=false; } } } //attribue les nouvelles positions à la boule for (e=0;e<balles.length;e++) { b=balles[e]; if(b.bouge) { b.x = b.nx; b.y = b.ny; } b.bouge=true; } } //test de collision function collision(aa,bb) { angle= Math.atan2(bb.ny-aa.ny,bb.nx-aa.nx); //dy/dx ra=aa.a*deg2rad;rb=bb.a*deg2rad;m2=aa.m+bb.m;//constante pr eviter les recalculs ca=Math.cos(angle); sa=Math.sin(angle); //if (d) alert("aa [angle depart="+aa.a+", vitesse="+aa.v+"]\nbb [angle depart="+bb.a+", vitesse="+bb.v+"]\n angle collision="+angle*rad2deg); //calcul des vitesses normal et perpendiculaire au choc va_norm=aa.v*Math.cos(-ra+angle); va_perp=aa.v*Math.sin(-ra+angle); vb_norm=bb.v*Math.cos(-rb+angle); vb_perp=bb.v*Math.sin(-rb+angle); //if (d) alert("va_norm :"+va_norm+", va_perp :"+va_perp+"\nvb_norm :"+vb_norm+", vb_perp :"+vb_perp); //conservation energie va2_norm=( (aa.m-bb.m)/m2)*va_norm+( (2*bb.m)/m2 )*vb_norm; vb2_norm=( (bb.m-aa.m)/m2)*vb_norm+( (2*aa.m)/m2 )*va_norm; //if (d) alert("va2_norm :"+va2_norm+", vb2_norm :"+vb2_norm); //nouvelle position, on resort ses cours de maths va2x=va2_norm*ca-va_perp*Math.cos(angle+Math.PI/2); //cos(a+PI/2) = -sin(a) va2y=va2_norm*sa-va_perp*Math.sin(angle+Math.PI/2); //sin(a+PI/2) = cos(a) vb2x=vb2_norm*ca-vb_perp*Math.cos(angle+Math.PI/2); vb2y=vb2_norm*sa-vb_perp*Math.sin(angle+Math.PI/2); //if (d) alert("va2x :"+va2x+", va2y :"+va2y+"\nvb2x :"+vb2x+", vb2y :"+vb2y); //recalcul angle + vitesse aa.v = Math.sqrt(va2x*va2x+va2y*va2y); bb.v = Math.sqrt(vb2x*vb2x+vb2y*vb2y); aa.a = Math.atan2(va2y,va2x)*rad2deg; bb.a = Math.atan2(vb2y,vb2x)*rad2deg; } //effecute le deplacement de la boule function bougeMoi() { htm=""; stopper = false; for (e=0;e<balles.length;e++) { b=balles[e]; //seuil de deplacement avant stop if (b.v<seuilStop) b.v=0; if (b.v>0 && !stopper) stopper = true; //b.v*=0.998; b.v-=coefLin; //retourne un angle entre 0 & 360 if (b.a<0) do {b.a+=360;} while(b.a<0); if (b.a>360) do {b.a-=360;} while(b.a>360); nx=b.x;ny=b.y; if (b.v>0) //en javascript 0*cos(a) <>0 ??? { nx = b.x+b.v*Math.cos(b.a*deg2rad); ny = b.y+b.v*Math.sin(b.a*deg2rad); } //TODO: il faudrait trouver un truc ici pr eviter que les boules se chevauche b.nx=nx; b.ny=ny; //si mode debug on affiche les info de positions //if (d) htm+="a :"+parseInt(b.a,10)+" - x :"+parseInt(nx,10)+" - y :"+parseInt(ny,10)+" - v :"+parseInt(b.v*100)/100+"<br>"; } if (!stopper) { //alert('stop'); pause = true; } //pause = !stopper; if (d) _("debug_").innerHTML=htm; } //replace les boules au bonne positions function placeMoi() { for (e in balles) { b=balles[e]; _(b.id).style.left=parseInt(b.x,10)-b.r+minx; _(b.id).style.top=parseInt(b.y,10)-b.r+miny; } } //places les elements à l'ecran function ajoutBalle() { htm=""; for (e in balles) { b=balles[e]; htm+="<img id='"+b.id+"' src='img/"+src[b.t]+".png' style='position:absolute; top:"+(b.y-b.r+miny)+"px;left:"+(b.x-b.r+minx)+"px;width:"+(b.r*2)+"px;height:"+(b.r*2)+"px;'>"; } _("ecran").innerHTML = htm; } //------------------------------------- //------------------------------------- function ajoutPix(x, y, l, h, c) { return "<div style='left:"+x+"px;top:"+y+"px;width:"+l+"px;height:"+h+"px;background-color:"+c+";overflow:hidden;position:absolute;'><\/div>"; } /* tracer d'une droite avec l'algorithme Bresenham -modifié pour créer un effet d'epaisseur x1,y1,x2,y2 : coordonnée du segment diam : diametre du segment c : couleur
  • /
function ligne2(x1, y1, x2, y2, diam, c) { htm=""; if (c==null) c="black"; //couleur par defaut if (diam==null) diam=2; //epaiseur par defaut if(x1 > x2) { //swap(x1,x2); var z = x2;x2 = x1;x1 = z; z = y2;y2 = y1;y1 = z; } var dx = x2-x1, dy = Math.abs(y2-y1); //delta x & y var x = x1, y = y1; var ySens = (y1 > y2)? -1 : 1; //direction de Y if(dx < dy) { var d2 = diam>>1; //equivalent à Math.round(diam/2); var dxy = 2*(dx - dy), p = 2*dx-dy, py = y; if(y2 > y1) { while(dy > 0) { --dy; y += ySens; if(p > 0) { htm+=ajoutPix(x++, py, diam, y-py+d2,c); p += dxy; py = y; } else p += 2*dx; } htm+=ajoutPix(x2, py, diam, y2-py+d2+1,c); } else { while(dy > 0) {--dy; if(p > 0) { htm+=ajoutPix(x++, y, diam, py-y+d2,c); y += ySens; p += dxy; py = y; } else { y += ySens; p += 2*dx; } } htm+=ajoutPix(x2, y2, diam, py-y2+d2,c); } } else { var dxy = 2*(dy - dx), p = 2*dy-dx, px = x; d2 = diam>>1 ; while(dx > 0) {--dx; ++x; if(p > 0) { htm+=ajoutPix(px, y, x-px+d2, diam,c); y += ySens; p += dxy; px = x; } else p += 2*dy; } htm+=ajoutPix(px, y, x2-px+d2+1, diam,c); } return htm; }

Codes Sources

A voir également

Vous n'êtes pas encore membre ?

inscrivez-vous, c'est gratuit et ça prend moins d'une minute !

Les membres obtiennent plus de réponses que les utilisateurs anonymes.

Le fait d'être membre vous permet d'avoir un suivi détaillé de vos demandes et codes sources.

Le fait d'être membre vous permet d'avoir des options supplémentaires.