Forum Solarus-Games francophone

Jeux amateurs => Tutoriaux => Tutoriaux "Développement" => Discussion démarrée par: SaniOKh le 19 Juin 2007 à 22:46

Titre: Déplacements à collisions glissantes (par HCkev)
Posté par: SaniOKh le 19 Juin 2007 à 22:46
Ce tutorial montre ma manière de procéder pour ce qui est des déplacements à collisions glissantes pour les jeu Zelda et autre jeux à déplacements semblables. Évidemment, il n’y a pas qu’une seule manière de faire ce genre de collisions. Je ne fait que faire part de comment j’ai procédé dans mon jeu.

Attention toutefois, dans ce tuto, vous ne trouverez aucun bout de code pour un logiciel spécifique; je ne fait qu’expliquer les étapes à suivre, ce qu’il faut vérifier(à l’aide de conditions) et ce qu’il faut faire, dans le but de vous faire comprendre comment ça marche. Je donnerai toutesfois, à la fin du tuto, un gabarit de conditions de base, il vous suffira de les traduire dans le langage utilisé. Ce tuto devrait normalement fonctionner sur tout logiciel ayant un langage de programmation par scripts. Logiciels qui devraient normalement être « compatible » : Game maker, Multimedia fusion, The games factory, forcément la programmation « pure » (comme C++), et sûrement plusieurs autres.



Pour commencer, nous allons d’abord aborder le mouvement de Link ainsi que les collisions ordinaires; si le moteur ne fonctionne pas de la même façon dont je vais vous montrer, il fort possible que ces collisions glissantes du tuto ne fonctionnent pas.

Donc, pour bouger Link, je procède de la manière suivante : Je travaille directement avec les coordonnés X et Y de Link. Les chiffres des coordonnés représentent donc le pixel central du sprite. En ligne droite(haut, bas, gauche et droite), Link bouge d’un certain nombre de pixels; tout dépend du logiciel utilisé, du framerate, etc. Dans mon jeu, Link bouge de 6 pixels sur une ligne droite, et de 4 pixels(sur chaque axe, évidemment) en diagonale. Je conseille de créer une variable pour les deux chiffres de mouvement afin de pouvoir les changer à tout moment. Notez que les mouvements diagonaux doivent être environ le 2/3 du mouvement en ligne droite(Évidemment, arrondissez, parce qu’un sprite ne peut pas bouger d’un demi pixel sans devenir flou). Donc, dans ce tuto, j’utiliserai donc 6 et 4 comme mouvements.



Pour bien visualiser la chose, nous allons utiliser des petits croquis. Prenons celui-ci par exemple :
(http://img186.imageshack.us/img186/2856/collisonsschema01uo0.gif)

Il s'agit en quelque sorte d'une vue agrandie; chaque carré représente un pixel(Et non un tile !). Les pixels bleus représentent des murs et donc où Link ne doit pas marcher. Le carré vert est le pixel central de Link(Et non Link lui-même !), et donc ses coordonés X et Y.


Quand vient le temps de bouger, il faut d'abord vérifier, avant même de faire bouger Link, si, 6 pixels plus loin, s’il toucherait à un mur, s'il avançait(Donc il n'avance pas encore, alors ne faites pas bouger Link !). Si la voie est libre, donc s'il ne toucherait à aucun décors, il avance directement de 6 pixels. Dans ce croquis, si on bouge vers le haut, Link peut très bien avancer de 6 pixels sans problème:
(http://img186.imageshack.us/img186/3756/collisonsschema02tj3.gif)


Par contre, s'il y aurait collision, comme dans le prochain mouvement vers le haut que ferait Link, il n’avancerait pas. Il faut déterminer de combien de pixel il pourrait avancer pour se coller sur le mur sans le toucher. Il faut tout d’abord faire une "copie"(Attention, pas un alias, il faut que la variable originale reste intact) de la variable de déplacement dans une autre variable, puis faire une boucle qui va diminuer la variable de 1, jusqu'a ce que Link puisse avancer. Autrement dit, si Link est à 3 pixels du mur, la boucle va soustraire 1 au 6, jusqu'a ce qu'il arrive à 3. Il s’agit habituellement d’utiliser une boucle while dans laquel on vérifie si Link toucherait aux décors s’il avançait de (la variable copiée) pixels. Il est bien aussi de vérifier si la variable est supérieur à zéro, sinon on risque d’avoir des valeurs négatives dans certains cas. Donc il est temps de faire bouger Link, il avancera donc de 3 pixels(dans ce cas-ci), de manière à être collé sur le mur, et ne pas arrêter à 3 pixels du mur.
(http://img180.imageshack.us/img180/5596/collisonsschema03im6.gif)

Il faut faire le même procéder dans les autres directions. Évidemment, pour bouger à gauche, il faut utiliser des valeurs négatives, même chose pour bouger vers le haut.

Maintenant qu’on bouge dans les 4 directions de base, il faut faire les mouvements en diagonales. Il s'agit du même principe, sauf qu'on travaille à la fois avec X et Y, selon la direction. Pour bouger en direction haut-droite par exemple, on bougera de x et –y. Et on fait la même chose pour les autres directions !


Maintenant, passons aux fameuses collisions glissantes. C’est pas compliqué, on se sert du même principe ! Commençons d’abord avec le mouvement en ligne droite, avec le glissement en diagonale. Nous verrons le fonctionnement du glissement en ligne droite plus loin, c’est légèrement différent.

Lorsque Link est collé sur le mur et ne peut plus avancer, il est temps de vérifier si Link pourrait glisser en diagonale. La manière la plus simple est de vérifier des deux côtés si Link toucherait à un mur s'il avançait en diagonale. Dans ce croquis, où Link continue d'avancer vers le haut, on vérifie à gauche:
(http://img180.imageshack.us/img180/2619/collisonsschema04ls9.gif)


Nous voyons très bien que Link ne peut pas bouger là, il se retrouverait coincé dans le décors. Vérifions ensuite à droite:
(http://img140.imageshack.us/img140/6968/collisonsschema05zm6.gif)


Et voilà, il n’a aucun obstacle. Nous allons donc déplacer Link vers haut-droite:
(http://img183.imageshack.us/img183/2091/collisonsschema06um4.gif)


Encore une fois, il suffit de faire les vérifications de chaque côtés.


On pourrait s’arrêter ici. Mais il reste un défaut: En effet, si on regarde bien, Link pourrait encore glisser d'un pixel en haut-droite, mais il n’avancera pas parce qu’il y a un mur 4 pixels plus loin. De plus, lorsque l'on retrouve un passage étroit, pour une porte notamment:
(http://img223.imageshack.us/img223/7934/collisonsschema07ux0.gif)


Link devrait normalement glisser vers la gauche, mais en utilisant cette méthode, il ne pourra glisser que s'il est à 4 pixels de la porte; Pas très pratique !

Pour régler ce problème, rien de plus simple : une boucle ! Nous allons faire exactement comme si nous nous déplaçons normalement en diagonale avec les flèches; nous allons diminuer la variable(en fait, la copie) jusqu'a ce que Link puisse passer, et ce, de chaque côté.
(http://img182.imageshack.us/img182/641/collisonsschema08fp8.gif)


Et voilà ! C’est fini. En fait, presque : Il reste le glissement en ligne droite, lorsque Link marche en diagonale. Exemple :
(http://img120.imageshack.us/img120/7781/collisonsschema09gm4.gif)

Là, on avançait normalement en direction haut-droite, puis le moteur de base a collé Link au mur. Comme Link ne peut plus avancer, nous allons vérifier s'il peut y avoir glissement. Nous nous déplacions donc en direction haut-droite, nous allons donc vérifier si Link peut marcher vers le haut, ou vers la droite.
(http://img440.imageshack.us/img440/4079/collisonsschema10bd4.gif)

Et donc, c'est un peu le même principe que le glissement en diagonale. Et donc, encore ici, il y a encore un problème dans ce cas:
(http://img244.imageshack.us/img244/7003/collisonsschema11pv1.gif)


Il faut encore utiliser une boucle !
(http://img218.imageshack.us/img218/7354/collisonsschema12kp5.gif)


Et voilà, il suffit de faire ces actions pour chaque direction. C’est terminé !



Et maintenant, la partie la plus attendue ! Un code. Comme j’ai dit plus haut, ce n’est pas un code sur un logiciel précis; je n’ai mis que la structure des conditions if et boucles while, il vous suffira de changer les conditions pour rendre le tout fonctionnel dans votre langage. Généralement, il ne faudra changer que ce qui se trouve entre [ crochets ].


// Ces variables ne devraient pas changer
move_pix_s = 6; // Mouvement en ligne droite (_s pour "straight")
move_pix_d = 4; // Mouvement en diagonale    (_d pour "diagonal")


if ( [Touche "Haut" appuyée]  &&  [Touche "Gauche" appuyée] )
{
// Boucle qui permet de bouger du bon nombre de pixels pour coller Link au mur
move_pix_c = move_pix_d; // Copie de la variable, afin de garder l'autre variable intacte
while ( move_pix_c > 0  &&  [Position X - move_pix_c, Position Y - move_pix_c ---> touche pas aux murs] )
{
move_pix_c--;
}

// Pouvons-nous bouger ?
if ( move_pix_c > 0 )
{
[Soustraire move_pix_c à la position Y de Link]
[Soustraire move_pix_c à la position X de Link]
}
else
{
// Pouvons-nous glisser vers le haut ?
move_pix_c = move_pix_s; // Copie de la variable, afin de garder l'autre variable intacte
while ( move_pix_c > 0  &&  [Position X, Position Y - move_pix_c ---> touche pas aux murs] )
{
move_pix_c--;
}

if ( move_pix_c > 0 )
{
[Soustraire move_pix_c à la position Y de Link]
}
else
{
// Sinon... Pouvons-nous glisser vers la gauche ?
move_pix_c = move_pix_s; // Copie de la variable, afin de garder l'autre variable intacte
while ( move_pix_c > 0  &&  [Position X - move_pix_c, Position Y ---> touche pas aux murs] )
{
move_pix_c--;
}

[Soustraire move_pix_c à la position X de Link]
}
}
}
else if ( [Touche "Haut" appuyée]  &&  [Touche "droite" appuyée] )
{
// Boucle qui permet de bouger du bon nombre de pixels pour coller Link au mur
move_pix_c = move_pix_d; // Copie de la variable, afin de garder l'autre variable intacte
while ( move_pix_c > 0  &&  [Position X + move_pix_c, Position Y - move_pix_c ---> touche pas aux murs] )
{
move_pix_c--;
}

// Pouvons-nous bouger ?
if ( move_pix_c > 0 )
{
[Soustraire move_pix_c à la position Y de Link]
[Ajouter move_pix_c à la position X de Link]
}
else
{
// Pouvons-nous glisser vers le haut ?
move_pix_c = move_pix_s; // Copie de la variable, afin de garder l'autre variable intacte
while ( move_pix_c > 0  &&  [Position X, Position Y - move_pix_c ---> touche pas aux murs] )
{
move_pix_c--;
}

if ( move_pix_c > 0 )
{
[Soustraire move_pix_c à la position Y de Link]
}
else
{
// Sinon... Pouvons-nous glisser vers la droite ?
move_pix_c = move_pix_s; // Copie de la variable, afin de garder l'autre variable intacte
while ( move_pix_c > 0  &&  [Position X + move_pix_c, Position Y ---> touche pas aux murs] )
{
move_pix_c--;
}

[Ajouter move_pix_c à la position X de Link]
}
}
}
else if ( [Touche "Bas" appuyée]  &&  [Touche "Gauche" appuyée] )
{
// Boucle qui permet de bouger du bon nombre de pixels pour coller Link au mur
move_pix_c = move_pix_d; // Copie de la variable, afin de garder l'autre variable intacte
while ( move_pix_c > 0  &&  [Position X - move_pix_c, Position Y + move_pix_c ---> touche pas aux murs] )
{
move_pix_c--;
}

// Pouvons-nous bouger ?
if ( move_pix_c > 0 )
{
[Ajouter move_pix_c à la position Y de Link]
[Soustraire move_pix_c à la position X de Link]
}
else
{
// Pouvons-nous glisser vers le bas ?
move_pix_c = move_pix_s; // Copie de la variable, afin de garder l'autre variable intacte
while ( move_pix_c > 0  &&  [Position X, Position Y + move_pix_c ---> touche pas aux murs] )
{
move_pix_c--;
}

if ( move_pix_c > 0 )
{
[Ajouter move_pix_c à la position Y de Link]
}
else
{
// Sinon... Pouvons-nous glisser vers la gauche ?
move_pix_c = move_pix_s; // Copie de la variable, afin de garder l'autre variable intacte
while ( move_pix_c > 0  &&  [Position X - move_pix_c, Position Y ---> touche pas aux murs] )
{
move_pix_c--;
}

[Soustraire move_pix_c à la position X de Link]
}
}
}
else if ( [Touche "Bas" appuyée]  &&  [Touche "droite" appuyée] )
{
// Boucle qui permet de bouger du bon nombre de pixels pour coller Link au mur
move_pix_c = move_pix_d; // Copie de la variable, afin de garder l'autre variable intacte
while ( move_pix_c > 0  &&  [Position X + move_pix_c, Position Y + move_pix_c ---> touche pas aux murs] )
{
move_pix_c--;
}

// Pouvons-nous bouger ?
if ( move_pix_c > 0 )
{
[Ajouter move_pix_c à la position Y de Link]
[Ajouter move_pix_c à la position X de Link]
}
else
{
// Pouvons-nous glisser vers le bas ?
move_pix_c = move_pix_s; // Copie de la variable, afin de garder l'autre variable intacte
while ( move_pix_c > 0  &&  [Position X, Position Y + move_pix_c ---> touche pas aux murs] )
{
move_pix_c--;
}

if ( move_pix_c > 0 )
{
[Ajouter move_pix_c à la position Y de Link]
}
else
{
// Sinon... Pouvons-nous glisser vers la droite ?
move_pix_c = move_pix_s; // Copie de la variable, afin de garder l'autre variable intacte
while ( move_pix_c > 0  &&  [Position X + move_pix_c, Position Y ---> touche pas aux murs] )
{
move_pix_c--;
}

[Ajouter move_pix_c à la position X de Link]
}
}
}
else if ( [Touche "Haut" appuyée] )
{
// Boucle qui permet de bouger du bon nombre de pixels pour coller Link au mur
move_pix_c = move_pix_s; // Copie de la variable, afin de garder l'autre variable intacte
while ( move_pix_c > 0  &&  [Position X, Position Y - move_pix_c ---> touche pas aux murs] )
{
move_pix_c--;
}

// Pouvons-nous bouger ?
if ( move_pix_c > 0 )
{
[Soustraire move_pix_c à la position Y de Link]
}
else
{
// Pouvons-nous glisser vers haut-gauche ?
move_pix_c = move_pix_d; // Copie de la variable, afin de garder l'autre variable intacte
while ( move_pix_c > 0  &&  [Position X - move_pix_c, Position Y - move_pix_c ---> touche pas aux murs] )
{
move_pix_c--;
}

if ( move_pix_c > 0 )
{
[Soustraire move_pix_c à la position X de Link]
[Soustraire move_pix_c à la position Y de Link]
}
else
{
// Sinon... Pouvons-nous glisser vers haut-droite ?
move_pix_c = move_pix_s; // Copie de la variable, afin de garder l'autre variable intacte
while ( move_pix_c > 0  &&  [Position X + move_pix_c, Position Y - move_pix_c ---> touche pas aux murs] )
{
move_pix_c--;
}

[Ajouter move_pix_c à la position X de Link]
[Soustraire move_pix_c à la position Y de Link]
}
}
}
else if ( [Touche "Bas" appuyée] )
{
// Boucle qui permet de bouger du bon nombre de pixels pour coller Link au mur
move_pix_c = move_pix_s; // Copie de la variable, afin de garder l'autre variable intacte
while ( move_pix_c > 0  &&  [Position X, Position Y + move_pix_c ---> touche pas aux murs] )
{
move_pix_c--;
}

// Pouvons-nous bouger ?
if ( move_pix_c > 0 )
{
[Ajouter move_pix_c à la position Y de Link]
}
else
{
// Pouvons-nous glisser vers bas-gauche ?
move_pix_c = move_pix_d; // Copie de la variable, afin de garder l'autre variable intacte
while ( move_pix_c > 0  &&  [Position X - move_pix_c, Position Y + move_pix_c ---> touche pas aux murs] )
{
move_pix_c--;
}

if ( move_pix_c > 0 )
{
[Soustraire move_pix_c à la position X de Link]
[Ajouter move_pix_c à la position Y de Link]
}
else
{
// Sinon... Pouvons-nous glisser vers bas-droite ?
move_pix_c = move_pix_s; // Copie de la variable, afin de garder l'autre variable intacte
while ( move_pix_c > 0  &&  [Position X + move_pix_c, Position Y + move_pix_c ---> touche pas aux murs] )
{
move_pix_c--;
}

[Ajouter move_pix_c à la position X de Link]
[Ajouter move_pix_c à la position Y de Link]
}
}
}
else if ( [Touche "Gauche" appuyée] )
{
// Boucle qui permet de bouger du bon nombre de pixels pour coller Link au mur
move_pix_c = move_pix_s; // Copie de la variable, afin de garder l'autre variable intacte
while ( move_pix_c > 0  &&  [Position X - move_pix_c, Position Y ---> touche pas aux murs] )
{
move_pix_c--;
}

// Pouvons-nous bouger ?
if ( move_pix_c > 0 )
{
[Soustraire move_pix_c à la position X de Link]
}
else
{
// Pouvons-nous glisser vers haut-gauche ?
move_pix_c = move_pix_d; // Copie de la variable, afin de garder l'autre variable intacte
while ( move_pix_c > 0  &&  [Position X - move_pix_c, Position Y - move_pix_c ---> touche pas aux murs] )
{
move_pix_c--;
}

if ( move_pix_c > 0 )
{
[Soustraire move_pix_c à la position X de Link]
[Soustraire move_pix_c à la position Y de Link]
}
else
{
// Sinon... Pouvons-nous glisser vers bas-gauche ?
move_pix_c = move_pix_s; // Copie de la variable, afin de garder l'autre variable intacte
while ( move_pix_c > 0  &&  [Position X - move_pix_c, Position Y + move_pix_c ---> touche pas aux murs] )
{
move_pix_c--;
}

[Soustraire move_pix_c à la position X de Link]
[Ajouter move_pix_c à la position Y de Link]
}
}
}
else if ( [Touche "Droite" appuyée] )
{
// Boucle qui permet de bouger du bon nombre de pixels pour coller Link au mur
move_pix_c = move_pix_s; // Copie de la variable, afin de garder l'autre variable intacte
while ( move_pix_c > 0  &&  [Position X + move_pix_c, Position Y ---> touche pas aux murs] )
{
move_pix_c--;
}

// Pouvons-nous bouger ?
if ( move_pix_c > 0 )
{
[Ajouter move_pix_c à la position X de Link]
}
else
{
// Pouvons-nous glisser vers haut-droite ?
move_pix_c = move_pix_d; // Copie de la variable, afin de garder l'autre variable intacte
while ( move_pix_c > 0  &&  [Position X + move_pix_c, Position Y - move_pix_c ---> touche pas aux murs] )
{
move_pix_c--;
}

if ( move_pix_c > 0 )
{
[Ajouter move_pix_c à la position X de Link]
[Soustraire move_pix_c à la position Y de Link]
}
else
{
// Sinon... Pouvons-nous glisser vers bas-droite ?
move_pix_c = move_pix_s; // Copie de la variable, afin de garder l'autre variable intacte
while ( move_pix_c > 0  &&  [Position X + move_pix_c, Position Y + move_pix_c ---> touche pas aux murs] )
{
move_pix_c--;
}

[Ajouter move_pix_c à la position X de Link]
[Ajouter move_pix_c à la position Y de Link]
}
}
}




Et voilà ^_^

Je conseille tout d’abord d’essayer la méthode dans un fichier vierge et de voir comment se comporte le tout, et découvrir comment les « murs » doivent être placés. Si vous avez un quelconque problème, il me ferai plaisir de vous aider.


Astuce: On peut rendre les coins des murs plus « glissants », au besoin, en les coupant en diagonale.


Je répète que ce n’est qu’un manière parmi tant d’autres pour réaliser ce genre de collisions. Il existe notamment un système qui consiste à placer huit capteurs autour de Link et d’effectuer les déplacements nécessaires dépendant quel(s) capteur(s) touche(nt) les murs. Cette méthode à capteurs est notamment utilisée dans la démo de Mercuri’s Chest (si je ne me trompe pas ;))