JeuWeb - Crée ton jeu par navigateur
[Résolu][AS2.0] Ciblage de MC et déplacement de projectiles - Version imprimable

+- JeuWeb - Crée ton jeu par navigateur (https://jeuweb.org)
+-- Forum : Discussions, Aide, Ressources... (https://jeuweb.org/forumdisplay.php?fid=38)
+--- Forum : Programmation, infrastructure (https://jeuweb.org/forumdisplay.php?fid=51)
+--- Sujet : [Résolu][AS2.0] Ciblage de MC et déplacement de projectiles (/showthread.php?tid=5791)



[Résolu][AS2.0] Ciblage de MC et déplacement de projectiles - Globe - 04-11-2011

Bonjour à tous, depuis peu je profite des quelques minutes de temps libre que j'ai par jour pour apprendre Flash et ActionScript.

Quelques points à prendre en considération : je suis une bille absolue en Maths, et mon code ressemble plus à du bricolage qu'à quelque chose de véritablement structuré =D

Maintenant passons à ce qui me pose soucis Smile

J'entreprend la création d'un jeu flash, un jeu de Tower Defense. Je n'utilise pas une map faite de tile, mais une image et j'utilise actionscript pour gérer le tout.

Pour le moment j'ai réussi à créer ma map, donner la possibilité de créer des tours, et au sbires d'avancer le long du passage. Chaque sbire est une instance du MovieClip Creep1 qui comprend la santé du sbire, sa vitesse de déplacement et les fonctions qui lui sont utiles (mourir par exemple). Au lieu d'avoir un placement de tours libres j'utilise des slots, qui sont des instance du MovieClip towerSlot. Lors de la construction d'une tour le towerSlot change simplement de frame pour passer à l'image de la tour construite, et l'instance prend pour valeurs de dégats, cadence de tir... celles de la tour construite (suis-je clair ? Tongue).

Ainsi j'ai les classes Creep1.as et towerSlot.as qui sont attachées à ma scène.

Je voudrais désormais faire cibler et tirer mes tours, tous mes creeps sont stockés à la création dans un array creeps. Voici donc comment je m'y prend pour le moment :

towerSlot.as
			
if(fireTimer>=fireRate) { // si le décompte de tir est supérieur à la cadence de tir de la tour

for(var i=0;i<_root.creeps.length;i++){ // je parcour mon array creeps
if(Math.sqrt(Math.pow(_root.creeps[i]._y - this._y, 2) + Math.pow(_root.creeps[i]._x - this._x, 2)) < this.range){ // une formule piquée sur le net pour savoir si le creep est à portée
target = _root.creeps[i];


//j'attache une instance du MC projectile et je défini sa cible
var projectile = _root.attachMovie( "projectile" , "projectile" + _root.getNextHighestDepth() , _root.getNextHighestDepth(), {_x:this._x,_y:this._y} );
projectile.hit = this.dmg;
projectile.targetX = _root.creeps[i]._x;
projectile.targetY = _root.creeps[i]._y;


}

}
fireTimer = 0;

}

Pour le moment tout fonctionne, seul pépin de temps en temps la tour tire deux projectiles au lieu d'un, j'ignore pourquoi.

Maintenant la seconde partie du code contenue dans projectile.as
            
this.yDist=this.targetY - this._y; //distance entre le projectile et l'ennemi
this.xDist=this.targetX - this._x; //distance entre le projectile et l'ennemi (y)
this.angle=Math.atan2(this.yDist,this.xDist);//l'angle de déplacement, une autre formule piquée sur le net
this.ySpeed=Math.sin(this.angle) * this.speed;//déplacement vertical en fonction de l'angle auquel est l'ennemi
this.xSpeed=Math.cos(this.angle) * this.speed;//déplacement horizontal en fonction de l'angle auquel est l'ennemi
//déplacement du projectile
this._x+= this.xSpeed;
this._y+= this.ySpeed;
for(var i in _root.creeps) { // je parcours mon tableau creeps

if (this.hitTest(_root.creeps[i])){ //si le projectile touche un creep
_root.creeps[i].health-=this.hit; // je réduis sa santé
this.removeMovieClip(); // j'efface le MC du projectile

}

Tien d'extraordinairement compliqué pour la plupart d'entre vous je suppose (moi les maths... Big Grin)

Le problème qui se pose principalement avec mon script, c'est que bien souvent le projectile n'atteins pas sa cible, en fait mes creeps se déplacent en fonction de leur vitesse d'un waypoint (point de passage) à un autre à un certain angle. Le problème est que le temps que le projectile arrive aux coordonnées du creep, lui à déjà bougé. Il faut donc que je réussisse à compenser son déplacement.

Je viens donc savoir si quelqu'un aurait une idée, comment recalculer la trajectoire de mon projectile pour qu'il prenne en compte le déplacement de mon creep (je veux que 100% des tirs touchent leurs cible).

J'espère avoir réussi à me faire comprendre et ne pas trop m'être embrouillé, je sais bien qu'il y à peu de codeurs AS sur ce forum, mais c'est très proche du javascript et c'est plus du coté logique que j'ai besoin d'un coup de main que du coté développement.

Merci d'avoir pris le temps de me lire Tongue

EDIT : Voici ce que ça donne pour le moment : http://www.dreamglobe.fr/dump/td.swf
Et j'ai trouvé pourquoi elles tirent plusieurs projectiles, en fait elles visent tous les ennemis à portée donc je pourrais régler ce problème assez simplement.


RE: [AS2.0] Ciblage de MC et déplacement de projectiles - Thêta Tau Tau - 04-11-2011

Salut,

Je connais pas l'AS, je vais donc rester au côté mathématique de la chose sans entrer dans le code. J'expliquerais juste le concept à toi de coder ça.


t=0 correspond au moment où le projectile est tiré (donc au moment où on fait tout le calcul). Un t>0 correspond donc à une projection dans le futur.

On cherche donc à ce que le creep et le projectile soit au même moment au même endroit

appelons Tc ce moment, avec c pour contact
On a donc :
Code :
creep.x(Tc) == projectile.x(Tc)     (en considérant x(t) comme une méthode renvoyant l’ordonnée au temps t)
creep.y(Tc) == projectile.y(Tc)
(attention c'est pas du code mais juste une relation mathématique).

Ce qu'on va chercher c'est Tc, duquel on pourra déduire tout le reste.
Il peut en théorie y avoir plusieurs solutions mais on se contentera de la première trouvée (en pratique je pense qu'il y en aura rarement plusieurs d'ailleurs, à part dans le cas de creep très rapides se déplaçant en zigzag).

Les méthodes x et y du creep sont assez simples à faire (mêmes formules que celles que tu utilises déjà), mais assez lourdes donc je m'attarderais pas dessus.

Pour celles du projectile c'est impossible car on cherche justement l'angle de tir, mais on va faire sans.

Maintenant il n'y a "plus qu'à" faire varier t jusqu'à ce qu'on trouve une valeur vérifiant les équations ci-dessus. Mais en voilà une plus pratique :
Code :
(creep.x(Tc)-projectile.x(Tc))^2 + (creep.y(Tc)-projectile.y(Tc))^2=0

on testera donc :
(creep.x(t)-projectile.x(t))^2 + (creep.y(t)-projectile.y(t))^2 == 0
L’intérêt de cette formule c'est que pour n’importe quel t elle sera positive, donc on peut remplacer le "==0" par un "< marge d'erreur" vu que trouver pile poil le bon Tc demanderais trop de puissance de calcul.
Second intérêt : cette "marge d'erreur" c'est le carré de la distance entre le projectile et le creep, donc par exemple si tu fixe ta marge d'erreur à 9, ton projectile passera à moins de 3px du creep, et vu que les sprites font plusieurs pixels, il y aura contact.


Mais... Mais... T'as pas dit qu'on connaissait pas projectile.x(t) et projectile.y(t)?


Et oui, mais ça ne pose pas de problèmes au final! Quand on va tester une valeur de t, il suffit de considérer qu'on vise à l'endroit où se trouve le creep à ce moment là (donc [creep.x(t),creep.y(t)] ). C'est un raisonnement assez bizare mais très courant en maths (cf. wikipédia).

Du coup :
1- Tu calcules xspeed et yspeed pareil que dans ton code au dessus, en "visant" [creep.x(t),creep.y(t)]
Code :
projectile.x(t)=tower.x+projectile.xspeed * t
projectile.y(t)=tower.y+projectile.yspeed * t

Voilà suffit maintenant de remplacer creep.x(t) et creep.y(t) de la formule pour obtenir un truc calculable. Niveau algo le plus simple serais un truc du genre (mais c'est pas du tout optimisé).

Code :
t=0;
pas=sqrt(marge_d_erreur) / creep.speed; //C'est à la louche faudrait tester.
//On calcule projectile.xspeed et projectile.yspeed
while( (creep.x(t)- tower.x - projectile.xspeed * t)^2 + (creep.y(t)- tower.y - projectile.yspeed * t)^2 > marge_d_erreur )
{
     t = t + pas;
    // On recalcule projectile.xspeed et projectile.yspeed
}
projectile.targetX = creep.x(t);
projectile.targetY = creep.y(t);
(c'est toujours pas du code utilisable c'est juste pour montrer le concept, si il y a besoin et que j'ai le temps je veut bien essayer de coder tout ça en php).





Ou sinon tu triches et tu fait des projectiles "à tête chercheuse" (qui changent de direction donc), qui sont simplistes niveau code.



RE: [AS2.0] Ciblage de MC et déplacement de projectiles - Ter Rowan - 04-11-2011

y aurait pas une erreur de frappe ?

creep.x(t)-projectile.x(t))^2 * (creep.y(t)-projectile.y(t))^2

pour une distance, c'est plus (addition) non ?

creep.x(t)-projectile.x(t))^2 + (creep.y(t)-projectile.y(t))^2



RE: [AS2.0] Ciblage de MC et déplacement de projectiles - Thêta Tau Tau - 04-11-2011

Oui tu as raison, c'est bien une erreur, c'est le théorème de Pythagore donc bien un +. On peut par ailleurs mettre ça à la racine si on préfère (histoire d'avoir la distance et non son carré, même si au final ça change rien).


RE: [AS2.0] Ciblage de MC et déplacement de projectiles - Globe - 05-11-2011

Merci beaucoup de ton aide, c'est assez enrichissant, ça va me permettre de gérer d'autres choses dans le futur et ça me donne un nouvel angle de réflexion pour certaines questions que je me posais.

Pour mon problème actuel j'ai cependant trouvé une solution plus simple une fois l'esprit vidée, en allant bosser à la chaîne ce soir, l'esprit complètement vide ça m'est venu, flash permet en fait quelque chose de beaucoup plus facile.

onEnterFrame() permet d'effectuer un script à chaque changement de frame (24 frames par seconde dans cet exemple). Au lieu d'utiliser les coordonnées du creep en dur comme je le faisais et d'effectuer un calcul de trajectoire préliminaire au tir, à chaque instant du tir la trajectoire va se corriger en utilisant directement les coordonnées du creep, au lieu d'assigner les coordonnées du creep au moment du tir je les utiliserais directement, à chaque frame le projectile recalculeras sa trajectoire en fonction des coordonnées actuelles du creep et non pas celle du moment du tir. Je ne sais pas si je suis clair je rentre tout juste du boulot et je m'apprête à partir en vadrouille donc j'essaye de faire sommaire.

En tout cas merci, je suis sûr que ton approche me sera utile à l'avenir Wink