[Resolu]A 180° ça marche un coup sur deux

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]A 180° ça marche un coup sur deux

Message par EmileF » 06 Juil 2019 16:35

Bonjour,

J'ai encore une fois besoin de vos lumières

Je suis en train de créer un espèce de casse tête qui consiste à retrouver l'emplacement des roues dentées dans un rouage pour le faire fonctionner.

Les roues sont placées au hasard au départ par script.

Le rouage se présente verticalement comme plaqué sur un mur.

Quand l'ensemble du rouage est orienté à 0 degrés sur l'axe des y, tout marche bien. Quand il est orienté à 180°, en fait sur le mur d'en face, il n'y a plus qu'une roue sur deux qui est bien placée. Mon problème vient du fait qu'il y a vraiment une roue sur 2 bien placée, en fait si j'ai bien repéré, les roues paires sont bien les impaires non. Voilà des heures que j'essaye de comprendre le phénomène mais ça me dépasse.

Si quelqu'un avait un soupçon de solution, ce serai nickel.

Voici le script, désolé, il fait près de 400 lignes, mais il est commenté

Code : Tout sélectionner

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Rouage : MonoBehaviour
{
    
    public Camera Camera;   //pour gérer les raycasts
    public Enigme Enigme;   //pour une relation avec le reste du jeu
    public Player Player;   //pour orienter vers le coffre s'il y a lieu
    public Transform Coffre;//le coffre contenant le vase pour la suite quand l'énigme est trouvée
    //public bool execute;
    public Transform Cremaillere; // la crémaillère finale
    public Roue[] Roues;    //les préfabs des roues
    public int nombre;      //le nombre de roues du rouage
    public float speed;     //la vitesse de base
    public float delai;     //le temps de rotation des roues quand l'énigme est trouvée

    List<Roue> rouage;      //la liste des roues du rouage à partir de la crémaillère
    List<Transform> tsStart;//la liste des transforms contenant l'axe du départ
    List<Transform> tsEnd;  //la liste des transforms contenant l'axe de l'arrivée
    //
    public List<int> IntEnd;        //liste des index indiquant la référence de la roue à l'arrivée pour controler que la roue est de la bonne taille
    public List<int> IndexArrives;  //liste des index des roues dans le rouage à l'arrivée pour les faire pivoter dans le bon sens et à la bonne vitesse par rapport à l'ensemble
    float speedCremaillere;         //vitesse du déplacement de la crémaillere
    bool move;                      //pour appliquer les animations quand l'énigme est résolue

    public float orientation;       //la rotation de l'ensemble (je pensais que ça pouvait être utile pour régler le problème)

    int current;    //index de la roue en cours de déplacement (drag)
    float distance; //distance entre la roue et la caméra pour le drag
    Vector3 offset; //distance entre la roue et le curseur pour le drag

    private void Start()
    {
        //orientation générale de l'ensemble
        orientation = transform.eulerAngles.y;

        //Pour créer le rouage
        //Le nombre d'essais et là pour éviter les boucles infinies
        retour0:
        int essais = 0;
        retour:
        if (!CreerRouage())
        {
            essais++;
            if (essais < 1000)
                goto retour;
            else
                print("Initialisation impossible");
        }

        //Place les axes pignons pour le départ du jeu
        //Le nombre d'essais et là pour éviter les boucles infinies
        essais = 0;
        retour1:
        if (!PositionneStarts())
        {
            essais++;
            if (essais < 1000)
                goto retour1;
            else
            {
                print("Positionnement impossible");
                goto retour0;
            }
        }


    }

    private bool CreerRouage()
    {
        //initialisation des lists
        IntEnd = new List<int>();
        IndexArrives = new List<int>();
        rouage = new List<Roue>();

        //récupère et vide le transform dans lequel serant affichée les roues
        Transform roues = transform.Find("Roues");
        roues.Vide(); //Extension pour supprimer tous les gameobjects enfants du transform

        //crée la première roue, et positionne là par rapport à la crémaillère 
        int v = UnityEngine.Random.Range(0, Roues.Length);
        Roue r = Instantiate(Roues[v], roues);
        r.transform.localPosition = Cremaillere.localPosition + new Vector3(0, r.rayon, 0);
        r.transform.localEulerAngles = new Vector3(0, 0, 0);
        r.index = v;
        IntEnd.Add(v);
        IndexArrives.Add(-1);//c'est juste pour créer autant de int que de roue la valeur ne compte pas
        rouage.Add(r);

        //Cree les roues
        for (int i = 0; i < nombre-1; i++)
        {
            //100 essais avant d'abandonner pour eviter les boucles infinies
            int coups = 0;
            retour:
            coups++;
            if (coups > 100)
            {
                print("Infini");
                return false;
            }

            //POUR CREER UNE ROUE

            //récupère l'index de la roue suivante
            v = UnityEngine.Random.Range(0, Roues.Length);

            //pour calculer la position de la roue autour de la roue précédente
            int nDents = rouage[i].nDents / 2;

            //l'angle doit correspondre à la pointe d'une dent de la roue précédente pour éviter les corrections de rotation
            //ce sera une des dents dirigée vers le haut (mode self) de la roue précédente
            float angle = (UnityEngine.Random.Range(0, nDents) - nDents / 2) * rouage[i].pas + rouage[i].transform.eulerAngles.z;
            
            //le rayon est la somme des rayons des 2 roues
            float rayon = rouage[i].rayon + Roues[v].rayon;
            
            //calcule la position de la roue par rapport à la roue précédente
            //JE PENSE QUE C EST ICI QUE CA COINCE
            Vector3 pos = RotatePointAroundPivot(rayon, rouage[i].transform, angle);

            //si la position horizontale est trop à droite
            if (pos.x + Roues[v].rayon > transform.position.x + 1.2f)
            {
                print("Trop à droite");
                goto retour;
            }
            //ou trop à gauche
            if (pos.x - Roues[v].rayon < transform.position.x - 1.2f)
            {
               print("Trop à gauche");
                goto retour;
            }
            //ou trop haute
            if (pos.y + Roues[v].rayon > transform.position.y + 2.5f)
            {
                print("Trop haut");
                goto retour;
            }
            //si trop bas 
            if (pos.y - Roues[v].rayon * 1.5f < Cremaillere.position.y)
            {
                print("Cremaillére touchée");
                goto retour;
            }
            //ou si ça chevauche une autre roue (ça arrive des fois que c'est limite malgré l'augmentation du rayon à 1.5
            bool ok = true;
            for (int j = 0; j < i-1; j++)
            {
                if (Vector3.Distance(rouage[j].transform.position, pos)< rouage[j].rayon * 1.5f + Roues[v].rayon * 1.5f)
                {
                    print(rouage[j] + " touchée");
                    ok = false;
                    break;
                }
            }
            if (!ok)
                goto retour;


            //toutes les conditions sont remplies on crée la roue
            Roue roue = Instantiate(Roues[v], roues);
            roue.index = v;
            IntEnd.Add(v);
            IndexArrives.Add(-1);
            roue.transform.localEulerAngles = new Vector3(0, 0, angle);
            roue.transform.localPosition = pos;
            rouage.Add(roue);
        }

        //maintenant que toutes les roues sont crées
        //on déparente les axes end pour qu'il ne bougent plus si on déplace la roue
        tsEnd = new List<Transform>();
        for (int i = 0; i < nombre; i++)
        {
            Transform ts = rouage[i].transform.Find("AxeEnd");
            //ça parce que j'ai une erreur de nullité parfois sans que j'en ai trouvé la raison et qui ne gène pas dans le construction de mon rouage
            if (ts == null || roues == null)
                return false;
            ts.parent = roues;
            tsEnd.Add(ts);
        }


        return true;
    }

    //Positionne les roues pour le départ du jeu sur la façade du coffre bas
    private bool PositionneStarts()
    {
        for (int i = 0; i < nombre-1; i++)
        {
            //1000 essais pour éviter les boucles infinies
            int coups = 0;
            retour:
            coups++;
            if (coups > 1000)
                return false;
            //calcule les positions au hasard 
            float x = UnityEngine.Random.Range(-2.5f, 2.5f);
            float y = UnityEngine.Random.Range(-3f, -2f);
            Vector3 pos = new Vector3(x, y, -0.35f); //-0.35f parce que le coffre bas est plus épais
            //on évite les chevauchements
            bool ok = true;
            for (int j = 0; j < nombre; j++)
            {
                if (i != j)
                {
                    if (Vector3.Distance(pos, rouage[j].transform.localPosition) < rouage[i].rayon + rouage[j].rayon)
                    {
                        ok = false;
                        break;
                    }
                }
            }
            if (!ok)
                goto retour;

            rouage[i].transform.localPosition = pos;
        }

        //on liste les axes start et on les déparente pour éviter de les déplacer avec les roues
        Transform roues = transform.Find("Roues");
        tsStart = new List<Transform>();
        for (int i = 0; i < nombre; i++)
        {
            Transform ts = rouage[i].transform.Find("AxeStart");
            ts.parent = roues;
            tsStart.Add(ts);
        }

        //pour signaler que tout va bien
        return true;
    }

    private void Update()
    {
        //pour éviter l'exécution si résolu
        if (!Enigme.Resolue)
        {
            //pour appliquer les animations
            if (move)
            {
                //fait pivoter les roues
                for (int i = 0; i < nombre; i++)
                {
                    rouage[i].transform.Rotate(0, 0, rouage[i].speed);
                }
                //fait glisser la crémaillère
                Cremaillere.Translate(speedCremaillere, 0, 0);
                delai -= Time.deltaTime;
                //si délai terminé 
                if (delai <=0)
                {
                    //on fait pivoter le player vers le coffre(si player y a)
                    if (Player)
                        Player.RotateVers(Coffre.position);
                    //on signale que l'énigme est résolue
                    Enigme.Resolue = true;
                }
            }
            else
            {
                //à la pression de la touche gauche de la souris
                if (Input.GetMouseButtonDown(0))
                {   
                    //petite extension raycast pour récupérer le transform sous le clic si tag = "Gear"
                    Transform gear = Camera.RaycastObjet("Gear");
                    if (gear != null)
                    {
                        //on récupère l'index dans le rouage de la roue cliquée
                        int g = rouage.IndexOf(gear.GetComponent<Roue>());
                        if (g > -1)
                        {
                            //si c'est ok on calcule distance et offset de départ pour le drag
                            current = g;
                            distance = Camera.WorldToScreenPoint(rouage[current].transform.position).z;
                            offset = rouage[current].transform.position - MouseWorldPos();
                        }
                    }
                }
                if (current > -1)
                {
                    //si le bouton gauche de la souris reste pressé
                    if (Input.GetMouseButton(0))
                    {
                        //on déplace la souris ebn suivant le curseur
                        rouage[current].transform.position = MouseWorldPos() + offset;
                    }
                    //quand on relache le bouton de la souris
                    if (Input.GetMouseButtonUp(0))
                    {
                        bool ok = false;
                        float d = 10;
                        int p = -1;
                        //on recherche l'axe end le plus proche
                        for (int i = 0; i < nombre; i++)
                        {
                            float dist = Vector3.Distance(rouage[current].transform.position, tsEnd[i].position);
                            if (dist < d)
                            {
                                d = dist;
                                p = i;
                            }
                        }
                        //si la plus petite distance est inférieure à 1
                        if (d < 1) 
                        {
                            //on positionne et rotationne la roue par rapport à l'axe End
                            rouage[current].transform.position = tsEnd[p].position;
                            rouage[current].transform.rotation = tsEnd[p].rotation;
                            IndexArrives[current] = p; //on récupère l'index pour les rotations
                            Controle(); //on controle si terminé
                        }
                        else
                        {
                            //si trop loin on ramène la roue sur son axe start
                            rouage[current].transform.position = tsStart[current].position;
                            rouage[current].transform.rotation = tsStart[current].rotation;
                            IndexArrives[current] = -1;
                        }
                        current = -1;
                    }
                }
            }
        }
    }

    //pour controler si toutes les roues sont placées
    private void Controle()
    {
        for (int i = 0; i < nombre-1; i++)
        {
            //vérifie que l'index de la roue corresponde à l'index de la position End
            if (IndexArrives[i]<0 || rouage[IndexArrives[i]].index != IntEnd[i])
                return;
        }
        //si on arrive ici c'est qua toutes les roues sont en place
        FaitTourner();
    }

    private void FaitTourner()
    {
        //la roue 11 est restée en place pour indiquer la fin du rouage
        IndexArrives[11] = 11;
        //la roue 11, roue moteur prend la vitesse de base
        rouage[IndexArrives[nombre - 1]].speed = speed;
        //on remonte le rouage de l'avant dernière roue à la première pour calculer les vitesses et sens de rotation
        for (int i = nombre - 2; i >= 0; i--)
        {
            rouage[IndexArrives[i]].speed = -rouage[IndexArrives[i + 1]].speed / rouage[IndexArrives[i]].nDents * rouage[IndexArrives[i + 1]].nDents;
            //si c'est la première roue
            if (IndexArrives[i] == 0)
            {
                //on calcule la vitesse de la crémaillère
                float R = rouage[IndexArrives[i]].rayon; // 0.56f;
                float W = rouage[IndexArrives[i]].speed * Mathf.Deg2Rad;
                speedCremaillere = R * W;

            }
        }
        //on déclenche les animations
        move = true;
    }

    //pour calculer la position de la souris pour le drag
    Vector3 MouseWorldPos()
    {
        Vector3 mousepoint = Input.mousePosition;
        mousepoint.z = distance;
        return Camera.ScreenToWorldPoint(mousepoint);
    }

    //pour calculer la position d'un point autour d'un transform selon son rayon et l'angle
    public Vector3 RotatePointAroundPivot(float rayon, Transform pivot, float angleZ)
    {
        //JE PENSE QUE LE PROBLEME PEUT ETRE ICI, J AI MIS L ORIENTATION POUR LE CAS MAIS CA NE CHANGE RIEN
        Quaternion angle = Quaternion.Euler(0, orientation, angleZ);
        Vector3 point = pivot.position + new Vector3(0, rayon, 0);
        return RotatePointAroundPivot(point, pivot.position, angle);
    }

    public Vector3 RotatePointAroundPivot(Vector3 point, Vector3 pivot, Vector3 angles)
    {
        return RotatePointAroundPivot(point, pivot, Quaternion.Euler(angles));
    }

    public Vector3 RotatePointAroundPivot(Vector3 point, Vector3 pivot, Quaternion rotation)
    {
        return rotation * (point - pivot) + pivot;
    }
}

merci à vous de bien vouloir m'aider
Dernière édition par EmileF le 08 Juil 2019 20:52, é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: A 180° ça marche un coup sur deux

Message par boubouk50 » 08 Juil 2019 10:39

Salut,

Bon, j'avoue je n'ai pas lu ton code. Mais je peux avoir une explication et une solution.
Les rotations sont interdépendantes, c'est à dire que la modification sur un axe peut entrainer une modification sur les autres. Je ne me souviens plus dans quel ordre exactement elles sont effectuées, mais seul un axe (le dernier calculé) ne modifie pas les autres.
Pour des cas comme le tien, je préconise une hiérarchie simple qui ne modifie pas l'objet à tourner.
Ton mécanisme, pour qu'il fonctionne correctement dans tous les sens ne doit subir aucune autre transformation. Il faut donc appliquer ces transformations à son parent. Ainsi, localement, l'enfant restera toujours dans la même configuration.

EDIT: https://docs.unity3d.com/ScriptReferenc ... ngles.html: ordre de rotation: Z X Y
"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

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

Re: A 180° ça marche un coup sur deux

Message par EmileF » 08 Juil 2019 20:50

Merci boubouk,

Je crois avoir compris mon problème.

Bien que dans certains cas j'applique la rotation à l'enfant, c'est volontaire et en connaissance de cause.

Mon soucis en fait vient, comme je le pressentais, de la fonction RotatePointAroundPivot(), que j'ai pompé sur internet et dans laquelle les valeurs étaient mondiales, il me les fallait locales.

Merci boubouk, et excuses, j'ai oublié de mettre [resolu]

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

Répondre

Revenir vers « (C#) CSharp »