Recherche
Peut être aimerez-vous...
Sections du site
Sites Neamar
Lisez ces articles !
| Déplacement d'un joueur sur un graphe |
|
| Programmation et tuning - Algorithmie et optimisation |
| Écrit par Neamar |
| Lundi, 31 Mai 2010 19:44 |
|
Je commence aujourd'hui une petite série d'articles en préparation de mon nouveau jeu. Pendant que j'y suis aussi, coupons court aux malentendus : il ne s'agit pas de D-Graphe, même si les apparences sont trompeuses et que l'on va travailler avec un graphe. Dans cet article, nous ferons des maths (un peu), de l'algorithmie (plus), du debug (beaucoup) et de l'explication (encore plus).
Problématique : trouver un moyen agréable, intuitif et fonctionnel pour laisser un utilisateur parcourir un graphe (une figure de jeu). Justification de la problématique : imaginez un jeu qui implique de parcourir un graphe, celui-là par exemple : Vous placez votre joueur sur le nœud (rouge) tout en haut, et vous voulez qu'il puisse « parcourir » le graphe pour arriver en bas. Quelles solutions vous viennent à l'esprit ?
Personnellement, ce problème m'a bloqué pendant pas mal de temps (comprendre : ça a brisé mes élans de programmeur pour de nombreux mois ; j'avais des bons concepts mais j'étais incapable de les réaliser). Transplantée dans le monde réel, l'idée se résume en quelque mots. On prend une planche, on plante des clous à l'emplacement des nœuds. On accroche un bout de ficelle au clou de départ, puis on déroule la ficelle dans le plan de la planche. Bon, comme d'habitude le graphisme est négligé (mais pour le jeu final préparez-vous à un graphisme du feu de Dieu, made in Licoti [qui travaille aussi sur la nouvelle version d'Omnilogie qui sortira sous peu, mais chut c'est un secret ! ]) Donc plutôt que d'avoir des phrases qui divergent grossièrement (« bite », celui qui comprend ce jeu de mot mal amené gagne un abonnement d'un an à mon blog), voyons les concepts derrière. Et commençons par les choses qui fâchent, les mathématiques derrière. Histoire de ne pas perdre les lecteurs, je vais faire ça très court en vous présentant les deux seules fonctions techniques : /** * Calcule l'angle entre la droite formée par les deux points et l'horizontale. * Valeur de retour comprise entre 0 et 2PI. * Note : l'ordre des paramètres est important ! * Note : utilise la fonction atan2. Cf. http://fr.wikipedia.org/wiki/Atan2 * @param P1 le premier point * @param P2 le second point * @return l'angle (P1P2,x) */ protected function getAngle(P1:search?q=Point%20inurl:http://livedocs.adobe.com/flex/201/langref/%20inurl:Point.html&filter=0&num=100&btnI=lucky">Point, P2:search?q=Point%20inurl:http://livedocs.adobe.com/flex/201/langref/%20inurl:Point.html&filter=0&num=100&btnI=lucky">Point):search?q=Number%20inurl:http://livedocs.adobe.com/flex/201/langref/%20inurl:Number.html&filter=0&num=100&btnI=lucky">Number { var Angle:search?q=Number%20inurl:http://livedocs.adobe.com/flex/201/langref/%20inurl:Number.html&filter=0&num=100&btnI=lucky">Number = -search?q=Math%20inurl:http://livedocs.adobe.com/flex/201/langref/%20inurl:Math.html&filter=0&num=100&btnI=lucky">Math.atan2(P2.y - P1.y, P2.x - P1.x); if (Angle < 0) Angle += 2 * search?q=Math%20inurl:http://livedocs.adobe.com/flex/201/langref/%20inurl:Math.html&filter=0&num=100&btnI=lucky">Math.PI; return Angle; } /** * Distance pythagoréenne entre deux points. * @param P1 le premier point * @param P2 le deuxième point * @return la distance calculée avec le théorème de Pythagore. */ protected function getDistance(P1:search?q=Point%20inurl:http://livedocs.adobe.com/flex/201/langref/%20inurl:Point.html&filter=0&num=100&btnI=lucky">Point, P2:search?q=Point%20inurl:http://livedocs.adobe.com/flex/201/langref/%20inurl:Point.html&filter=0&num=100&btnI=lucky">Point):search?q=Number%20inurl:http://livedocs.adobe.com/flex/201/langref/%20inurl:Number.html&filter=0&num=100&btnI=lucky">Number { return search?q=Math%20inurl:http://livedocs.adobe.com/flex/201/langref/%20inurl:Math.html&filter=0&num=100&btnI=lucky">Math.sqrt(search?q=Math%20inurl:http://livedocs.adobe.com/flex/201/langref/%20inurl:Math.html&filter=0&num=100&btnI=lucky">Math.pow(P1.x - P2.x, 2) + search?q=Math%20inurl:http://livedocs.adobe.com/flex/201/langref/%20inurl:Math.html&filter=0&num=100&btnI=lucky">Math.pow(P1.y - P2.y, 2)); } Et nous voilà partis ! Ensuite, à chaque déplacement de souris il faut :
Et voilà, c'est tout pour la base. Le problème, c'est qu'il y a pas mal de petits détails à régler :
Tous les problèmes réglés, on obtient le code suivant. (Note : j'ai pré-calculé les angles φ pour accélérer les calculs) //Déplacer le point représentant la souris MousePos.x = mouseX; MousePos.y = mouseY; //Variables utiles aux calculs qui vont suivre var DernierHook:Hook = getHook(); var AngleActuel:search?q=Number%20inurl:http://livedocs.adobe.com/flex/201/langref/%20inurl:Number.html&filter=0&num=100&btnI=lucky">Number = getAngle(DernierHook.P, MousePos); var AngleMin:search?q=Number%20inurl:http://livedocs.adobe.com/flex/201/langref/%20inurl:Number.html&filter=0&num=100&btnI=lucky">Number = search?q=Math%20inurl:http://livedocs.adobe.com/flex/201/langref/%20inurl:Math.html&filter=0&num=100&btnI=lucky">Math.min(AngleActuel, DernierAngle); var AngleMax:search?q=Number%20inurl:http://livedocs.adobe.com/flex/201/langref/%20inurl:Number.html&filter=0&num=100&btnI=lucky">Number = search?q=Math%20inurl:http://livedocs.adobe.com/flex/201/langref/%20inurl:Math.html&filter=0&num=100&btnI=lucky">Math.max(AngleActuel, DernierAngle); var Distance:search?q=Number%20inurl:http://livedocs.adobe.com/flex/201/langref/%20inurl:Number.html&filter=0&num=100&btnI=lucky">Number = getDistance(DernierHook.P, MousePos); if (AngleMax > 5 && AngleMin < 1) {//On a tourné autour du cercle, il faut inverser les min et max et modifier l'angleMin pour ne pas avoir un saut de 2PI. var Swap:search?q=Number%20inurl:http://livedocs.adobe.com/flex/201/langref/%20inurl:Number.html&filter=0&num=100&btnI=lucky">Number = AngleMax; AngleMax = AngleMin; AngleMin = Swap - 2 * search?q=Math%20inurl:http://livedocs.adobe.com/flex/201/langref/%20inurl:Math.html&filter=0&num=100&btnI=lucky">Math.PI; } //Faut-il ajouter un hook ? Tester pour chacun des noeuds for each(var Item:Position in Angles) { //Si l'objet est différent du dernier hameçon (on ne s'accroche pas deux fois au même) //Que sa distance est inférieure à la distance de la souris //Et que l'angle est compris dans l'intervalle défini par les dernières mesures if (Item.P != DernierHook.P && Item.Distance < Distance && Item.Angle >= AngleMin && Item.Angle <= AngleMax) {//On a trouvé un hameçon ! addHook(Item.P, (Item.Angle),0); } } //Enregistrer et dessiner. DernierAngle = AngleActuel; drawShape(); Et le résultat : Mais là, on ne gère pas le décrochage d'un hameçon… On ajoute donc la variable suivante dans le bloc de déclaration : var Sens:search?q=int%20inurl:http://livedocs.adobe.com/flex/201/langref/%20inurl:int.html&filter=0&num=100&btnI=lucky">int = (AngleActuel > DernierAngle)?1: -1;//Sens trigo // anti trigo Il faut aussi penser à inverser le sens de rotation dans le petit if qui teste pour savoir si on a fait un tour (auquel cas on passera Sens = -Sens). Et enfin, après la boucle pour l'ajout on ajoute une boucle pour la suppression : //Faut-il enlever un hook ? while(DernierHook.Sens != Sens && DernierHook.Angle >=AngleMin && DernierHook.Angle<=AngleMax) { if(removeHook()) DernierHook = getHook(); } Et on obtient ainsi l'animation montrée plus haut. Comme vous le voyez, en choisissant des primitives « intelligentes » (addHook(), removeHook(), getHook(), getAngle(), drawShape()) on peut se concentrer uniquement sur l'agorithmie et oublier les implémentations techniques. Les curieux pourront télécharger le code, compilable même sans l'IDE d'Adobe. Comme d'habitude, il est relativement commenté et devrait être compréhensible. |
| Mise à jour le Jeudi, 24 Juin 2010 17:58 |


Alors merci beaucoup pour ce jeu, il est fabuleux !