[RESOLU] Rotation Click and go

Questions à propos du scripting. Hors Shader, GUI, Audio et Mobile.
Avatar de l’utilisateur
DJ-OMZ
Messages : 129
Inscription : 11 Jan 2017 13:15

[RESOLU] Rotation Click and go

Message par DJ-OMZ » 16 Mai 2017 12:38

Bonjour,

Je fais un jeu dans lequel je déplace mon personnage sur une map dans un style échiquiers. Il y a des cases et j'avance de case en case.

J'ai un chemin qui est defini lorsque je clique sur un endroit de la map, et mon personnage se déplace jusque l'endroit en question.

Je voudrais, en plus de ce déplacement, que le personnage effectue une rotation vers l'endroit ou il va.

Savez vous comment tourner un objet par rapport a un vecteur de base et un de destination ?
Dernière édition par DJ-OMZ le 17 Mai 2017 10:53, édité 1 fois.

Avatar de l’utilisateur
evereal
Messages : 109
Inscription : 06 Nov 2015 18:46

Re: Rotation Click and go

Message par evereal » 16 Mai 2017 13:52

Salut,
Il suffit d'utiliser la fonction RotateTowards
https://docs.unity3d.com/ScriptReferenc ... wards.html
“La théorie, c'est quand on sait tout et que rien ne fonctionne. La pratique, c'est quand tout fonctionne et que personne ne sait pourquoi. Ici, nous avons réuni théorie et pratique : Rien ne fonctionne... et personne ne sait pourquoi !”

Avatar de l’utilisateur
DJ-OMZ
Messages : 129
Inscription : 11 Jan 2017 13:15

Re: Rotation Click and go

Message par DJ-OMZ » 16 Mai 2017 14:12

evereal a écrit :Salut,
Il suffit d'utiliser la fonction RotateTowards
https://docs.unity3d.com/ScriptReferenc ... wards.html
D'accord et c'est bien ce que j'ai fait, mais comment faire une comparaison pour savoir quand la rotation est bien complete et qu'il n'y a plus besoin de tourner ?

Dans mon test j'ai un comportement que je ne comprends pas...

Code : Tout sélectionner

public class Unit : MonoBehaviour
{
    public int tileX;
    public int tileZ;
    public TileMap map;
    int compteur = 1;
    public bool arrivee = true;
    bool rotationTerminee = true;

    public List<Node> currentPath = null;

    void Update()
    {
        if (currentPath != null)
        {
            arrivee = false;
            rotationTerminee = false;
            MoveToNextTile();
        }
    }

    public void MoveToNextTile() //Cette methode va appeler la rotation a chaque noeud, faire tourner puis deplacer
    {
        if (!arrivee)
        {
       	    //On récupère noeud par noeud
            Vector3 destination = map.TileToWorld(currentPath[compteur].x, currentPath[compteur].z) + new Vector3(0, 0, 0);
            if (transform.position == destination)
            {
                compteur++;
                rotationTerminee = false;
                if (compteur == currentPath.Count)
                {
                    tileX = currentPath[compteur - 1].x;
                    tileZ = currentPath[compteur - 1].z;
                    arrivee = true;
                    rotationTerminee = true;
                    compteur = 1;
                    currentPath = null;
                }
            }
            else //En gros, pour chaque noeud, on appelle ce else, et quand on est arrivé a la position, on passe au noeud suivant
            {
                LookAtNextTile(destination); //Ma super methode de rotation qui marche po
                if (rotationTerminee) //On tourne PUIS on avance
                {
                    transform.position = Vector3.MoveTowards(transform.position, destination, 8f * Time.deltaTime);
                }
            }
        }
    }

    public void LookAtNextTile(Vector3 destination)
    {
        Vector3 vectorRotation = Vector3.RotateTowards(transform.forward, destination, 0.2f, 0);
        transform.rotation = Quaternion.LookRotation(vectorRotation);
        if (Quaternion.LookRotation(transform.forward) == Quaternion.LookRotation(destination))
        {
            rotationTerminee = true;
        }
    }
}
Peut-être y a-t-il quelque chose qui vous choque dans LookAtNextTile que vous voyez, et que je ne vois pas ?

Avatar de l’utilisateur
evereal
Messages : 109
Inscription : 06 Nov 2015 18:46

Re: Rotation Click and go

Message par evereal » 16 Mai 2017 14:44

remplace

Code : Tout sélectionner

Vector3 vectorRotation = Vector3.RotateTowards(transform.forward, destination, 0.2f, 0);
par

Code : Tout sélectionner

Vector3 targetDir = destination - transform.position;
Vector3 vectorRotation = Vector3.RotateTowards(transform.forward, targetDir , 0.2f, 0);
“La théorie, c'est quand on sait tout et que rien ne fonctionne. La pratique, c'est quand tout fonctionne et que personne ne sait pourquoi. Ici, nous avons réuni théorie et pratique : Rien ne fonctionne... et personne ne sait pourquoi !”

Avatar de l’utilisateur
DJ-OMZ
Messages : 129
Inscription : 11 Jan 2017 13:15

Re: Rotation Click and go

Message par DJ-OMZ » 16 Mai 2017 14:58

Code : Tout sélectionner

//previousTile est la case actuelle, destination est la case suivante, a chaque fois.
public void LookAtNextTile(Vector3 destination)
    {
        Debug.Log("Rotation Terminée : " + rotationTerminee);
        Debug.Log("PreviousTile : " + previousTile);
        Debug.Log("Destination  : " + destination);
        Vector3 targetRotation = destination - previousTile;
        Vector3 vectorRotation = Vector3.RotateTowards(transform.forward, targetRotation, 0.2f, 0);
        transform.rotation = Quaternion.LookRotation(vectorRotation);
        if (Quaternion.LookRotation(previousTile) == Quaternion.LookRotation(destination)) // C'est ici que je sais pas comment faire...
        {
            Debug.Log("Rotation finite !");
            rotationTerminee = true;
        }
    }
Il semble que ça tourne bien mais j'entre dans une boucle infini. Je sais pas comment on dit en code "Quand la rotation est complète"...

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

Re: Rotation Click and go

Message par boubouk50 » 16 Mai 2017 15:08

Alors il ne faut pas comparer strictement les variables. Certaines ne sont pas possibles. Au mieux utiliser un delta pour dire "si c'est très proche on fait pas".
Ici il vaut mieux comparer l'angle entre les deux quaternions pour déterminer l'égalité. Si sa valeur absolue est inférieure au delta que tu te donnes (0.5° par ex) alors tu arrêtes.
"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

Avatar de l’utilisateur
DJ-OMZ
Messages : 129
Inscription : 11 Jan 2017 13:15

Re: Rotation Click and go

Message par DJ-OMZ » 16 Mai 2017 16:51

Je suis vraiment désolé mais je ne comprends pas ce qui se passe.... Le Delta me donne des resultats bizarres, et c'est bien a ce niveau la que j'ai mon soucis :

Code : Tout sélectionner

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

public class Unit : MonoBehaviour
{
    public int tileX;
    public int tileZ;
    public TileMap map;
    int compteur = 1;
    public bool arrivee = true;
    bool rotationTerminee = false;
    Vector3 previousTile;

    public List<Node> currentPath = null;

    void Start()
    {
        previousTile = transform.position;
    }

    void Update()
    {
        if (currentPath != null)
        {
            //int currNode = 0;
            arrivee = false;
            //while (currNode < currentPath.Count - 1)
            //{
            //    Vector3 start = map.TileToWorld(currentPath[currNode].x, currentPath[currNode].z) + new Vector3(0, 1, 0);
            //    Vector3 end = map.TileToWorld(currentPath[currNode + 1].x, currentPath[currNode + 1].z) + new Vector3(0, 0, 0);
            //    Debug.DrawLine(start, end, Color.red);
            //    //Debug.Log("Arrivée : " + end);
            //    currNode++;
            //}
            MoveToNextTile();
        }
    }

    public void MoveToNextTile()
    {
        if (!arrivee)
        {
            Vector3 destination = map.TileToWorld(currentPath[compteur].x, currentPath[compteur].z) + new Vector3(0, 0, 0);
            if (transform.position == destination)
            {
                Debug.Log("Next Tile");
                compteur++;
                previousTile = destination;
                rotationTerminee = false;
                if (compteur == currentPath.Count)
                {
                    tileX = currentPath[compteur - 1].x;
                    tileZ = currentPath[compteur - 1].z;
                    arrivee = true;
                    compteur = 1;
                    currentPath = null;
                }
            }
            else
            {
                if (rotationTerminee)
                {
                    Debug.Log("On se déplace");
                    transform.position = Vector3.MoveTowards(transform.position, destination, 8f * Time.deltaTime);
                } else
                {
                    LookAtNextTile(destination);
                }
            }
        }
    }

    public void LookAtNextTile(Vector3 destination)
    {
        Debug.Log("Rotation Terminée : " + rotationTerminee);
        Debug.Log("PreviousTile : " + previousTile);
        Debug.Log("Destination  : " + destination);
        Vector3 targetRotation = destination - previousTile;
        Vector3 vectorRotation = Vector3.RotateTowards(transform.forward, targetRotation, 0.2f, 0);
        Debug.Log("Delta : " + Quaternion.Angle(Quaternion.LookRotation(vectorRotation), Quaternion.LookRotation(destination)));
        transform.rotation = Quaternion.LookRotation(vectorRotation);
        if (Quaternion.LookRotation(vectorRotation) == Quaternion.LookRotation(destination))
        {
            Debug.Log("Rotation finite !");
            rotationTerminee = true;
        }
    }
}
J'ai un comportement que je ne comprend pas :
Si je clique tout a droite : Mon objet tourne, puis une fois qu'il fait face a droite, il y va. Une fois arrivé, je refais un déplacement n'importe ou ailleurs, tant que ce déplacement me demande une rotation, et pouf, ça marche plus....

Si je vais en diagonale (donc d'abord tout droit puis un virage), je vais tourner au niveau du virage et puis hop, j'avance plus.

Dans les 2 cas je suis dans la boucle infinie de la rotation..... Je suis totalement perdu la.

Pourquoi est-ce que ça fonctionne une fois et pas 2 ? Je vois vraiment pas d'ou vient le problème. Et les angles sont bloqués, a 180 dans un cas, et dans d'autres cas, a 71.56, 68, des nombres bizarres comme ça alors que mon objet, visuellement, a fait la rotation exacte...

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

Re: Rotation Click and go

Message par boubouk50 » 16 Mai 2017 17:14

Comme je te l'ai déjà dit, il ne faut pas comparer strictement n'importe quelles variables. Typiquement:

Code : Tout sélectionner

 if (transform.position == destination)
Tu compares 2 Vector3 qui sont composés de floats. Donc tu compares 3x2 floats entre eux. Les floats sont des nombres décimaux approximatifs, les comparer est très risqué puisqu'ils ne sont pas exacts.
Ainsi, ton perso peut être à 0.000001 de son objectif qu'il n'est pas considéré comme arrivé. Il faut donc border tout cela. Comme les quaternions avant où on teste l'angle entre les 2 directions (ou bien le dot product), ici il faut tester la distance entre les deux positions. Si elle est inférieur à un delta (10 cm par exemple) alors tu peux considérer que ton personnage est arrivé.

Toute variable qui est indéfinie ou approximative (dans le sens Pi = 3.14... est un nombre indéfini) ne doit pas être comparée telle quelle avec une autre.
"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

Avatar de l’utilisateur
DJ-OMZ
Messages : 129
Inscription : 11 Jan 2017 13:15

Re: Rotation Click and go

Message par DJ-OMZ » 17 Mai 2017 09:23

Je comprends. En fait j'ai mis un déplacement de 0.2f vers la destination, et chaque case est a 1 de distance des cases voisines. C'est pour ça que ici, la comparaison des Vector3 ne pose pas de problèmes. Mais pour être sûr, je vais suivre ton conseil.

La ou se situe l'erreur, c'est dans la méthode LookAtNextTile.
Quand je fais un premier mouvement, tout roule, ça marche nickel, ça tourne, ça avance. Mais si j'ai une autre rotation a faire après, en faisant un second déplacement par exemple, c'est la que j'ai mon erreur.
J'ai mis un debug qui affiche le delta :

Code : Tout sélectionner

Debug.Log("Delta : " + Quaternion.Angle(Quaternion.LookRotation(vectorRotation), Quaternion.LookRotation(destination)));
Mais j'obtiens des deltas importants, 63, 78 ect... Et du coup je suis dans la boucle infinie puisque mon booléen rotationTerminee ne passe pas a vrai.

Je ne pige pas car pour le premier mouvement, tout fonctionne nickel...

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

Re: Rotation Click and go

Message par boubouk50 » 17 Mai 2017 09:41

Tu compares une direction vers un point dans l'espace avec une direction vers un vecteur de direction (ce qui ne veut rien dire). Tu as de la chance que ça marche la première fois, je dirai.
Il faut comparer ta rotation actuelle (this.transform.rotation) avec celle finale (Quaternion.LookRotation(destination)).

Code : Tout sélectionner

Debug.Log("Delta : " + Quaternion.Angle(this.transform.rotation, Quaternion.LookRotation(destination)));
"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

Répondre

Revenir vers « Scripting »