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;
}
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.