[RESOLU] [DB-AL] Calcul de vitesse angulaire

Questions à propos du scripting. Hors Shader, GUI, Audio et Mobile.
Avatar de l’utilisateur
NodaMegumi
Messages : 11
Inscription : 02 Août 2019 00:30
Localisation : Lyon

[RESOLU] [DB-AL] Calcul de vitesse angulaire

Message par NodaMegumi » 23 Août 2021 19:31

Bonjour,
Je viens vers vous car cela fait quelques jours que je bloque sur un problème de physique. J'ai fait beaucoup de recherches avant de poster sur le forum, mais je ne trouve pas la réponse à mon problème.

Mon script consiste à accrocher un objet en mouvement autour d'un autre objet ( en mouvement ou non ) et de le faire orbiter autour, tout en conservant sa vitesse, et de pouvoir le décrocher. J'ai pour cela fait un script qui crée un HingeJoint à certaines conditions. Les deux objets possèdent un RigidBody n'utilisant pas la gravité et drag et angular drag sont à 0. L'objet s'accroche bien, il n'est pas ralenti en s'accrochant, mais ralentit petit à petit une fois accroché. Je tente donc d'utiliser le moteur du HingeJoint, mais la valeur targetVelocity est en degrés par seconde. Après quelques recherches, je tombe sur la formule suivante :
vitesse angulaire (rad/s) = vitesse ( m/s ) / rayon (m)

En l'appliquant dans mon script, j'obtiens :

Code : Tout sélectionner

float radius = Vector3.Distance(transform.position, other.position);
float angularVelocity = rb.velocity.magnitude / radius;
//Conversion de rad/s en d/s
targetVelocity = angularVelocity * Mathf.Rad2Deg;
Malheureusement, je n'obtiens pas la bonne vitesse grâce au calcul et le moteur du HingeJoint change la vitesse de mon objet. J'ai des lacunes en maths et physique donc je n'arrive pas à comprendre d'où vient le problème.

Si quelqu'un voit le problème de mon calcul, ou peut me proposer une autre solution n'impliquant pas forcement un HingeJoint, j'en serai ravie!

Merci d'avoir lu,
Bonne soirée!

Avatar de l’utilisateur
jmhoubre
Messages : 851
Inscription : 05 Oct 2019 22:05

Re: [DB-AL] Calcul de vitesse angulaire

Message par jmhoubre » 24 Août 2021 14:59

Bonjour,

je me demande bien pourquoi de nombreuses questions sont posées avec un code amputé, sachant que les erreurs peuvent provenir de n'importe où. Cette pratique oblige à jouer au devin, et génère beaucoup de temps perdu, et des questions - réponses inutiles.

Ces prolégomènes étant faits, dit, j'ai plusieurs questions. Je précise que la formule est correcte.
  • Le moteur du joint est désactivé par défaut : as-tu pensé à l'activer ? (voir la doc)
  • La documentation précise que "The motor will only be able to reach targetVelocity, if JointMotor.force is sufficiently large". La force que tu appliques est-elle suffisante ? As-tu essayé de l'augmenter ?
  • Les limites sont-elles activées ? (voir la doc)
  • Le spring est-il activé ? (voir la doc)
  • Tes objets ont-ils des masses ?

Avatar de l’utilisateur
jmhoubre
Messages : 851
Inscription : 05 Oct 2019 22:05

Re: [DB-AL] Calcul de vitesse angulaire

Message par jmhoubre » 24 Août 2021 15:02

Re,

s'agissant d'une solution de rechange, as-tu essayé de parenter le satellite à l'autre objet au moment du contact ?

Avatar de l’utilisateur
NodaMegumi
Messages : 11
Inscription : 02 Août 2019 00:30
Localisation : Lyon

Re: [DB-AL] Calcul de vitesse angulaire

Message par NodaMegumi » 24 Août 2021 15:51

Bonjour,
Tout d'abord merci de ta réponse. Effectivement, j'aurai du dès le départ mettre tout le script. :gene:

Code : Tout sélectionner


public class ObjectController : MonoBehaviour
{
    public float speed;
    public float maxOrbit = 50;
    public Transform target;
    public HingeJoint joint;
    Rigidbody rb;
    bool attached;

    private void Start()
    {
        rb = GetComponent<Rigidbody>();
        rb.AddForce(transform.forward * speed * 100);
        StartCoroutine(FindOrbit());
    }

    IEnumerator FindOrbit()
    {
        //Cherche un objet proche auquel s'attacher
        bool attached = false;
        while (!attached)
        {
            System.Func<bool> found = new System.Func<bool>(() => nearest() != null);
            yield return new WaitUntil(found);
            target = nearest();
            StartCoroutine(Attach());
            attached = true;
        }
    }
    IEnumerator Attach()
    {
        //Attend d'avoir l'angle parfait pour créer le HingeJoint
        while (joint == null)
        {
            float angle = Vector3.Angle(transform.position - target.position, rb.velocity);
            yield return new WaitForFixedUpdate();
            if (angle <= 90)
            {
                CreateJoint();
            }
        }
    }

    void CreateJoint()
    {
        Destroy(joint);
        float radius = Vector3.Distance(transform.position, target.transform.position);
        float angularVelocity = rb.velocity.magnitude / radius;

        joint = target.gameObject.AddComponent<HingeJoint>();
        joint.connectedBody = rb;
        joint.anchor = Vector3.zero;
        joint.massScale = 0;
        joint.axis = Vector3.up;

        var motor = joint.motor;
        motor.targetVelocity = -Mathf.Rad2Deg * angularVelocity;
        motor.force = 1000;
        motor.freeSpin = true;

        joint.motor = motor;
        joint.useMotor = true;

    }

    Transform nearest()
    {
        Transform nearest = null;
        RaycastHit[] hits = Physics.SphereCastAll(transform.position, maxOrbit, transform.position);
        foreach (RaycastHit hit in hits)
        {
            float angle = Vector3.Angle(transform.position - hit.transform.position, rb.velocity);
            if (angle >= 90 && hit.transform.GetComponent<Rigidbody>().mass > rb.mass)
            {
                if (nearest == null)
                {
                    nearest = hit.transform;
                }
                else
                {
                    if (Vector3.Distance(transform.position, hit.transform.position) < Vector3.Distance(transform.position, nearest.position))
                    {
                        nearest = hit.transform;
                    }
                }
            }
        }
        return nearest;
    }
}
Le moteur est bien activé, il fonctionne et stabilise la vitesse de l'objet avec une force de 1000, mais pas à la bonne vitesse.
Je n'utilise pas le spring ni les limites, j'utilise seulement les anchors pour garder tout le temps la même distance entre les deux objets. Les objets ont une masse, mais je ne veux pas que le satellite embarque l'autre dans sa rotation même s'il est plus lourd. Alors j'ai changé la valeur massScale du HingeJoint sur 0. Je n'ai pas essayé de parenter l'objet car je souhaite que chacun ait une rotation propre ( dû à des collisions ou autre ).
Je précise aussi que tous les objets ont un RigidBody avec la position en Y bloquée.

Avatar de l’utilisateur
jmhoubre
Messages : 851
Inscription : 05 Oct 2019 22:05

Re: [DB-AL] Calcul de vitesse angulaire

Message par jmhoubre » 24 Août 2021 23:02

Bonsoir,

là je ne peux pas trop t'aider. J'ai énuméré toutes les pistes auxquelles je pensais.

As-tu essayé de passer la force à mettons 5000 ? Avec une force de 1000, la vitesse se stabilise à une valeur non nulle ?

Avatar de l’utilisateur
NodaMegumi
Messages : 11
Inscription : 02 Août 2019 00:30
Localisation : Lyon

Re: [DB-AL] Calcul de vitesse angulaire

Message par NodaMegumi » 25 Août 2021 00:47

Augmenter la force ne règle pas le problème. Oui, la vitesse se stabilise. J'ai noté les résultats de plusieurs tests avec la formule :

Quand je lance l'objet à une vitesse de 20 ( rigidbody.velocity.magnitude ):
rayon = 20
targetVelocity = vitesse / rayon = 20 / 20 * Mathf.Rad2Deg = 57.2 degrés/s
Le moteur fait ralentir mon objet pour atteindre une vitesse constante de 19.4 ( peu de différence par rapport à la vitesse de départ, donc le ralentissement ne se voit même pas ).

Par contre, quand je lance le même objet à une vitesse de 50 :
rayon = 20
targetVelocity = 50 / 20 * Mathf.Rad2Deg = 142,7 degrés/s
Le moteur fait ralentir mon objet pour atteindre une vitesse constante de 43.3, le ralentissement est cette fois plus visible.

C'est ce que je souhaite éviter. C'est pour cela que je pensais que mon problème venait du calcul car je ne retombe jamais sur la vitesse de départ mais toujours un peu en dessous... Changer le rayon ne change pas la vitesse finale.

Avatar de l’utilisateur
jmhoubre
Messages : 851
Inscription : 05 Oct 2019 22:05

Re: [DB-AL] Calcul de vitesse angulaire

Message par jmhoubre » 25 Août 2021 13:54

Bonjour,

j'ai de nouveau examiné ton code, mais je ne vois pas d'autres pistes.

Cela n'a rien à voir avec ton souci, mais je tique un peu sur la fonction Nearest, je pense que tu devrais utiliser Physics.OverlapSphere. Si j'ai compris son but, elle cherche dans un rayon de maxOrbit tous les colliders et renvoie le plus proche.
Or telle qu'elle est écrite, Nearest () cherche tous les colliders dans la direction transform.position (une position ou une direction, c'est un Vector3) depuis le point transform.position sur une distance infinie (valeur par défaut, puisque non spécifiée dans les paramètres) avec un Raycast de l'épaisseur de la sphère. La boucle foreach va donc examiner tout un tas de colliders inutiles, en plus il y a un calcul de distance à faire. S'il y a un collider à (maxOrbit + 1), mais dans la mauvaise direction, il ne sera pas détecté. Je ne pense pas que ce soit ce que tu veux faire.

Physics.OverlapSphere me semble plus adaptée. Elle ne renvoie que les colliders compris dans une sphère. En plus, cette fonction permet d'isoler les objets utiles des autres grâce aux layers.

Par ailleurs, ta boucle contient 2 calculs de distance qui utilisent une racine carrée, calcul couteux. Quand on a pas besoin de la distance, mais juste d'une comparaison on utilise plutôt Vector3.sqrMagnitude. Voici un code optimisé, dans lequel je met également en cache la distance minimale actuelle :

Code : Tout sélectionner

foreach (RaycastHit hit in hits)
{
	float currentMinSquarredDistance = Mathf.Infinity;
	float angle = Vector3.Angle(transform.position - hit.transform.position, rb.velocity);
	if (angle >= 90 && hit.transform.GetComponent<Rigidbody>().mass > rb.mass)
	{
		if (nearest == null)
		{
			nearest = hit.transform;
		}
		else
		{
			// Calcule la distance au carré entre le satellite et le collider examiné.
			float hitSqrDistance = Vector3.sqrMagnitude (transform.position - hit.transform.position);
			if ( hitSqrDistance < currentMinSquarredDistance )
			{
				nearest = hit.transform;
				// Met à jour la meilleure distance.
				currentMinSquarredDistance = hitSqrDistance;
			}
		}
	}
}


Avatar de l’utilisateur
NodaMegumi
Messages : 11
Inscription : 02 Août 2019 00:30
Localisation : Lyon

Re: [DB-AL] Calcul de vitesse angulaire

Message par NodaMegumi » 25 Août 2021 18:06

Bonjour!

Merci pour tes conseils, j'ai fait les modifications sur mon script, cela fonctionne et j'ai compris pourquoi c'était mieux de faire ainsi grâce à tes explications! :D

Répondre

Revenir vers « Scripting »