[Resolu][DB/MY-AL]PathFinding perso

Pour les scripts écrits en C#
Règles du forum
Merci de respecter la NOMENCLATURE suivante pour vos TITRES de messages :

Commencez par le niveau de vos scripts
DB = Débutant
MY = Moyen
CF = Confirmé

Puis le domaine d'application
-RS = Réseau
-AL = Algorithmie

Exemple :

[DB-RS] Mouvement perso multijoueur
EmileF
Messages : 673
Inscription : 18 Mars 2017 19:39

[Resolu][DB/MY-AL]PathFinding perso

Message par EmileF » 15 Oct 2018 11:00

Voilà, je suis en train de créer un petit jeu que j'appelle CourtCircuit, on peut le voir dans la rubrique Creations du forum.
http://www.unity3d-france.com/unity/php ... 12&t=16068

C'est un jeu de puzzle 3D dans lequel le perso doit déclencher des interrupteurs, on va les appeler comme ça, pour générer une impulsion dans un circuit électrique (d'où le nom) pour ouvrir des portails afin de libérer le passage vers la sortie.

Pour déplacer le perso que j'appelle Mimi il faut cliquer sur le chemin, et il va vers l'endroit où on a cliqué en suivant le chemin le plus court.

J'ai bien sûr fait un script pour cela que je vais vous présenter afin que vous puissiez me dire votre avis et comment améliorer les choses si nécessaire.

Il comprend une petite classe qui me permet d'éviter "Dictionary" que j'aime pas trop :

Code : Tout sélectionner

    public class DataChemin
    {
        public int Key;
        public List<GameObject> Chemin;
        public DataChemin() { }
        public DataChemin(int key, List<GameObject> chemin)
        {
            Key = key;
            Chemin = chemin;
        }
    }
Je sais que key ne sert à rien mais c'était pour le cas où.

On peut voir aussi une fonction publique qui permet l'initialisation qui peut être et même doit être exécutée dès que le niveau est en place, sauf s'il est sujet à des modifications en cours de jeu:

Code : Tout sélectionner

    [SerializeField]
    List<GameObject> Cellules;
    
    /// <summary>
    /// Liste les cellules du jeu
    /// Il vaut mieux appeler cette fonction dès que le niveau est créé 
    /// si les celules restent fixes, pour éviter qu'elle soit appelée 
    /// à chaque clic pour déplacer le player.
    /// </summary>
    public void DefinitCellulesEtVoisins()
    {
        Cellules = new List<GameObject>();
        foreach (var item in GameObject.FindGameObjectsWithTag("Cellule"))
        {
            Cellules.Add(item);
        }

        DefinitLesVoisins();
    }

    /// <summary>
    /// Parcours toutes les celules et ajoute à Voisins de chacune les cellules 
    /// qui sont à 1 case de décalage dans chaque direction
    /// </summary>
    private void DefinitLesVoisins()
    {
        //Definit les voisins
        foreach (var item1 in Cellules)
        {
            foreach (var item2 in Cellules)
            {
                if (item1 != item2)
                {
                    if (item1.transform.position + Vector3.forward == item2.transform.position ||
                        item1.transform.position + Vector3.back == item2.transform.position ||
                        item1.transform.position + Vector3.right == item2.transform.position ||
                        item1.transform.position + Vector3.left == item2.transform.position)
                    {
                        if (!item1.GetComponent<Tile>().Voisins.Contains(item2))
                            item1.GetComponent<Tile>().Voisins.Add(item2);
                        if (!item2.GetComponent<Tile>().Voisins.Contains(item1))
                            item2.GetComponent<Tile>().Voisins.Add(item1);
                    }
                }
            }
        }
    }

    
Et voilà le gros du script, j'ai mis les commentaires qui me parraissaient utiles pour une bonne comprehension...

Code : Tout sélectionner

    [SerializeField]
    GameObject start;
    [SerializeField]
    GameObject end;
    [SerializeField]
    List<DataChemin> Paths;
    [SerializeField]
    public List<GameObject> Path;
    [SerializeField]
    GameObject EnCours;

    /// <summary>
    /// L'appel de la fonction
    /// </summary>
    /// <param name="depart">La cellule de départ de Mimi</param>
    /// <param name="arrivee">La cellule ou elle doit arriver</param>
    internal void DefinitCheminMimi(GameObject depart, GameObject arrivee)
    {
        //si on veut initialiser les cellules à chaque fois
        DefinitCellulesEtVoisins();
        start = depart;
        end = arrivee;
        RechercheChemin();
    }

    /// <summary>
    /// On recherche le chemin le plus court qui pourra être utilise
    /// Path contiendra le chemin à utiliser
    /// </summary>
    void RechercheChemin()
    {

        InitCellules();
        int c = 0;
        Paths = new List<DataChemin>();

        retour:
        Path = new List<GameObject>();
        EnCours = start;
        Path.Add(EnCours);
        EnCours.GetComponent<Tile>().Visite = true;
        RechercheCheminPossible(); //dans Path, le résultat de la recherche
        //si la longueur du chemin est courte on sort
        if (Path.Count > 0 && Path.Count < 5)
            return;
        //sinon on stoque le chemin
        if (Path.Count > 1)
            Paths.Add(new DataChemin(c, Path));
        c++;

        //on repart au départ
        EnCours = start;
        //on va au premier carrefour et on recherche un autre chemin
        if (RechercheCarrefour(EnCours))
            goto retour;

        //s'il n'y a pas de carrefour, ou que tout a été visité
        if (Paths.Count > 0)
            //on recherche le chemin le plus court et on sort 
            RechercheLePlusCourt();

    }

    /// <summary>
    /// Met la valeur Visite de toutes les cellules à false
    /// </summary>
    void InitCellules()
    {
        foreach (GameObject item in Cellules)
        {
            if (item != null)
                item.GetComponent<Tile>().Visite = false;
        }
    }

    void RechercheCheminPossible()
    {
        retour:
        //s'il y a une cellule voisine à de la cellule en cours
        if (RechercheCarrefour(EnCours))
        {
            //on l'ajoute au chemin
            Path.Add(EnCours);
            EnCours.GetComponent<Tile>().Visite = true;
            //si on est arrivé
            if (EnCours == end)
            {
                //on demarque visité de la dernière cellule en cas de recherche supplémentaire et on sort
                end.GetComponent<Tile>().Visite = false;
                return;
            }
            //sinon on continue
            goto retour;
        }
        //il n'y a pas de cellule accessible autour de la cellule en cours
        for (int i = Path.Count - 1; i > -1; i--)
        {
            //on remonte le chemin à la recherche d'un carrefour
            if (!RechercheCarrefour(Path[i]))
            {
                Path.Remove(Path[i]);
            }
            else
            {
                //on reprend à partir du carrefour
                EnCours = Path[i];
                goto retour;
            }
        }
        return;

    }

    /// <summary>
    /// Recherche une cellule libre autour de la cellule donnée.
    /// </summary>
    /// <param name="cellule"></param>
    /// <returns>retourne true si il y a une cellule libre et EnCours contient la cellule libre trouvée</returns>
    bool RechercheCarrefour(GameObject cellule)
    {
        foreach (var item in cellule.GetComponent<Tile>().Voisins)
        {
            if (item != null && item.gameObject.activeSelf)
                if (!item.GetComponent<Tile>().Visite)
                {
                    EnCours = item;
                    return true;
                }
        }
        return false;
    }

    /// <summary>
    /// "Chemin" sera égal au chemin le plus petit entre plusieurs chemins contenus dans "Chemins"
    /// </summary>
    private void RechercheLePlusCourt()
    {

        Path = Paths[0].Chemin;
        foreach (DataChemin item in Paths)
        {
            if (item.Chemin.Count < Path.Count)
                Path = item.Chemin;
        }
    }


Voilà, s'il y a des commentaires, des corrections à apporter je suis tout ouïe, c'est le but de mon post.
S'il y a des félicitations vous pouvez aussi, ça fait toujours plaisir,
et si ça peut aider certains je serais ravi de le savoir

A bientôt...
Dernière édition par EmileF le 17 Oct 2018 10:31, édité 1 fois.
La différence entre l'intelligence et la stupidité est que l'intelligence est limitée.

Avatar de l’utilisateur
boubouk50
ModoGenereux
ModoGenereux
Messages : 6208
Inscription : 28 Avr 2014 11:57
Localisation : Saint-Didier-en-Bresse (71)

Re: [DB/MY-AL]PathFinding perso

Message par boubouk50 » 16 Oct 2018 09:13

Salut,

tu codes plutôt correctement, un peu moins de pessimisme, voyons! :super:
Je vois quand même 2/3 trucs:
Dans DefinitCellulesEtVoisins(), tu peux directement transformer ton tableau en liste et t'éviter la boucle.

Code : Tout sélectionner

//nameSpace
using System.Linq;
[...]
public void DefinitCellulesEtVoisins()
    {
        Cellules = GameObject.FindGameObjectsWithTag("Cellule").ToList();
        DefinitLesVoisins();
    }
Pour définir les voisins, il y a d'autres méthodes plus rapides pour cela, car ici tu as beaucoup de redondances. Il vaudrait mieux ne définir les voisins que sur 1 liste car la deuxième sera forcément redondante.
Je partirai donc plutôt sur une boucle for plutôt que foreach. Comme cela la seconde boucle ne prendrait en compte que les cellules suivantes de la première. Eh oui, pas besoin de les traiter puisqu'elles l'ont déjà été par la première boucle.
Déjà, pour te montrer les redondances inutiles:

Code : Tout sélectionner

private void DefinitLesVoisins()
    {
        //Definit les voisins
        foreach (var item1 in Cellules)
        {
            foreach (var item2 in Cellules)
            {
                if (item1 != item2)
                {
                    if (item1.transform.position + Vector3.forward == item2.transform.position ||
                        item1.transform.position + Vector3.back == item2.transform.position ||
                        item1.transform.position + Vector3.right == item2.transform.position ||
                        item1.transform.position + Vector3.left == item2.transform.position)
                    {
                    //Ceci est utile ici parce que justement tu as des redondances, sinon, sans redondance tu ne ferais qu'ajouter le bon voisin. Le truc, c'est qu'en ajoutant le voisin a item2 tu créeras la redondance quand item1 traitera ce même gameObject.
                        if (!item1.GetComponent<Tile>().Voisins.Contains(item2))
                            item1.GetComponent<Tile>().Voisins.Add(item2);
                        if (!item2.GetComponent<Tile>().Voisins.Contains(item1))
                            item2.GetComponent<Tile>().Voisins.Add(item1);
                    }
                }
            }
Aussi, ce que tu pourrais faire, (ce que je ferai en fait) c'est propager l'information de voisin. Tu prends ta première cellule, qui va trouver ses voisins, qui vont ensuite trouver leur voisin (moins celui déjà trouver) et ainsi de suite, jusqu'à ce qu'il n'y ait plus de voisin possible. Ceci est possible grâce à la récursivité.

Pour la suite, le gros pavé, j'ai pas encore tout lu, je le ferai peut-être plus tard, quand j'aurais le temps.

En tout cas, je suis impressionné de voir comment tu es prolifique et actif, ça fait plaisir de t'avoir comme membre parmi nous ;-)

Bon dev
"Ce n'est pas en améliorant la bougie, que l'on a inventé l'ampoule, c'est en marchant longtemps."
Nétiquette du forum
Savoir faire une recherche
Apprendre la programmation

djulio74
Messages : 682
Inscription : 19 Déc 2009 22:55

Re: [DB/MY-AL]PathFinding perso

Message par djulio74 » 16 Oct 2018 09:25

Salut!
Bon alors je vais essayer d'apporter ma petite pierre. lol. Je suis loin d’être pro du code cependant, donc a prend avec recul. ;)

Vu que tu dis toi-même que Key de ta classDataChemin ne sert à rien, tu pourrait tout autant te contenter d'un simple List<GameObbject>, et la Key serait du coup l'index de la list.
De plus même si tu te servais de Key, donc lui donner une valeur différente de l'index, si tu veux retrouver les GameObject avec une certaine Key, il te faudrait de tout façon parcourir la liste entière.

Code : Tout sélectionner

List<GameObject> Cellules;

Cellules = new List<GameObject>();
        foreach (var item in GameObject.FindGameObjectsWithTag("Cellule"))
        {
            Cellules.Add(item);
        }
Alors oui ça marche très bien, mais pour avoir fait des tests, quand on peut utiliser les array plutot que les list c'est mieux: plus rapide d’accès principalement (même si la le nombre n'est pas fou non plus. Mes tests étaient sur list/array d'un million d'élément. lol). toujours est'il que c'est une fonction que tu lance une fois au chargement du niveau, pas de changement en cours de partie. donc :

Code : Tout sélectionner

GameObject[] Cellules;

Cellules = GameObject.FindGameObjectsWithTag("Cellule");
Pour ta recherche de voisin, tu peut améliorer un peu. dans le sens ou là tu test chaque couple de cellules deux fois.
Tu peut te retrouver avec (item1 = Cellules[0] / Item2 = Cellulues[1]) et (item1 = Cellules[1] / Item2 = Cellulues[0]).

Code : Tout sélectionner

for ( int i = 0 ; i < Cellules.Length-1; i++){ // on ne compte pas la dernière, qui sera testée par item2
	var item1 = Cellules[i];
	for ( int j = i+1 ; i < Cellules.Length; j++){
        var item2 =Cellules[j];
         
// if (item1 != item2) tu n'as plus besoin, i et j seront toujours différents donc item1 =/= item2.
//{
       if (item1.transform.position + Vector3.forward == item2.transform.position ||
               item1.transform.position + Vector3.back == item2.transform.position ||
               item1.transform.position + Vector3.right == item2.transform.position ||
               item1.transform.position + Vector3.left == item2.transform.position)
              {
                    // si l'une est voisin de l'autre, l'autre forcément voisine de l'une,et qu'on ne test qu'une fois chaque cellulue :
                      if (!item1.GetComponent<Tile>().Voisins.Contains(item2))
                        // du coup pas besoin de la deuxieme condition, on ajoute a chacune l'autre dans ses voisines
                        item1.GetComponent<Tile>().Voisins.Add(item2);                                              
                        item2.GetComponent<Tile>().Voisins.Add(item1);        
//}
            }
        }
J'ai évidement pas testé le code, peut etre des erreur de syntaxe.
Du coup comme ça tu évite deux condition par recherche. et en plus un nombre bien plus petit de recherche.
Imagine 10 cellules => Ta fonction donnait 100 vérification ( 10 Item1 x 10 item2), la mienne (si elle marche dans ton cas, lol) donne 55 vérifications ( 10+9+8...+1). tu avait 4 conditions par vérification donc 400 appel de if, je n'en ai plus que 110..
=> La recherche des voisins serait donc 4 fois plus rapide.

Aussi quand tu utilise plusieurs fois le même élément, genre un transform.position, dans une fonction. il est préférable de le signaler une fois comme variable, et utiliser cette variable. ça évite l'appel au transform à répétition.

Code : Tout sélectionner

       if (item1.transform.position + Vector3.forward == item2.transform.position ||
               item1.transform.position + Vector3.back == item2.transform.position ||
               item1.transform.position + Vector3.right == item2.transform.position ||
               item1.transform.position + Vector3.left == item2.transform.position)
pourrait donner

Code : Tout sélectionner

Vector3 PositionItem1 = item1.transform.position;
Vector3 PositionItem2 = item2.transform.position;      
       if (PositionItem1  + Vector3.forward == PositionItem2  ||
              PositionItem1  + Vector3.back == PositionItem2  ||
              PositionItem1  + Vector3.right == PositionItem2  ||
              PositionItem1  + Vector3.left == PositionItem2 )
Et aussi, à chaque partie du if, tu ajoute à chaque fois un vecteur unitaire (longueur de 1, 1x par direction) pour tester si ça correspond à la position de l'autre cellule. Donc la condition pour que deux cellules soient voisine est que la distance entre elle doit être de 1. Tu peux donc encore simplifier par :

Code : Tout sélectionner

Vector3 PositionItem1 = item1.transform.position;
Vector3 PositionItem2 = item2.transform.position;    
float distance = ( PositionItem1  - PositionItem12).magnitude // renvoi la distance entre deux points ( deux vecteur position)
       if (distance == 1 )
       // ou
       // if ( Mathf.Approximately (distance, 1)) 
       
Ton code finale pour la fonction serait :

Code : Tout sélectionner

    [SerializeField]
    GameObject[] Cellules;
    
    /// <summary>
    /// Liste les cellules du jeu
    /// Il vaut mieux appeler cette fonction dès que le niveau est créé 
    /// si les celules restent fixes, pour éviter qu'elle soit appelée 
    /// à chaque clic pour déplacer le player.
    /// </summary>
    public void DefinitCellulesEtVoisins()
    {
       Cellules  = GameObject.FindGameObjectsWithTag("Cellule"))

        DefinitLesVoisins();
    }

    /// <summary>
    /// Parcours toutes les celules et ajoute à Voisins de chacune les cellules 
    /// qui sont à 1 case de décalage dans chaque direction
    /// </summary>
    private void DefinitLesVoisins()
    {
        //Definit les voisins
       for ( int i = 0 ; i < Cellules.Length-1; i++)
        item1 = Cellules[i];
        {
             for ( int j = i+1 ; j < Cellules.Length ; j++)
            {
            Item2 = Cellules[j];
		Vector3 PositionItem1 = item1.transform.position;
		Vector3 PositionItem2 = item2.transform.position;
		float distance = (PositionItem1 -PositionItem2).magnitude;
                    if (disatnce ==1)
                    {
                        if (!item1.GetComponent<Tile>().Voisins.Contains(item2))
                        {
                            item1.GetComponent<Tile>().Voisins.Add(item2);                       
                            item2.GetComponent<Tile>().Voisins.Add(item1);
                          }
                    }                
              }
        }
    }
    
( Encore une fois, juste écrit les codes ici, pas sous unity, certainement il y aura des problèmes de syntaxe je pense)

Ton code était déjà (pour moi) très bien, il fonctionnait et c'est le principal. Mais avec ma "génération d'une ile", j'ai réaliser à quelle point optimiser le nombre d'opération à demander au CPU peut être important. C'est pas une priorité ici, mais c'est bon d'avoir ce reflex je pense.
Donc essaye pour voir si ça marche.

Je vais analyser un peu ton pathfinding perso, pas tout compris a la première lecture, lol. Et je reviendrait dire ce que je pense ( si ce que je viens d’écrire te va comme avis, et si ce ne sont pas trop de grosses bêtises ^^) ;-)

______________________________________________________________
\_______________________ Impossible is nothing _______________________/

EmileF
Messages : 673
Inscription : 18 Mars 2017 19:39

Re: [DB/MY-AL]PathFinding perso

Message par EmileF » 16 Oct 2018 10:28

Non, je ne suis pas pessimiste, mais je ne suis pas sûr de moi.
J'ai beaucoup de plaisir à venir ici, j'apprends beaucoup, et je regrette de n'avoir pas osé plus tôt.

Premièrement

Code : Tout sélectionner


List<GameObject> Cellule;
[...]

Cellules = GameObject.FindGameObjectsWithTag("Cellule").ToList();
Inutile de passer par un List, Mais c'est un réflexe chez moi, je me sens mieux avec un List surtout s'il faut ajouter des éléments. Ce n'est pas le cas ici.
Donc:

Code : Tout sélectionner

    /// <summary>
    /// Liste les cellules du jeu
    /// Il vaut mieux appeler cette fonction dès que le niveau est créé 
    /// si les celules restent fixes, pour éviter qu'elle soit appelée 
    /// à chaque clic pour déplacer le player.
    /// </summary>
    public void DefinitCellulesEtVoisins()
    {
        Cellules = GameObject.FindGameObjectsWithTag("Cellule");
        DefinitLesVoisins();
    }
Deuxièmement:
Je n'arrivais pas à voir comment utiliser la récursivité, que je connais un peu déjà, mais quand j'ai essayé j'avais des opérations infinies qui m'ont découragé.
Grâce à vos commentaires Je ne recherche qu'a partir de la cellule suivant, et je ne cherche comme voisins que celui qui est devant et à droite car ce sera obligatoirement celui de derrière ou de gauche de la suivante, donc pas besoin de les rechercher.
Par contre j'ai quand même des redondances que je surveille
donc:

Code : Tout sélectionner

    /// <summary>
    /// Parcours toutes les celules et ajoute à Voisins de chacune les cellules 
    /// qui sont à 1 case de décalage dans chaque direction
    /// </summary>
    private void DefinitLesVoisins()
    {
        for (int i = 0; i < Cellules.Length; i++)
        {
            DefinitLesVoisins(i);
        }
    }

    private void DefinitLesVoisins(int i)
    {
    	// On recherche qu'a partir de la suivante (i+1)
        for (int j = i + 1; j< Cellules.Length; j++)
        {
            //Ne vérifie que devant et à droite car ça correspond au derrière ou à gauche de la suivante
            if (Cellules[i].transform.position + Vector3.forward == Cellules[j].transform.position ||
                Cellules[i].transform.position + Vector3.right == Cellules[j].transform.position)
            {
                //il faut quand même vérifier qu'il n'y ai pas redondance
                if (!Cellules[i].GetComponent<Tile>().Voisins.Contains(Cellules[j]))
                {
                    Cellules[i].GetComponent<Tile>().Voisins.Add(Cellules[j]);
                    Cellules[j].GetComponent<Tile>().Voisins.Add(Cellules[i]);
                }
                //on continue la recherche avec la cellule suivante
                DefinitLesVoisins(j);
            }

        }
    }

J'attends les commentaires suivant pour la suite, merci
La différence entre l'intelligence et la stupidité est que l'intelligence est limitée.

EmileF
Messages : 673
Inscription : 18 Mars 2017 19:39

Re: [DB/MY-AL]PathFinding perso

Message par EmileF » 16 Oct 2018 10:55

Ha Bonjour Djulio, je n'ai vu ton message qu'après avoir posté le mien.

En tout cas merci.
Djulio a écrit : Vu que tu dis toi-même que Key de ta classDataChemin ne sert à rien, tu pourrait tout autant te contenter d'un simple List<GameObbject>, et la Key serait du coup l'index de la list.
Pas tout à fait, la key ne sert à rien parce que je n'en ai pas besoin par la suite, mais ma class, me permet d'éviter Dictionary que je trouve moins lisible. Mais j'ai besoin de List<List<GameObjet>>
On va le retrouver dans Paths de la 2ème partie.

L'utilisation de Array plutôt que de List à été corrigé.

J'ai aussi corrigé la recherche pour qu'elle commence par la cellule suivant. et la double condition pour éviter les redondances

Je n'avais pas vu ton post pour la question de la distance, je n'ai pas encore testé le résultat, mais vaut-il mieux calculer la distance pour chacune ou vérifier simplement devant et à droite, ce qui correspond automatiquement à celle de derrière ou à gauche de la suivante, je ne sais pas.

En tout cas, merci de ton message, et tes idées ne sont jamais des bêtises, ça permet de se poser la question et c'est comme ça qu'on progresse.

A bientôt merci
La différence entre l'intelligence et la stupidité est que l'intelligence est limitée.

Avatar de l’utilisateur
boubouk50
ModoGenereux
ModoGenereux
Messages : 6208
Inscription : 28 Avr 2014 11:57
Localisation : Saint-Didier-en-Bresse (71)

Re: [DB/MY-AL]PathFinding perso

Message par boubouk50 » 16 Oct 2018 11:10

Attention, tu utilises la récursivité mais tu l'as met dans une boucle, c'est encore pire. La boucle doit te servir à trouver le voisin mais pas à appeler la fonction récursive, sinon tu perds tout intérêt et ajoute de la redondance. Le premier appel devrait être unique. Ensuite, tu trouves les voisins et tu rappelles cette fonction pour ceux-ci. Et c'est tout.
Je te fais vite fait le pseudo-code:

Code : Tout sélectionner

TrouveLesVoisinsDe (0);

fonction TrouveLesVoisinsDe (int index) {
	Pour Chaque autre cellule
		EstCeUnVoisin?
			Oui: Est-il déjà répertorié?
				Non: Ajouter en tant que voisin pour les deux et TrouveLeVoisinDe (IndexDuVoisin)
}
Ainsi, s'il ne trouve pas de voisin, la récursivité s'arrête, sinon elle continue tant que tous les voisins ne sont pas trouvés.
"Ce n'est pas en améliorant la bougie, que l'on a inventé l'ampoule, c'est en marchant longtemps."
Nétiquette du forum
Savoir faire une recherche
Apprendre la programmation

djulio74
Messages : 682
Inscription : 19 Déc 2009 22:55

Re: [DB/MY-AL]PathFinding perso

Message par djulio74 » 16 Oct 2018 11:15

J'ai faillit poster avant ton dernier message. lol

pour ta recherche de voisin, as-tu tester le code que tu donne? tester en jeux je veux dire. Ça ne m’étonnerai pas qu'il manque des voisins je pense. En effet tu n'as aucune façon de contrôler l’ordre dans lequel vont être tes cellules avec GameObject.FindGameObjectsWithTag, donc impossible de prédire si l'ordre correspondra pour juste la recherche haut/droite.
Du moins ça ne marcherait que si pour chaque cellules tu teste TOUTE les autres.
si pour Chaque cellule tu ne recherche qu'a partir de la cellule suivante, faudra tester toute les directions. A tester.

pour le calcul de la distance c'est pour remplacer les 4 conditions du if (if droite, if gauche..). Quand t'as plusieurs condition dans un if, ça reviens a dire

Code : Tout sélectionner

if(a ==b || a==c || a == d || a == e)
//equivalent :
if(a == b){

}
else if ( a == c){

}
else if ( a == d){

}
else if ( a == e){

}
Donc je pense oui que calculer une fois la distance, et ne tester que celle ci est meilleur.

______________________________________________________________
\_______________________ Impossible is nothing _______________________/

djulio74
Messages : 682
Inscription : 19 Déc 2009 22:55

Re: [DB/MY-AL]PathFinding perso

Message par djulio74 » 16 Oct 2018 11:21

a Boubouk50

est-il pas mieux de tout mettre dans une seul fonction (comme ma premiere réponse?

Code : Tout sélectionner

fonction TrouveLesVoisinsDe () {
	Pour Chaque cellule (1) autre que dernière 
		Pour chaque cellule (2) a partir de (1)
			(1) voisin de (2) ? distance ==1 ?
				Oui: (1) possède déjà (2) comme voisin ?
					Non: Ajouter (1) en tant que voisin a (2) , Ajouter (2) en tant que voisin a (1)
}
Il me semble avoir lu quelque part que l'appel de fonction était plus demandeur de ressource qu'une seule fonction qui fait tout?

Emile

Code : Tout sélectionner

    /// <summary>
    /// Parcours toutes les celules et ajoute à Voisins de chacune les cellules 
    /// qui sont à 1 case de décalage dans chaque direction
    /// </summary>
    int COUNT = 0; // juste pour faire un test du nombre de calcul, lance ta fonction avec 10 cellules.
    private void DefinitLesVoisins()
    {
        for (int i = 0; i < Cellules.Length; i++) // mieux   for (int i = 0; i < Cellules.Length - 1; i++), 
        //le couple derniere avant derniere cellules sera testé avec  i == Cellules.Length-2 ( avant derniere) et du coup j = Cellules.Length-1( derniere).
        //sans ce -1 dans i, tu vas tester deux fois la derniere. la derniere itération sera i = j.
        {
            DefinitLesVoisinsBis(i);
        }
        
        print (COUNT); // indique le nombre d'itération effectué. pour une recherche avec Cellules.Length =10, COUNT devrait etre de 55.
    }

    private void DefinitLesVoisinsBis(int i) // juste j'aime pas deux fonctions avec le même nom, lol
    {
    	// On recherche qu'a partir de la suivante (i+1)
        for (int j = i + 1; j< Cellules.Length; j++)
        {
            //Ne vérifie que devant et à droite car ça correspond au derrière ou à gauche de la suivante
            //avec j > i (j = i+1)  tu devrait tester TOUTE les directions, sinon il va y avoir des manquant.
            if (Cellules[i].transform.position + Vector3.forward == Cellules[j].transform.position ||
                Cellules[i].transform.position + Vector3.right == Cellules[j].transform.position)
            {
                //il faut quand même vérifier qu'il n'y ai pas redondance
                if (!Cellules[i].GetComponent<Tile>().Voisins.Contains(Cellules[j]))
                {
                    Cellules[i].GetComponent<Tile>().Voisins.Add(Cellules[j]);
                    Cellules[j].GetComponent<Tile>().Voisins.Add(Cellules[i]);
                }
                //on continue la recherche avec la cellule suivante
                //DefinitLesVoisins(j); pas besoin, et celle elle qui posera probleme, 
            }
		COUNT += 1 // ajoute 1 par itération de recherche
        }
    }
Dernière édition par djulio74 le 16 Oct 2018 11:35, édité 1 fois.

______________________________________________________________
\_______________________ Impossible is nothing _______________________/

EmileF
Messages : 673
Inscription : 18 Mars 2017 19:39

Re: [DB/MY-AL]PathFinding perso

Message par EmileF » 16 Oct 2018 11:25

Djulio a écrit : Et aussi, à chaque partie du if, tu ajoute à chaque fois un vecteur unitaire (longueur de 1, 1x par direction) pour tester si ça correspond à la position de l'autre cellule. Donc la condition pour que deux cellules soient voisine est que la distance entre elle doit être de 1.
J'ai donc essayé ton code, ça marche très bien.

Code : Tout sélectionner

    private void DefinitLesVoisins(int i)
    {
        for (int j = i+1; j< Cellules.Length; j++)
        {
            Vector3 PositionItem1 = Cellules[i].transform.position;
            Vector3 PositionItem2 = Cellules[j].transform.position;
            float distance = (PositionItem1 - PositionItem2).magnitude;
            if (distance == 1)
            {
                //il faut quand même vérifier qu'il n'y ai pas redondance
                if (!Cellules[i].GetComponent<Tile>().Voisins.Contains(Cellules[j]))
                {
                    Cellules[i].GetComponent<Tile>().Voisins.Add(Cellules[j]);
                    Cellules[j].GetComponent<Tile>().Voisins.Add(Cellules[i]);
                }
                DefinitLesVoisins(j);
            }

        }
    }

Voilà avec la récursivité, éviter les calculs répétitifs, le calcul des distances ...
le code final ne ressemble même plus au code initial. :mdr1:
Et vous me dites que c'est pas si mal que ça, vous êtes trop gentils :pleur4:
J'ai un peu peur pour la suite :mrgreen:

merci.
La différence entre l'intelligence et la stupidité est que l'intelligence est limitée.

EmileF
Messages : 673
Inscription : 18 Mars 2017 19:39

Re: [DB/MY-AL]PathFinding perso

Message par EmileF » 16 Oct 2018 11:42

[quote"Boubouk"]

Code : Tout sélectionner

TrouveLesVoisinsDe (0);

fonction TrouveLesVoisinsDe (int index) {
	Pour Chaque autre cellule
		EstCeUnVoisin?
			Oui: Est-il déjà répertorié?
				Non: Ajouter en tant que voisin pour les deux et TrouveLeVoisinDe (IndexDuVoisin)
}

[/quote]

j'ai donc essayé ceci:

Code : Tout sélectionner

    /// <summary>
    /// Parcours toutes les celules et ajoute à Voisins de chacune les cellules 
    /// qui sont à 1 case de décalage dans chaque direction
    /// </summary>
    private void DefinitLesVoisins()
    {
        //for (int i = 0; i < Cellules.Length; i++)
        {
            DefinitLesVoisins(0);
        }
    }

    private void DefinitLesVoisins(int i)
    {
        for (int j = i + 1; j < Cellules.Length; j++)
        {
            Vector3 PositionItem1 = Cellules[i].transform.position;
            Vector3 PositionItem2 = Cellules[j].transform.position;
            float distance = (PositionItem1 - PositionItem2).magnitude;
            if (distance == 1)
            {
                //il faut quand même vérifier qu'il n'y ai pas redondance
                if (!Cellules[i].GetComponent<Tile>().Voisins.Contains(Cellules[j]))
                {
                    Cellules[i].GetComponent<Tile>().Voisins.Add(Cellules[j]);
                    Cellules[j].GetComponent<Tile>().Voisins.Add(Cellules[i]);
                }
                DefinitLesVoisins(j);
            }

        }
    }

et là, je n'ai que les voisins à partir de la cellule[0] et au premier virage, plus rien...
Est-ce que c'est à cause de l'absence de la boucle du départ,
de l'index+1 de la boucle de la récursivité,
De la distance ???

j'ai essayé 0 au lieu de i+1 dans la boucle de la récursivité
j'ai un calcul infini.

C'était mieux avant.???
La différence entre l'intelligence et la stupidité est que l'intelligence est limitée.

Répondre

Revenir vers « (C#) CSharp »