vue Étape 4 : Ennemis et lance-missiles
Kommunauty
Connexion
Inscription
« Étape 3 : création de la map

Étape 4 : Ennemis et lance-missiles

le 6 septembre 2011 • Flash • par Dawlin

Alors ça c'est l'étape que tout le monde attend le plus parce que c'est trop funky fresh de mettre plein de mobs et de les défoncer à coup de lance-missile.

Certes.

Mais c'est très compliqué, d'une part parce qu'il faut réussir à commander les missiles pour qu'ils s'arrêtent contre les murs par exemple, d'autre part parce qu'il faut gérer les déplacement des monstres et le fait qu'eux aussi, tirent

Allons-y

Le lance-missiles

Ok. Donc on veut que notre perso crache du missile

Le principe de déplacement va être le même qu' dmode = 1 : on applique une rotation au missile, et on le fait avancer perpetuellement avec une certaine vitesse.

Commençons par définir des constantes dans constantes.as :


// Missiles
vitessem = 9;
fm = 20;

On a donc la vitesse des missiles, et un temps. Ce temps est la durée pendant laquelle on ne peut pas tirer de missile entre chaque tir. Sinon c'est trop facile

On va commencer par placer un code sur jeu.fla, juste après le début de la boucle this.onEnterFrame :


// Lancement de missiles :
// Fonction pour lancer les missiles (on va l'écrire tout de suite après)
missiliser();
// Fonction pour vérifier si on a le droit de lancer un missile
tm++;
if(tm >= fm){
interm = false;
tm=0;
}

On switche sur missiles.as qu'on va fraîchement entamer avec ceci :


//--------ENVOI DE MISSILES----------\\
missiliser = function(){
//si on prece space
if(Key.isDown(Key.SPACE)){
//Si l'autorisation de lancer un missile est accordée (false)
if(interm == false){
//On enlève l'autorisation pour ne plus en renvoyer
interm = true;
//on crée le missile
var missi:MovieClip  = map.attachMovie("fleche","m"+j,map.getNextHighestDepth());
//on place le missile
missi._x = perso._x-map._x;
missi._y = perso._y-map._y;
//on position le missile
missi._rotation = perso._rotation;

// On le décale pour qu'il sorte du bout du canon
// NB : le 30 ici est placé arbitrairement
missi._y -= 30*Math.cos(missi._rotation*Math.PI/180);
missi._x += 30*Math.sin(missi._rotation*Math.PI/180);
//Et à chaque frame on lui fait aller tout droit devant lui :
missi.onEnterFrame = function(){
this._y -= vitessem*Math.cos(missi._rotation*Math.PI/180);
this._x += vitessem*Math.sin(missi._rotation*Math.PI/180);

// Maintenant on calcule sa case pour savoir si il se cogne contre un mur :
mCaseX = Math.floor(this._x / larg);
mCaseY = Math.floor(this._y / larg);
if(frottArr[myMap[mCaseY][mCaseX]] == 10){
// Le cas échéant on le supprime
this.removeMovieClip();
}

// On détecte également si on a touché un ennemi :
for(var z in map.contM){
if(this.hitTest(map.contM[z])){
// Puis on détruit le monstre et le missile
map.contM[z].removeMovieClip();
this.removeMovieClip();  
}
}

}
//on incrémente le n° de missile
j++;
}
}
}

Globalement, vous pouvez tout comprendre, en plus c'est commenté . tout sauf ceci :


// On détecte également si on a touché un ennemi :
for(var z in map.contM){
if(this.hitTest(map.contM[z])){
// Puis on détruit le monstre et le missile
map.contM[z].removeMovieClip();
this.removeMovieClip();  
}
}

Ici on va parcourir le clip contM qui contient les méchants, et si la flèche touche un de ces méchants, elle est détruite et le méchant aussi.

Mais... Quels méchants ?

Ah oui,

Les Méchants

Les méchants sont stockés pour l'instant dans la bibliothèque. On va s'arranger pour qu'ils apparaissent aléatoirement sur la map, à l'intérieur d'un clip.

Pourquoi les mettre à l'intérieur d'un clip ? Parce qu'il sera plus facile de faire la liste des éléments qui se trouvent dans ce clip, pour par exemple détecter si ils ont été touchés par un missile

Ce clip s'appelle contM et est placé dans la map. Mais rassurez-vous, on va le faire dynamiquement bientôt.

Mais d'abord, on place des constantes dans constantes.as pour paramétrer ces méchants :


// Ennemis
nombreMechants = 2;
ligneVue = 200;
freqTirM = 50;

Ici on aura 2 méchants qui apparaîtront, qui s'apercevront de la présence du perso quand celui-ci sera à une distance de 200 pixels d'eux, et qui tireront au maximum toutes les 50 frames, soit deux secondes

Puis, toujours dans ce même fichier, on rajoute des fonctions qui nous seront utiles, notament une fonction pour calculer la distance entre deux clips :


//############# Quelques fonctions utiles

import mx.utils.Delegate

// Déclaration de la fonction aléatoire
function randRange(min:Number, max:Number):Number {
var randomNum:Number = Math.floor(Math.random()*(max-min+1))+min;
return randomNum;
}

//pour arrondir :
arrondir = function(vnombre,vvirgule){
nombre = vnombre;
//10 puissance vvirgule, vvirgule étant le nombre qu'on veut de chiffres après la virgule
virgule = Math.pow(10,vvirgule);

arrondi=(Math.round(nombre*virgule))/virgule;
return(arrondi);
}

//Fonction pour calculer la distance entre deux clips
distancer = function(voc1,voc2){
var clp1:MovieClip = voc1;
var clp2:MovieClip = voc2;

// Ici je modifie un peu en prenant en compte la position de la map
xa = clp1._x-map._x;
xb = clp2._x;

ya = clp1._y-map._y;
yb = clp2._y;


xf = (xb-xa);
xf = xf*xf;

yf = (yb-ya);
yf = yf*yf;

f = xf+yf;

dist = Math.sqrt(f);

return(dist);
// Pour comprendre, prendre une feuille, un crayon, et le théorême de pithagore :)
}

Note : ces fonctions ont été rédigées par moi pour les deux dernière, randRange se trouve dans l'aide de flash (F1) et elle permet de générer un nombre aléatoirement entre "min" et "max"

Passons à mechantsMobiles.as :


//########## Gestion de quand les méchants tirent :
gestTirMechants = function(vocc){
// on indique à qui s'applique ce script
var mechant:MovieClip = vocc;
// On vérifie si le peros n'est pas dans la ligne de vue du méchant :
if(distancer(perso,mechant)<=ligneVue){
// Alors on calcule l'angle pour se tourner vers lui (voir les formules de trigo de base)
if(perso._y-map._y <= mechant._y){
angle = - Math.atan((mechant._x-(perso._x-map._x))/(mechant._y-(perso._y-map._y)))*180/Math.PI;
}
else{
angle = 180 - Math.atan((mechant._x-(perso._x-map._x))/(mechant._y-(perso._y-map._y)))*180/Math.PI
}
//trace(angle);
// Alors on se tourne vers lui
mechant._rotation = angle;
// Alors on l'attaque si on a le droit de tirer
if(mechant.tirOk <= 0){
//On remet l'interdiction de tirer en rechargeant le timer
mechant.tirOk = freqTirM;
// On fait apparaître un missile
var missile:MovieClip = mechant._parent._parent.attachMovie("fleche","f"+mechant._parent._parent.getNextHighestDepth(),mechant._parent._parent.getNextHighestDepth());
// On le place, on le rotate comme le méchant
missile._x = mechant._x;
missile._y = mechant._y;
missile._rotation = mechant._rotation;
// On le décale pour qu'il sorte du bout du canon
// NB : le 30 ici est placé arbitrairement
missile._y -= 30*Math.cos(missile._rotation*Math.PI/180);
missile._x += 30*Math.sin(missile._rotation*Math.PI/180);

// Puis on le fait partir
missile.onEnterFrame = function(){
this._y -= (vitessem-3)*Math.cos(missile._rotation*Math.PI/180);
this._x += (vitessem-3)*Math.sin(missile._rotation*Math.PI/180);
// On vérifie si il ne sort pas de la map (même principe que pour les missiles d'avant)
mCaseX = Math.floor(this._x / larg);
mCaseY = Math.floor(this._y / larg);
if(frottArr[myMap[mCaseY][mCaseX]] == 10){
// Le cas échéant on le supprime
this.removeMovieClip();
}
// On vérifie aussi si il ne touche pas le perso
if(this.hitTest(perso)){
perso.vie --;
this.removeMovieClip();
}
}
// Oulala ! Des boucles imbriquées dans des boucles imbriquées dans ... C'est pas super propre ! Mais bon...
}
}
//Grâce à cette ligne, l'interdiction de tirer va s'enlever au bout d'un moment.
mechant.tirOk --;
}

Oh ! Quelque chose est apparu ! Vous avez vu également


perso.vie --;

C'est quand le héros perd de la vie ! Il a fallu d'abord lui assigner des points de vie de base dans constantes.as :


perso.vie = 5;

Et il faut se débrouiller pour afficher la vie. Moi comme vous le voyez dans les tests je l'affiche dans le champ de texte qu'on a préparé dans l'étape 1

Voilà pour les faire tirer. Je sais que ce tutoriel c'est surtout du code à manger, mais d'un autre côté ça ne peut pas s'expliquer en rajoutant un paragraphe puis un autre, puis une ligne ici ça prendrait des heures. Alors excusez-moi

Maintenant nos ennemis ne sont toujours pas apparus, donc on va y remédier. On va se servir de la constante "nombreMechants" pour savoir combien on en ajoute


// En ce qui concerne les méchants mobiles
//On crée un clip qui va les contenir, à l'intérieur de map
map.createEmptyMovieClip("contM",map.getNextHighestDepth());
//Puis on crée une boucle, qui va générer autant de méchants que demandés
for(k=0;k<nombreMechants;k++){
//Même techniques que maintes fois : on attache, on place
var mech:MovieClip = map.contM.attachMovie("mechant","m"+k,map.contM.getNextHighestDepth());
mech._x = randRange(larg,(map._width-2*larg));
mech._y = randRange(larg,(map._height-2*larg));

// On lui fixe un interrupteur pour savoir si il a le droit de tirer
mech.tirOk = 60;
}

Puis, il faut lui donner un peu d'intelligence à ce méchant : Un méchant fait deux choses (dans les films en tout cas) : Soit il marche tranquillou sans rien faire (gros feignant), Soit il tire sur tout ce qui bouge

Donc, on va lui dire que si le héros est à portée de tir (souvenez vous de la constante "ligneVue = 200;" ) on lui tire dessus, sinon, on se déplace tranquillou.

On va pour cela utiliser la fonction qu'on vient de déclarer, gestTirMechants(); ainsi qu'un fonction de déplacement aléatoire. Ah ! N'y aurait-il pas un tutoriel spécial sur le déplacement aléatoire, sorti le 1er Septembre 2011 sur Kommunauty.fr ?? Ah mais si ! Il est là ! Régalez-vous, parce qu'on va justement l'utiliser :

Juste avant la fin de l'accolade, c'est à dire juste après "mech.tirOK = 60", placez ceci :


//on dirige le méchant
mech.onEnterFrame = function(){
// Tout dépend de si le perso est à portée de tir
if(distancer(perso,this)<=ligneVue){
// Dans ce cas-ci on tire
gestTirMechants(this);
}
// sinon on applique la fonction de déplacement aléatoire du tutoriel du même nom :)
else{
ax = this._x;
ay = this._y;
timer++;
if(timer>freq*coef){
timer=0;
}else if (timer>freq){
this._rotation+=r;
}else{
r = randRange(rarot,rrot);
}
this._y -= vitesseMech*Math.cos(this._rotation*Math.PI/180);
this._x += vitesseMech*Math.sin(this._rotation*Math.PI/180);
// Et comme pour tout, on restaure les positions si on se retrouve dans une case bloquante :)
mechCaseX = Math.floor(this._x / larg);
mechCaseY = Math.floor(this._y / larg);
if(frottArr[myMap[mechCaseY][mechCaseX]] == 10){
// Le cas échéant on le supprime
this._x = ax;
this._y = ay;
}
}

}

Ah mais fichtre ! J'ai encore oublié les constantes. D'autant plus que ce déplacement aléatoire consomme énormément en paramètre.

Back To constantes.as, et à la fin (ou au début, ou avant les fonctions) :


// Déplacements aléatoires des ennemis
rrot = 10;
rarot = 0-rrot;
vitesseMech = 3;
//Timerisation
timer =0;
freq = 20;
coef = 1.5;

Et. Voilà.

On teste, pour voir :

QUOI ? Ton jeu à toi les monstres ils explosent quand ils meurent !!

Ah ? Ah oui ! Mais c'est que j'ai déjà lu l'étape suivante du tutoriel

hop hop, au passage, voilà les sources de cette étape : Cliquez Ici

Étape 5 : Animations »


  
Aucun commentaire

Sois le premier à débuter une discussion à propos de cet article !



Ajoute un commentaire !

Ajouter une image... Trouvée sur internet » De mon PC »
Adresse URL :
Adresse de la page de la vidéo :
Taille du texte :
Couleur du texte :

Article lu 3568 fois.